摘要:裝飾器模式允許向一個現有的對象添加新的功能,同時又不改變其結構。這便是裝飾模式,通過一層一層的裝飾,我們可以靈活的得到我們想要的結果。可以輕松的添加新的裝飾器類或者新的組件來創建靈活的結構。
前言
在編碼的時候,我們為了擴展一個類經常是使用繼承方式來實現,隨著擴展功能的增多,子類會越來越膨脹,使系統變得不靈活。
裝飾器模式( Decorator Pattern )允許向一個現有的對象添加新的功能,同時又不改變其結構。它能讓我們在擴展類的時候讓系統較好的保持靈活性。
那么裝飾器模式具體是什么樣的呢?
從一個情景開始我們有一塊地,在這塊地上,我們要蓋一棟有好幾間房間的別墅,每間房間的裝修費用都不同,現在,我們要對蓋別墅的費用進行計算。
先定義一個Land類,表示這塊地,Land類定義了在這塊地上蓋別墅需要花錢這個規則。
abstract class Land { abstract public function cost(); }
Land已經定義好了在這塊地上蓋房需要花錢的這個規則了,但是蓋一間房間具體花多少錢呢?
此時我們再定義一個Room類,這個類具體的定義了一個房間建造的基本費用(一個最簡單房間,里面啥也沒有的)。
class Room extends Land { private $money = 1000; public function cost() { return $this->money; } }
然后開始建造房間,我們建了兩個房間,分別是客廳和餐廳,用LivingRoom類和DiningRoom類來表示
class LivingRoom extends Room { public function cost() { return parent::cost()+200; //客廳的建造費用在房屋建造費用的基礎上多200,比如要買沙發,電視 } } class DiningRoom extends Room { public function cost() { return parent::cost()+100; //餐廳的建造費用在房屋建造費用的基礎上多100,比如買餐桌 } }
現在,我們很容易就能得到建造一間客廳所需的花費
$livingRoomCost = new LivingRoom(); echo $livingRoomCost->cost();問題的產生
不過,這樣的結構并不具備靈活性,雖然我們可以很容易的分別得出建造一間客廳和建造一間餐廳的費用,但是,如果我買的地比較小,只能把餐廳和客廳建在同一個房間里,那要怎么去計算費用?難道還要很麻煩的去創建一個包含客廳和餐廳的LivingDiningRoom類?這樣做的話除了麻煩,還會使代碼產生重復。
解決問題為了更好的解決這個問題,我們得做一些調整,同樣先聲明Land類和Room類,不同的是,引入了一個房間的裝飾類RoomDecorator,它繼承了Land類,因為沒有實現Land類的cost()方法,所以需將它聲明為抽象類,并且定義了一個以Land類的對象為參數的構造方法,傳入的對象會保存在$land屬性中,該屬性聲明為protected,以便子類訪問。具體如下。
abstract class RoomDecorator extends Land { protected $land; public function __construct(Land $land) { $this->land = $land; } }
然后我們再重新定義客廳類和餐廳類
class LivingRoom extends RoomDecorator { public function cost() { return $this->land->cost()+200; } } class DiningRoom extends RoomDecorator { public function cost() { return $this->land->cost()+100; } }
這兩個類都擴展自RoomDecorator類,這意味著它們都擁有指向Land對象的引用。當它們的cost()方法被調用時,都會先調用所引用的Land類對象的cost()方法,然后再執行自己特有的操作。
所以這時候,建造一間客廳所需的費用是這樣計算
$livingRoomCost = new LivingRoom(new Room()); echo $livingRoomCost->cost(); //輸出1200
建造一間餐廳所需的費用是這樣計算
$diningRoomCost = new DiningRoom(new Room()); echo $diningRoomCost->cost(); //輸出1100
回到剛才的問題,如果我們需計算建造一間包含客廳餐廳的房間所需費用,代碼如下
$livingRoom = new DiningRoom(new LivingRoom(new Room())); echo $livingRoom->cost(); //輸出1300
看,我們現在計算建造費用的思路是:計算出基礎房間的費用 --> 在基礎房間上裝飾成客廳的費用 --> 在客廳的基礎上加裝飾餐廳的費用 --> 得到包含客廳餐廳的房間費用。已經不需要麻煩的通過創建一個LivingDiningRoom類來計算包含客廳餐廳的房間建造費用了。
這便是裝飾模式,通過一層一層的裝飾,我們可以靈活的得到我們想要的結果。可以輕松的添加新的裝飾器類或者新的組件來創建靈活的結構。
完整代碼money; } } //裝飾器 abstract class RoomDecorator extends Land { protected $land; public function __construct(Land $land) { $this->land = $land; } } class LivingRoom extends RoomDecorator { public function cost() { return $this->land->cost()+200; } } class DiningRoom extends RoomDecorator { public function cost() { return $this->land->cost()+100; } } $livingRoomCost = new LivingRoom(new Room()); echo $livingRoomCost->cost(); //輸出1200 $diningRoomCost = new DiningRoom(new Room()); echo $diningRoomCost->cost(); //輸出1100 $livingDining = new DiningRoom(new LivingRoom(new Room())); echo $livingDining->cost(); //輸出1300
the end.
happy coding! ^_^
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/25853.html
摘要:三的洋蔥模型這里簡單講講在中是如何分層的,也就是說請求到達服務端后如何層層處理,直到響應請求并將結果返回客戶端。從而使得端支持跨域等。 ??最近已經使用過一段時間的nestjs,讓人寫著有一種java spring的感覺,nestjs可以使用express的所有中間件,此外完美的支持typescript,與數據庫關系映射typeorm配合使用可以快速的編寫一個接口網關。本文會介紹一下作...
摘要:三的洋蔥模型這里簡單講講在中是如何分層的,也就是說請求到達服務端后如何層層處理,直到響應請求并將結果返回客戶端。從而使得端支持跨域等。 ??最近已經使用過一段時間的nestjs,讓人寫著有一種java spring的感覺,nestjs可以使用express的所有中間件,此外完美的支持typescript,與數據庫關系映射typeorm配合使用可以快速的編寫一個接口網關。本文會介紹一下作...
摘要:如果看不懂的話,可以在評論區中提問,我會第一時間回答你無論何時我一直都在嗯哼該文章屬于編程中的那些經典套路設計模式匯總系列 在正式閱讀前,我先談談我們該用什么姿勢和心態學習設計模式: 如果你還沒有過多的編程經驗(泛指半年以下),我建議你把它當做小說來看,能看懂多少是多少,因為半年以下經驗的程序員用到設計模式的情況只會出現在面試上,至于實際工作中?相對來說這部分不會由你負責。 如果你已...
閱讀 3576·2021-09-24 09:48
閱讀 1100·2021-09-10 10:51
閱讀 3278·2019-08-30 13:03
閱讀 3326·2019-08-30 12:51
閱讀 1395·2019-08-30 11:22
閱讀 1071·2019-08-29 18:38
閱讀 2042·2019-08-29 16:41
閱讀 3207·2019-08-29 15:32