在這個有沒有物件都要高呼“物件導向”的年代,掌握物件導向會給我們帶來意想不到的方便。學程式設計的小夥伴從開始能寫幾行程式碼實現簡單功能到後來懂得將一些重複的操作組合起來形成一個“函式”,再到後來將“函式”和屬性組合起來形成一個“類”。一步步走來,我們在考慮著機器執行程式碼效率的提高的同時也在考慮減輕程式設計師的工作量。 那麼我們今天講到的介面卡模型更著重考慮的是什麼呢?是程式設計師工作量。
什麼時候會用到介面卡模式?
其實最簡單的例子是當我們引用一個第三方類庫。這個類庫隨著版本的改變,它提供的API也可能會改變。如果很不幸的是,你的應用裡引用的某個API已經發生改變的時候,除了在心中默默地罵“wocao”之外,你還得去硬著頭皮去改大量的程式碼。
難道真的一定要如此嗎?按照套路來說,我會回答“不是的”。我們有介面卡模式啊~~
當介面發生改變時,介面卡模式就派上了用場。
舉個栗子
如果通過上面的簡單描述,你都能懂,那在下只能佩服你的領悟能力超群了。一般人一定還是不知所云。為了方便理解,我引用一位博友的例子。原文地址。
一開始的和諧
黑棗玩具公司專門生產玩具,生產的玩具不限於狗、貓、獅子,魚等動物。每個玩具都可以進行“張嘴”與“閉嘴”操作,分別呼叫了openMouth與closeMouth方法。
在這個時候,我們很容易想到可以第一定義一個抽象類Toy,甚至是介面Toy,這些問題不大,其他的類去繼承父類,實現父類的方法。一片和諧,信心向榮。
平衡的破壞
為了擴大業務,現在黑棗玩具公司與紅棗遙控公司合作,紅棗遙控公司可以使用遙控裝置對動物進行嘴巴控制。不過紅棗遙控公司的遙控裝置是呼叫的動物的doMouthOpen及doMouthClose方法。黑棗玩具公司的程式設計師現在必須要做的是對Toy系列類進行升級改造,使Toy能呼叫doMouthOpen及doMouthClose方法。
考慮實現的方法時,我們很直接地想到,你需要的話我再在我的父類子類裡給你新增這麼兩個方法就好啦。當你一次又一次在父類子類裡面重複新增著這兩個方法的時候,總會想著如此重複的工作,難道不能解決麼?當有數百個子類的時候,程式設計師會改瘋的。程式設計師往往比的是誰在不影響效率的時候更會“偷懶”。這樣做下去程式設計師會覺得自己很傻。(其實我經常當這樣的傻子)
abstract class Toy { public abstract function openMouth(); public abstract function closeMouth(); //為紅棗遙控公司控制介面增加doMouthOpen方法 public abstract function doMouthOpen(); //為紅棗遙控公司控制介面增加doMouthClose方法 public abstract function doMouthClose(); } class Dog extends Toy { public function openMouth() { echo "Dog open Mouth\n"; } public function closeMouth() { echo "Dog open Mouth\n"; } //增加的方法 public function doMouthOpen() { $this->doMouthOpen(); } //增加的方法 public function doMouthClose() { $this->closeMouth(); } } class Cat extends Toy { public function openMouth() { echo "Cat open Mouth\n"; } public function closeMouth() { echo "Cat open Mouth\n"; } //增加的方法 public function doMouthOpen() { $this->doMouthOpen(); } //增加的方法 public function doMouthClose() { $this->closeMouth(); } }
更加煩躁
程式設計師剛剛碼完程式碼,喝了口水,突然間另一個訊息傳來。
黑棗玩具公司也要與綠棗遙控公司合作,因為綠棗遙控公司遙控裝置更便宜穩定。不過綠棗遙控公司的遙控裝置是呼叫的動物的operMouth($type)方法來實現嘴巴控制。如果$type為0則“閉嘴”,反之張嘴。
這下好了,程式設計師又得對Toy及其子類進行升級,使Toy能呼叫operMouth()方法。擱誰都不淡定了。
abstract class Toy { public abstract function openMouth(); public abstract function closeMouth(); public abstract function doMouthOpen(); public abstract function doMouthClose(); //為綠棗遙控公司控制介面增加doMouthClose方法 public abstract function operateMouth($type = 0); } class Dog extends Toy { public function openMouth() { echo "Dog open Mouth\n"; } public function closeMouth() { echo "Dog open Mouth\n"; } public function doMouthOpen() { $this->doMouthOpen(); } public function doMouthClose() { $this->closeMouth(); } public function operateMouth($type = 0) { if ($type == 0) { $this->closeMouth(); } else { $this->operateMouth(); } } } class Cat extends Toy { public function openMouth() { echo "Cat open Mouth\n"; } public function closeMouth() { echo "Cat open Mouth\n"; } public function doMouthOpen() { $this->doMouthOpen(); } public function doMouthClose() { $this->closeMouth(); } public function operateMouth($type = 0) { if ($type == 0) { $this->closeMouth(); } else { $this->operateMouth(); } } }
在這個時候,程式設計師必須要動腦子想辦法了,就算自己勤快,萬一哪天紫棗青棗黃棗山棗這些遙控公司全來的時候,忽略自己不斷增多的工作量不說,這個Toy類可是越來越大,總有一天程式設計師不崩潰,系統也會崩潰。
問題在出在哪裡呢?
像上面那樣編寫程式碼,程式碼實現違反了“開-閉”原則,一個軟體實體應當對擴充套件開放,對修改關閉。即在設計一個模組的時候,應當使這個模組可以在不被修改的前提下被擴充套件。也就是說每個屍體都是一個小王國,你讓我參與你的事情這個可以,但你不能修改我的內部,除非我的內部程式碼確實可以優化。
在這種想法下,我們懂得了如何去用繼承,如何利用多型,甚至如何實現“高內聚,低耦合”。
回到這個問題,我們現在面臨這麼一個問題,新的介面方法我要實現,舊的介面(Toy抽象類)也不能動,那麼總得有個解決方法吧。那就是引入一個新的類--我們本文的主角--介面卡。 介面卡要完成的功能很明確,引用現有介面的方法實現新的介面的方法。更像它名字描述的那樣,你的介面不改的話,我就利用現有介面和你對接一下吧。
到此,解決方法已經呼之欲出了,下面貼上程式碼。
<?php abstract class Toy { public abstract function openMouth(); public abstract function closeMouth(); } class Dog extends Toy { public function openMouth() { echo "Dog open Mouth\n"; } public function closeMouth() { echo "Dog close Mouth\n"; } } class Cat extends Toy { public function openMouth() { echo "Cat open Mouth\n"; } public function closeMouth() { echo "Cat close Mouth\n"; } } //目標角色:紅棗遙控公司 interface RedTarget { public function doMouthOpen(); public function doMouthClose(); } //目標角色:綠棗遙控公司及 interface GreenTarget { public function operateMouth($type = 0); } //類介面卡角色:紅棗遙控公司 class RedAdapter implements RedTarget { private $adaptee; function __construct(Toy $adaptee) { $this->adaptee = $adaptee; } //委派呼叫Adaptee的sampleMethod1方法 public function doMouthOpen() { $this->adaptee->openMouth(); } public function doMouthClose() { $this->adaptee->closeMouth(); } } //類介面卡角色:綠棗遙控公司 class GreenAdapter implements GreenTarget { private $adaptee; function __construct(Toy $adaptee) { $this->adaptee = $adaptee; } //委派呼叫Adaptee:GreenTarget的operateMouth方法 public function operateMouth($type = 0) { if ($type) { $this->adaptee->openMouth(); } else { $this->adaptee->closeMouth(); } } } class testDriver { public function run() { //例項化一隻狗玩具 $adaptee_dog = new Dog(); echo "給狗套上紅棗介面卡\n"; $adapter_red = new RedAdapter($adaptee_dog); //張嘴 $adapter_red->doMouthOpen(); //閉嘴 $adapter_red->doMouthClose(); echo "給狗套上綠棗介面卡\n"; $adapter_green = new GreenAdapter($adaptee_dog); //張嘴 $adapter_green->operateMouth(1); //閉嘴 $adapter_green->operateMouth(0); } } $test = new testDriver(); $test->run();
最後的結果就是,Toy類及其子類在不改變自身的情況下,通過介面卡實現了不同的介面。
最後總結
將一個類的介面轉換成客戶希望的另外一個介面,使用原本不相容的而不能在一起工作的那些類可以在一起工作.
介面卡模式核心思想:把對某些相似的類的操作轉化為一個統一的“介面”(這裡是比喻的說話)--介面卡,或者比喻為一個“介面”,統一或遮蔽了那些類的細節。介面卡模式還構造了一種“機制”,使“適配”的類可以很容易的增減,而不用修改與介面卡互動的程式碼,符合“減少程式碼間耦合”的設計原則。
以上
系列文章: