国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

設(shè)計模式之:適配器模式

2019-11-14 14:48:07
字體:
供稿:網(wǎng)友

      適配器很容易理解, 大多數(shù)人家庭都有手機轉(zhuǎn)接器, 用來為移動電話充電,這就是一種適配器. 如果只有USB接頭, 就無法將移動電話插到標(biāo)準(zhǔn)插座上. 實際上, 必須使用一個適配器, 一端接USB插頭, 一端接插座. 當(dāng)然, 你可以拿出電氣工具,改裝USB連接頭, 或者重新安裝插座, 不過這樣會帶來很多額外的工作, 而且可能會把連接頭或插座弄壞. 所以, 最可取的方法就是找一個適配器. 軟件開發(fā)也是如此.

類適配器模式(使用繼承)

      類適配器模式很簡單, 不過與對象適配器模式相比, 類適配器模式的靈活性弱些, 類適配器簡單的原因在于 , 適配器(Adapter)會從被適配者(Adaptee)繼承功能, 所以適配模式中需要編寫的代碼比較少.
      由于類適配器模式包含雙重繼承, 但是php并不支持雙重繼承, 不過幸運的是,PHP可以用接口來模擬雙重繼承, 下面是一個正確的結(jié)構(gòu), 不僅繼承了一個類, 同時還繼承了一個接口
class ChildClass extends ParentClass implements ISomeAdapter{    }
實現(xiàn)類適配器模式時, 參與者必須包括一個PHP接口
 
下面以一個貨幣兌換為例來演示:
      假設(shè)有一個企業(yè)網(wǎng)站在同時銷售軟件服務(wù)和軟件產(chǎn)品, 目前, 所有交易都在美國進(jìn)行, 所以完全可以用美元來完成所有計算.現(xiàn)在開發(fā)人員希望能有一個轉(zhuǎn)換器能處理美元和歐元的兌換, 而不改變原來按美元交易額的類.通過增加一個適配器, 現(xiàn)在程序即可以用美元計算也可以用歐元計算.
DollarCalc.php
<?phpclass DollarCalc{    PRivate $dollar;    private $product;    private $service;    public $rate = 1;    public function requestCalc($productNow, $serviceNow)    {        $this->product = $productNow;        $this->service = $serviceNow;        $this->dollar = $this->product + $this->service;        return $this->requestTotal();    }    public function requestTotal()    {        $this->dollar *= $this->rate;        return $this->dollar;    }}
查看這個類,可以看到其中有一個屬性$rate,requestTotal()方法使用$rate計算一次交易的金額.在這個版本中, 這個值設(shè)置為1,實際上總金額無需再乖以兌換率, 不過如果要為客戶提供折扣或者要增加額外服務(wù)或產(chǎn)品的附加費, $rate變量會很方便. 這個類并不是適合器模式的一部分, 不過這是一個起點.
 
需求變化了
現(xiàn)在客戶的公司要向歐洲發(fā)展,所以需要開發(fā)一個應(yīng)用, 能夠用歐元完成同樣的計算. 你希望這個歐元計算能夠像DollarCalc一樣, 所要做的就是改變變量名.
EuroCalc.php
<?phpclass EuroCalc{    private $euro;    private $product;    private $service;    public $rate = 1;    public function requestCalc($productNow, $serviceNow)    {        $this->product = $productNow;        $this->service = $serviceNow;        $this->euro = $this->product + $this->service;        return $this->requestTotal();    }    public function requestTotal()    {        $this->euro *= $this->rate;        return $this->euro;    }}
接下來, 再把應(yīng)用的其余部分插入到EuroCalc類中. 不過,因為客戶的所有數(shù)據(jù)都是按美元計算的.換句話說, 如果不重新開發(fā)整個程序, 就無法在系統(tǒng)中"插入"這個歐元計算. 但是你不想這么做. 為了加入EuroCalc, 你需要一個適配器: 就像找一個適配器來適應(yīng)歐洲的插座一樣, 可以創(chuàng)建一個適配器, 使你的系統(tǒng)能夠使用歐元. 幸運的是, 類適配器正是為這樣的情況設(shè)計的.首先需要創(chuàng)建一個接口. 在這個類圖中, 這個接口名為ITarget. 它只有一個方法requester(). requester()是一個抽象方法, 要由接口的具體實現(xiàn)來實現(xiàn)這個方法.
ITarget.php
<?phpinterface ITarget{    public function requester();}
現(xiàn)在開發(fā)人員可以實現(xiàn)requester()方法, 請求歐元而不是美元.
在使用繼承的適配器設(shè)計模式中, 適配器(Adapter)參與都既實現(xiàn)ITarget接口,還實現(xiàn)了具體類EuroCalc. 創(chuàng)建EuroAdapter不需要做太多工作, 因為大部分工作已經(jīng)在EuroCal類中完成.現(xiàn)在要做的就是實現(xiàn)request()方法, 使它能把美元值轉(zhuǎn)換為歐元值.
EuroAdapter.php
<?phpinclude_once('EuroCalc.php');include_once('ITarget.php');class EuroAdapter extends EuroCalc implements ITarget{    public function __construct()    {        $this->requester();    }    public function requester()    {        $this->rate = 0.8111;        return $this->rate;    }}
類適配模式中, 一個具體類會繼承另一個具體類, 有這種結(jié)構(gòu)的設(shè)計模式很少見, 大多數(shù)設(shè)計模式中, 幾乎都是繼承一個抽象類, 并由類根據(jù)需要實現(xiàn)其抽象方法和屬性. 換句話說, 一般談到繼承時, 都是具體類繼承抽象類.
由于既實現(xiàn)了一個接口又?jǐn)U展了一個類, 所以EuroAdapter類同時擁有該接口和具體類的接口. 通過使用requester()方法, EuroAdapter類可以設(shè)置rate值(兌換率), 從而能使用被適配者的功能, 而元而做任何改變.
下面定義一個Client類, 從EuroAdapter和DollarCalc類發(fā)出請求. 可以看到,原來的DollarCalc仍能很好地工作, 不過它沒有ITarget接口. 
Client.php
<?phpinclude_once('EuroAdapter.php');include_once('DollarCalc.php');class Client{    public function __construct()    {        $euro = '€';        echo "區(qū)元: $euro" . $this->makeApapterRequest(new EuroAdapter()) . '<br />';        echo "美元: $: " . $this->makeDollarRequest(new DollarCalc()) . '<br />';    }    private function makeApapterRequest(ITarget $req)    {        return $req->requestCalc(40,50);    }    private function makeDollarRequest(DollarCalc $req)    {        return $req->requestCalc(40,50);    }}$woker = new Client();
運行結(jié)果如下:
Euros: €72.999Dollars: $: 90
可以看到,美元和歐元都可以處理, 這就是適配器模式的方便之處.
這個計算很簡單, 如果是針對更為復(fù)雜的計算, 繼承要提供建立類適配器的Target接口的必要接口和具體實現(xiàn)
 

使用組合的適配器模式

      對象適配器模式使用組合而不是繼承, 不過它也會完成同樣的目標(biāo). 通過比較這兩個版本的適配器模式, 可以看出它們各自的優(yōu)缺點. 采用類適配器模式時,適配器可以繼承它需要的大多數(shù)功能, 只是通過接口稍微調(diào). 在對象適配器模式中 適配器(Adapter)參與使用被適配者(Adaptee), 并實現(xiàn)Target接口. 在類適配器模式中, 適配器(Adapter)則是一個被適配者(Adaptee), 并實現(xiàn)Target接口.
示例: 從桌面環(huán)境轉(zhuǎn)向移動環(huán)境
PHP程序員經(jīng)常會遇到這樣一個問題:需要適應(yīng)移動環(huán)境而做出調(diào)整.不久之前,你可能只需要考慮提供一個網(wǎng)站來適應(yīng)多種不同的桌面環(huán)境. 大多數(shù)桌面都使用一個布局, 再由設(shè)計人員讓它更美觀. 對于移動設(shè)備, 設(shè)計人員和開發(fā)人員不僅需要重新考慮桌面和移動環(huán)境中頁面顯示的設(shè)計元素, 還要考慮如何從一個環(huán)境切換到另一個環(huán)境.
首先來看桌面端的類Desktop(它將需要一個適配器). 這個類使用了一個簡單但很寬松的接口:
IFormat.php
<?phpinterface IFormat{    public function formatCSS();    public function formatGraphics();    public function horizontalLayout();}
它支持css和圖片選擇, 不過其中一個方法指示一種水平布局, 我們知道這種布局并不適用小的移動設(shè)備.下面給出實現(xiàn)這個接口的Desktop類
Desktop.php
<?phpinclude_once('IFormat.php');class Desktop implements IFormat{    public function formatCSS()    {        echo "引用desktop.css<br />";    }    public function formatGraphics()    {        echo "引用desktop.png圖片<br />";    }    public function horizontalLayout()    {        echo '桌面:水平布局';    }}
問題來了, 這個布局對于小的移動設(shè)備來說太寬了. 所以我們的目標(biāo)是仍采用同樣的內(nèi)容, 但調(diào)整為一種移動設(shè)計.
下面來看移動端的類Mobile
首先移動端有一個移動端的接口
IMobileFormat
<?phpinterface IMobileFormat{    public function formatCSS();    public function formatGraphics();    public function verticalLayout();}
可以看到, IMobileFormat接口和IFormat接口是不一樣的,也就是不兼容的, 一個包含了方法horizontalLayout(), 另一個包含方法verticalLaout(), 它們的差別很小, 最主要的區(qū)別是: 桌面設(shè)計可以采用水平的多欄布局, 而移動設(shè)計要使用垂直布局,而適配器就是要解決這個問題
下面給出一個實現(xiàn)了IMoibleFormat接口的Mobile類
Mobile.php
<?phpinclude_once('IMobileFormat.php');class Mobile implements IMobileFormat{    public function formatCSS()    {        echo "引用mobile.css<br />";    }    public function formatGraphics()    {        echo "引用mobile.png圖片<br />";    }    public function verticalLayout()    {        echo '移動端:垂直布局';    }}
Mobile類和Desktop類非常相似, 不過是圖片和CSS引用不同
接下來,我們需要一個適配器,將Desktop和Mobile類結(jié)合在一起
MobileAdapter.php
<?phpinclude_once('IFormat.php');include_once('Mobile.php');class MobileAdapter implements IFormat{    private $mobile;    public function __construct(IMobileFormat $mobileNow)    {        $this->mobile = $mobileNow;    }    public function formatCSS()    {        $this->mobile->formatCSS();    }    public function formatGraphics()    {        $this->mobile->formatGraphics();    }    public function horizontalLayout()    {        $this->mobile->verticalLayout();    }}
可以看到,MobileAdapter實例化時要提供一個Mobile對象實例.還要注意 ,類型提示中使用了IMobileFormat, 確保參數(shù)是一個Mobile對象.有意思的是, Adapter參與者通過實現(xiàn)horizontalLayout()方法來包含verticalLayout()方法.實際上, 所有MobileAdapter方法都包裝了一個Mobile方法.碰巧的是, 適配器參與者中的一個方法并不在適配器接口中(verticalLayout());它們可能完全不同, 適配器只是把它們包裝在適配器接口(IFormat)的某一方法中.
 
客戶調(diào)用(Client)
Client.php
<?phpinclude_once('Mobile.php');include_once('MobileAdapter.php');class Client{    private $mobile;    private $mobileAdapter;    public function __construct()    {        $this->mobile = new Mobile();        $this->mobileAdapter = new MobileAdapter($this->mobile);        $this->mobileAdapter->formatCSS();        $this->mobileAdapter->formatGraphics();        $this->mobileAdapter->horizontalLayout();    }}$worker = new Client();
適配器模式中的Client類必須包裝Adaptee(Mobile)的一個實例, 以便集成到Adapter本身.實例化Adapter時, Client使用Adatee作為參數(shù)來完成Adapter的實例化.所以客戶必須首先創(chuàng)建一個Adapter對象(new Mobile()), 然后創(chuàng)建一個Adapter((new MobileAdapter($this->mobile)).
Client類的大多數(shù)請求都是通過MobileAdapter發(fā)出的. 不過這個代碼的最后他使用了Mobile類的實例.

適配器和變化

      PHP程序員要即該面對變化.不同版本的PHP會變化, 可能增加新的功能, 另外還可能取消一些功能.而且隨著PHP的大大小小的變化,MySQL也在改變.例如, mysql的擴(kuò)展包升級為mysqli, PHP開發(fā)人員需要相應(yīng)調(diào)整, 要改為使用mysqli中的新API.這里適合采用適配器模式嗎?可能不適合.適配器可能適用, 可能不適用,這取決于你的程序如何配置.當(dāng)然可以重寫所有連接和交互代碼, 不過這可不是適配器模式的本意, 這就像是重新安裝USB連接頭, 想把它插進(jìn)標(biāo)準(zhǔn)的墻上插座一樣. 不過, 如果所有原來的mysql代碼都在模塊中, 你可以修改這個模塊(類),換入一個有相同接口的新模塊.只是要使用mysqli而不是mysql.我不認(rèn)為交換等同于適配器, 不過道理是一樣的, 在適配器模式中, 原來的代碼沒有任何改變, 有變化的只是適配器.
如果需要結(jié)合使用兩個不兼容的接口, 這種情況下, 適配器模式最適用.適配器可以完成接口的"聯(lián)姻".可以把適配器看作是一個婚姻顧問;通過創(chuàng)建一個公共接口來克服雙方的差異.利用 這種設(shè)計模式, 可以促成二者的合作,而避免完全重寫某一部分.

上一篇:面試題一

下一篇:回溯法求解迷宮問題

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 迁安市| 宜宾县| 赞皇县| 惠来县| 忻州市| 乐业县| 恩平市| 安塞县| 山阳县| 海原县| 德江县| 扶余县| 林州市| 开封县| 金湖县| 闻喜县| 原阳县| 右玉县| 阿城市| 当涂县| 华蓥市| 汾西县| 静乐县| 股票| 博湖县| 新乐市| 孙吴县| 湖南省| 灯塔市| 鹤峰县| 清镇市| 蓬溪县| 慈利县| 新沂市| 嵊州市| 睢宁县| 桦南县| 密山市| 辽源市| 元江| 麦盖提县|