有大半個月都沒有寫隨筆了,主要是大四升學來一大堆亂七八糟的事情,找工作碰壁。主要也怪自己實力不濟,更主要的是缺乏亮眼實戰經驗,很難引起HR的興趣。所以在這裡還是做個小廣告,希望有哪個能賜一份兒工作,實習也無所謂,最不濟實習不給工資也行嘛(我被打擊的已經完全沒有底線了)。地點隨便,但目前我只能在成都讀完這剩下的一年書,反正寫程式碼哪裡都行嘛。
說來慚愧,我總是覺得自己「懂得多麼道理,就是過不好這一生」。這些部落格分享也不管有沒有人看,總是要寫出自己現有的認知,更多的是希望得到前輩的指導。
回歸主題,前面分享完基本的三種設計模式,今天分享另一種我們感覺不常用到,但這到需要的時候大呼方便的模式--適配器模式。
在這個有沒有對象都要高呼「面向對象」的年代,掌握面向對象會給我們帶來意想不到的方便。學程式設計的夥伴從開始能寫幾行程式碼實現簡單功能到後來懂得將一些重複的操作組合起來形成一個“函數”,再到後來將“函數”和屬性組合起來形成一個“類”。一步步走來,我們在考慮機器運行程式碼效率的提高的同時也在考慮減輕程式設計師的工作量。 那我們今天講到的適配器模型更著重考慮的是什麼呢?是程式設計師工作量。
什麼時候會用到適配器模式?
其實最簡單的例子是當我們引用一個第三方類別庫。這個類別庫隨著版本的改變,它所提供的API也可能會改變。如果很不幸的是,你的應用程式裡引用的某個API已經改變的時候,除了在心中默默地罵「wocao」之外,你還得去硬著頭皮去改大量的程式碼。
難道真的一定要如此嗎?照套路來說,我會回答「不是的」。我們有適配器模式啊~~
當介面改變時,適配器模式就派上了用場。
舉個栗子
如果透過上面的簡單描述,你都能懂,那在下只能佩服你的領悟能力超群了。一般人一定還是不知所云。為了方便理解,我引用一位博友的例子。原文地址。
一開始的和諧
黑棗玩具公司專門生產玩具,生產的玩具不限於狗、貓、獅子,魚等動物。每個玩具都可以進行「張嘴」與「閉嘴」操作,分別呼叫了openMouth與closeMouth方法。
在這個時候,我們很容易想到可以第一定義一個抽象類別Toy,甚至是介面Toy,這些問題不大,其他的類別去繼承父類,實作父類別的方法。一片和諧,信心向榮。
平衡的破壞
為了擴大業務,現在黑棗玩具公司與紅棗遙控公司合作,紅棗遙控公司可以使用遙控設備對動物進行嘴巴控制。不過紅棗遙控公司的遙控設備是呼叫的動物的doMouthOpen及doMouthClose方法。黑棗玩具公司的程式設計師現在必須要做的是對Toy系列類別進行升級改造,使Toy能呼叫doMouthOpen及doMouthClose方法。
考慮實現的方法時,我們很直接地想到,你需要的話我再在我的父類子類裡給你添加這麼兩個方法就好啦。當你一次又一次在父類別子類別裡面重複加入這兩個方法的時候,總會想著如此重複的工作,難道不能解決麼?當有數百個子類別的時候,程式設計師會改瘋的。程式設計師往往比的是誰在不影響效率的時候更會「偷懶」。這樣做下去程式設計師會覺得自己很傻。 (其實我常常當這樣的傻子)
<span>abstract</span><span>class</span><span> Toy { </span><span>public</span><span>abstract</span><span>function</span><span> openMouth(); </span><span>public</span><span>abstract</span><span>function</span><span> closeMouth(); </span><span>//</span><span>为红枣遥控公司控制接口增加doMouthOpen方法</span><span>public</span><span>abstract</span><span>function</span><span> doMouthOpen(); </span><span>//</span><span>为红枣遥控公司控制接口增加doMouthClose方法</span><span>public</span><span>abstract</span><span>function</span><span> doMouthClose(); } </span><span>class</span> Dog <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Dog open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Dog open Mouth\n"<span>; } </span><span>//</span><span>增加的方法</span><span>public</span><span>function</span><span> doMouthOpen() { </span><span>$this</span>-><span>doMouthOpen(); } </span><span>//</span><span>增加的方法</span><span>public</span><span>function</span><span> doMouthClose() { </span><span>$this</span>-><span>closeMouth(); } } </span><span>class</span> Cat <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Cat open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Cat open Mouth\n"<span>; } </span><span>//</span><span>增加的方法</span><span>public</span><span>function</span><span> doMouthOpen() { </span><span>$this</span>-><span>doMouthOpen(); } </span><span>//</span><span>增加的方法</span><span>public</span><span>function</span><span> doMouthClose() { </span><span>$this</span>-><span>closeMouth(); } }</span>
更加煩躁
程式設計師剛剛碼完代碼,喝了口水,突然間另一個消息傳下來。
黑棗玩具公司也要與綠棗遙控公司合作,因為綠棗遙控公司遙控設備更便宜穩定。不過綠棗遙控公司的遙控設備是呼叫的動物的operMouth($type)方法來實現嘴巴控制。若$type為0則“閉嘴”,反之張嘴。
這下好了,程式設計師又得對Toy及其子類別進行升級,使Toy能呼叫operMouth()方法。擱誰都不淡定了。
<span>abstract</span><span>class</span><span> Toy { </span><span>public</span><span>abstract</span><span>function</span><span> openMouth(); </span><span>public</span><span>abstract</span><span>function</span><span> closeMouth(); </span><span>public</span><span>abstract</span><span>function</span><span> doMouthOpen(); </span><span>public</span><span>abstract</span><span>function</span><span> doMouthClose(); </span><span>//</span><span>为绿枣遥控公司控制接口增加doMouthClose方法 </span><span>public</span><span>abstract</span><span>function</span> operateMouth(<span>$type</span> = 0<span>); } </span><span>class</span> Dog <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Dog open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Dog open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> doMouthOpen() { </span><span>$this</span>-><span>doMouthOpen(); } </span><span>public</span><span>function</span><span> doMouthClose() { </span><span>$this</span>-><span>closeMouth(); } </span><span>public</span><span>function</span> operateMouth(<span>$type</span> = 0<span>) { </span><span>if</span> (<span>$type</span> == 0<span>) { </span><span>$this</span>-><span>closeMouth(); } </span><span>else</span><span> { </span><span>$this</span>-><span>operateMouth(); } } } </span><span>class</span> Cat <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Cat open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Cat open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> doMouthOpen() { </span><span>$this</span>-><span>doMouthOpen(); } </span><span>public</span><span>function</span><span> doMouthClose() { </span><span>$this</span>-><span>closeMouth(); } </span><span>public</span><span>function</span> operateMouth(<span>$type</span> = 0<span>) { </span><span>if</span> (<span>$type</span> == 0<span>) { </span><span>$this</span>-><span>closeMouth(); } </span><span>else</span><span> { </span><span>$this</span>-><span>operateMouth(); } } }</span>
在這個時候,程式設計師必須要動腦子想辦法了,就算自己勤快,萬一哪天紫棗青棗黃棗山棗這些遙控公司全來的時候,忽略自己不斷增加的工作量不說,這個Toy類別可是越來越大,總有一天程式設計師不崩潰,系統也會崩潰。
問題在出在哪裡呢?
像上面那样编写代码,代码实现违反了“开-闭”原则,一个软件实体应当对扩展开放,对修改关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。也就是说每个尸体都是一个小王国,你让我参与你的事情这个可以,但你不能修改我的内部,除非我的内部代码确实可以优化。
在这种想法下,我们懂得了如何去用继承,如何利用多态,甚至如何实现“高内聚,低耦合”。
回到这个问题,我们现在面临这么一个问题,新的接口方法我要实现,旧的接口(Toy抽象类)也不能动,那么总得有个解决方法吧。那就是引入一个新的类--我们本文的主角--适配器。 适配器要完成的功能很明确,引用现有接口的方法实现新的接口的方法。更像它名字描述的那样,你的接口不改的话,我就利用现有接口和你对接一下吧。
到此,解决方法已经呼之欲出了,下面贴上代码。
<?<span>php </span><span>abstract</span><span>class</span><span> Toy { </span><span>public</span><span>abstract</span><span>function</span><span> openMouth(); </span><span>public</span><span>abstract</span><span>function</span><span> closeMouth(); } </span><span>class</span> Dog <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Dog open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Dog close Mouth\n"<span>; } } </span><span>class</span> Cat <span>extends</span><span> Toy { </span><span>public</span><span>function</span><span> openMouth() { </span><span>echo</span> "Cat open Mouth\n"<span>; } </span><span>public</span><span>function</span><span> closeMouth() { </span><span>echo</span> "Cat close Mouth\n"<span>; } } </span><span>//</span><span>目标角色:红枣遥控公司 </span><span>interface</span><span> RedTarget { </span><span>public</span><span>function</span><span> doMouthOpen(); </span><span>public</span><span>function</span><span> doMouthClose(); } </span><span>//</span><span>目标角色:绿枣遥控公司及 </span><span>interface</span><span> GreenTarget { </span><span>public</span><span>function</span> operateMouth(<span>$type</span> = 0<span>); } </span><span>//</span><span>类适配器角色:红枣遥控公司 </span><span>class</span> RedAdapter <span>implements</span><span> RedTarget { </span><span>private</span><span>$adaptee</span><span>; </span><span>function</span> __construct(Toy <span>$adaptee</span><span>) { </span><span>$this</span>->adaptee = <span>$adaptee</span><span>; } </span><span>//</span><span>委派调用Adaptee的sampleMethod1方法 </span><span>public</span><span>function</span><span> doMouthOpen() { </span><span>$this</span>->adaptee-><span>openMouth(); } </span><span>public</span><span>function</span><span> doMouthClose() { </span><span>$this</span>->adaptee-><span>closeMouth(); } } </span><span>//</span><span>类适配器角色:绿枣遥控公司 </span><span>class</span> GreenAdapter <span>implements</span><span> GreenTarget { </span><span>private</span><span>$adaptee</span><span>; </span><span>function</span> __construct(Toy <span>$adaptee</span><span>) { </span><span>$this</span>->adaptee = <span>$adaptee</span><span>; } </span><span>//</span><span>委派调用Adaptee:GreenTarget的operateMouth方法 </span><span>public</span><span>function</span> operateMouth(<span>$type</span> = 0<span>) { </span><span>if</span> (<span>$type</span><span>) { </span><span>$this</span>->adaptee-><span>openMouth(); } </span><span>else</span><span> { </span><span>$this</span>->adaptee-><span>closeMouth(); } } } </span><span>class</span><span> testDriver { </span><span>public</span><span>function</span><span> run() { </span><span>//</span><span>实例化一只狗玩具 </span><span>$adaptee_dog</span> = <span>new</span><span> Dog(); </span><span>echo</span> "给狗套上红枣适配器\n"<span>; </span><span>$adapter_red</span> = <span>new</span> RedAdapter(<span>$adaptee_dog</span><span>); </span><span>//</span><span>张嘴 </span><span>$adapter_red</span>-><span>doMouthOpen(); </span><span>//</span><span>闭嘴 </span><span>$adapter_red</span>-><span>doMouthClose(); </span><span>echo</span> "给狗套上绿枣适配器\n"<span>; </span><span>$adapter_green</span> = <span>new</span> GreenAdapter(<span>$adaptee_dog</span><span>); </span><span>//</span><span>张嘴 </span><span>$adapter_green</span>->operateMouth(1<span>); </span><span>//</span><span>闭嘴 </span><span>$adapter_green</span>->operateMouth(0<span>); } } </span><span>$test</span> = <span>new</span><span> testDriver(); </span><span>$test</span>->run();
最后的结果就是,Toy类及其子类在不改变自身的情况下,通过适配器实现了不同的接口。
最后总结
将一个类的接口转换成客户希望的另外一个接口,使用原本不兼容的而不能在一起工作的那些类可以在一起工作.
适配器模式核心思想:把对某些相似的类的操作转化为一个统一的“接口”(这里是比喻的说话)--适配器,或者比喻为一个“界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种“机制”,使“适配”的类可以很容易的增减,而不用修改与适配器交互的代码,符合“减少代码间耦合”的设计原则。
以上
系列文章:
php模式设计之 单例模式
php模式设计之 工厂模式
php模式设计之 注册树模式
php模式设计之 适配器模式
以上就介绍了php模式设计之 适配器模式,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。