PHP設計模式之裝飾器模式定義與用法詳解
本文實例講述了PHP設計模式之裝飾器模式定義與用法。分享給大家供大家參考,具體如下:
什么是裝飾器模式
作為一種結構型模式, 裝飾器(Decorator)模式就是對一個已有結構增加"裝飾".
適配器模式, 是為現在有結構增加的是一個適配器類,.將一個類的接口,轉換成客戶期望的另外一個接口.適配器讓原本接口不兼容的類可以很好的合作.
裝飾器模式是將一個對象包裝起來以增強新的行為和責任.裝飾器也稱為包裝器(類似于適配器)
有些設計設計模式包含一個抽象類,而且該抽象類還繼承了另一個抽象類,這種設計模式為數不多,而裝飾器就是其中之一.
什么時候使用裝飾器模式
基本說來, 如果想為現有對象增加新功能而不想影響其他對象, 就可以使用裝飾器模式.如果你好不容易為客戶創(chuàng)建了一個網站格式, 主要組件的工作都很完美, 客戶請求新功能時, 你肯定不希望推翻重來, 再重新創(chuàng)建網站. 例如, 假設你已經構建了客戶原先請求的組件, 之后客戶又有了新的需求, 希望在網站中包含視頻功能. 你不用重寫原先的組件, 只需要"裝飾"現有組件, 為它們增加視頻功能. 這樣即可以保持原來的功能,還可以增加新功能.
有些項目可能有時需要裝飾, 而有時不希望裝飾, 這些項目體現了裝飾器設計模式的另一個重要特性.假設你的基本網站開發(fā)模式可以滿足大多數客戶的要求. 不過, 胡些客戶還希望有一些特定的功能來滿足他們的需求. 并不是所有人都希望或需要這些額外的功能. 作為開發(fā)人員, 你希望你創(chuàng)建的網站能滿足客戶的業(yè)務目標. 所以需要提供"本地化"(customerization)特性, 即針對特定業(yè)務提供的特性. 利用裝飾器模式, 不僅能提供核心功能, 還可以用客戶要求的特有功能"裝飾"這些核心功能.
簡單的裝飾器例子
一個web開發(fā)企業(yè),計劃建立一個基本網站,并提供一些增強功能. 不過,web開發(fā)人員知道, 盡管這個基本計劃適用于大多數客戶, 但客戶以后很可能還希望進一步提升, 利用裝飾器模式, 可以很容易地增加多個具體裝飾器,另外由于你能選擇要增加的裝飾器, 所以企業(yè)不僅能控制功能, 還可以控制項目的成本 .
Component接口
Component參與者是一個接口, 在這里, 它是一個抽象類IComponent. 這個抽象類只有一個屬性$site, 另外有兩個抽象方法getSite()
和getPrice().Component
參與者具體為具體組件和Decorator參與者抽象類建立接口:
IComponent.php
<?php abstract class IComponent { protected $site; abstract public function getSite(); abstract public function getPrice(); }
Decorator接口
這個例子中的裝飾器接口可能會讓你驚訝.這是一個抽象類,而且它還擴展了另一個抽象類! 這個類的作用就是維護組件接口(IComponent)的一個引用, 這是通過擴展IComponent完成的:
Decorator.php
<?php abstract class Decorator extends IComponent { /* 任務是維護Component的引用 繼承getSite()和getPrice() 因為仍然是抽象類,所以不需要實現父類任何一個抽象方法 */ }
Decorator類的主要作用就是維護組件接口的一個引用.
在所有的裝飾器模式實現中, 你會發(fā)現,具體組件和裝飾順都有相同的接口. 它們的實現可能不同, 另外除了基本接口的屬性和方法外, 組件和裝飾器可能還有額外的屬性和方法.
具體組件
這個例子中只有一個具體組件,它生成一個網站名, 另外生成一個基本網站報價:
BasicSite.php
<?php class BasicSite extends IComponent { public function __construct() { $this->site = "Basic Site"; } public function getSite() { return $this->site; } public function getPrice() { return 1200; } }
兩個抽象方法都使用直接賦值來實現, 不過靈活性并不體現在如何改變設置的值.實際上, 要通過增加裝飾器值來改變"Basic Site"值.
具體裝飾器
這個例子中的具體裝飾器與具體組件有相同的接口.實際上, 它們是從Decorator抽象類(而不是IComponent類)繼承了這個接口. 不過,要記住, Decorator所做的就是繼承IComponent接口.
Maintenance.php
<?php class Maintenance extends Decorator { public function __construct(IComponent $siteNow) { $this->site = $siteNow; } public function getSite() { $format = "<br /> Maintenance"; return $this->site->getSite() . $format; } public function getPrice() { return 950 + $this->site->getPrice(); } }
這個裝飾器Maintenance在改變了site的值, 還有包裝的具體組件價格上還會增加它自己 的價格. 另個兩個具體裝飾器與Maintenance裝飾器也類似
Video.php
<?php class Video extends Decorator { public function __construct(IComponent $siteNow) { $this->site = $siteNow; } public function getSite() { $format = "<br /> Video"; return $this->site->getSite() . $format; } public function getPrice() { return 350 + $this->site->getPrice(); } }
DataBase.php
<?php class DataBase extends Decorator { public function __construct(IComponent $siteNow) { $this->site = $siteNow; } public function getSite() { $format = "<br /> DataBase"; return $this->site->getSite() . $format; } public function getPrice() { return 800 + $this->site->getPrice(); } }
測試這個應用時,可以看到,在基本的價格之上還會增加各個裝飾器的價格.另外還能指定裝飾器名的格式, 增加了兩個空格,使之縮進
裝飾器實現中最重要的元素之五就是構造函數, 要為構造函數提供一個組件類型. 由于這里只有一個具體組件, 所有裝飾器的實例化都會使用這個組件. 使用多個組件時, 裝飾器可以包裝應用中的一部分或全部組件, 也可以不包裝任何組件.
客戶
Client類并不是這個設計模式的一部分, 但是正確使用Client類至關重要.每個裝飾器在實例化時"包裝"組件, 不過, 首先必須創(chuàng)建一個要包裝的對象, 這里是BasicSite類實例
Client.php
<?php function __autoload($class_name) { include $class_name . '.php'; } class Client { private $basicSite; public function __construct() { $this->basicSite = new BasicSite(); $this->basicSite = $this->WrapComponent($this->basicSite); $siteShow = $this->basicSite->getSite(); $format = "<br /> <strong>Total= $"; $price = $this->basicSite->getPrice(); echo $siteShow . $format . $price . "</strong>"; } private function WrapComponent(IComponent $component) { $component = new Maintenance($component); $component = new Video($component); $component = new DataBase($component); return $component; } } $worker = new Client();
wrapComponent()
方法檢查傳入的BasicSite實例, 以確保參數有正確的數據類型(IComponent), 然后分別實例化3個裝飾器, 對該實例對象進行裝飾.
Basic Site
Maintenance
Video
DataBase
Total= $3300
適配器和裝飾器模式都有另外一個名字"包裝器"(wrapper)".
適配器可以"包裝"一個對象, 創(chuàng)建一個與Adaptee兼容的接口, 而無須對它做任何修改.
裝飾器也可以"包裝"一個組件對象, 這樣就能為這個已胡的組件增加職責, 而無須對它做任何修改.
下面的代碼展示了Client如何將組件對象($component)包裝在裝飾器(Maintence)中:
$component = new Maintenance($component);
類似于"接口", 在計算機編程中用到"包裝器"時, 不同的上下文會有不同的用法和含義. 一般來講, 在設計模式中使用"包裝器"是為了處理接口的不兼容, 或者希望為組件增加功能,包裝器就表示用來減少不兼容性的策略.
更多關于PHP相關內容感興趣的讀者可查看本站專題:《php面向對象程序設計入門教程》、《PHP基本語法入門教程》、《PHP數組(Array)操作技巧大全》、《php字符串(string)用法總結》、《php+mysql數據庫操作入門教程》及《php常見數據庫操作技巧匯總》
希望本文所述對大家PHP程序設計有所幫助。
相關文章
PHP數組操作實例分析【添加,刪除,計算,反轉,排序,查找等】
這篇文章主要介紹了PHP數組操作,結合實例形式分析php針對數組的添加,刪除,計算,反轉,排序,查找等操作實現技巧,需要的朋友可以參考下2016-12-12php數組函數序列之array_flip() 將數組鍵名與值對調
array_flip() 函數將使數組的鍵名與其相應值調換,即鍵名變成了值,而值變成了鍵名2011-11-11