工廠模式告一段落,我們來研究其他一些模式,不知道各位大佬有沒有嘗試過女裝?據說女裝大佬程式員很多喲,其實,今天的裝飾器模式就和化妝這件事很像,相信如果有程式媛MM在的話,馬上就能和你講清楚這個設計模式,
Gof類圖及解釋
裝飾這兩個字,我們暫且把他變成化妝,首先你得有一張臉,然后打底,然后上妝,可以早上來個淡妝上班,也可以下班的時候補成濃妝出去嗨,當然,碼農們下班的時間點正好是能趕上夜場的下半場的,話說回來,不管怎么化妝,你的臉還是你的臉,有可能可以化成別人不認識的另一個人,但這的的確確還是你的臉,這就是裝飾器,對物件(臉)進行各種裝飾(化妝),讓這個臉更好看(增加職責),
GoF定義:動態地給一個物件添加一些額外的職責,就增加功能來說,Decorator模式相比生成子類更為靈活
GoF類圖

代碼實作
interface Component{
public function operation();
}
class ConcreteComponent implements Component{
public function operation(){
echo "I'm face!" . PHP_EOL;
}
}
很簡單的一個介面和一個實作,這里我們就把具體的實作類看作是一張臉吧!
abstract class Decorator implements Component{
protected $component;
public function __construct(Component $component){
$this->component = $component;
}
}
抽象的裝飾者類,實作Component介面,但并不實作operation()方法,讓子類去實作,在這里主要保存一個Componet的參考,一會就要對他進行裝飾,對應到上方的具體類,我們就是要準備給臉化妝啦!
class ConcreteDecoratorA extends Decorator{
public $addedState = 1; // 沒什么實際意義的屬性,只是區別于ConcreteDecoratorB
public function operation(){
echo $this->component->operation() . "Push " . $this->addedState . " cream!" . PHP_EOL;
}
}
class ConcreteDecoratorB extends Decorator{
public function operation(){
$this->component->operation();
$this->addedBehavior();
}
// 沒什么實際意義的方法,只是區別于ConcreteDecoratorA
public function addedBehavior(){
echo "Push 2 cream!" . PHP_EOL;
}
}
兩個具體裝飾者,在這里我是涂了兩次霜,畢竟是純爺們,對化妝這事兒真的是不了解,好像第一步應該先是打粉底吧?不過這次就這樣,我們這兩個裝飾器實作的就是給臉上涂兩層霜,
- 從代碼中可以看出,我們是一直對具體的那個ConcreteComponent物件來進行包裝
- 再往下的話其實我們是對他的operation()這個方法包裝了兩次,每次都是在前一次的基礎上加了一點點東西
- 不要糾結于A和B裝飾器上的added屬性和方法,他們只是GoF類圖中用以區別這兩個裝飾器不是同一個東西,每個裝飾器都可以干很多別的事,Component物件也不一定只有operation()這一個方法,我們可以選擇性的去裝飾物件中的全部或者部分方法
- 好像我們都繼承了Component,直接子類一路重寫不就行了,搞這費勁干嘛?親,了解下組合的概念喲,我們的Decorator父類里面是一個真實物件的參考哦,解耦了自身哦,我們只給真實的物件去做包裝,您可別直接實體化裝飾器來直接用
- 還是沒懂?好處呢?老系統的類啊、方法啊你敢隨便亂改?想給前任寫的牛(S)逼(B)代碼擴展新功能時不妨試試裝飾器這貨,說不定有奇效!
手機這玩意干不過某米、某O、某為,這沒法玩呀,好吧,哥們去專心做手機殼吧!嗯,我先準備了一個透明殼(Component),貌似有點丑,沒辦法,誰叫哥們窮,給某米的加上各種純色(DecoratorA1),然后背后印上各種顏色的植物(DecoratorB1)吧;某O的手機最近喜歡找流量明顯做代言,那我給他的手機殼就用各種炫彩色(DecoratorA2)和明星的卡通頭像(DecoratorB2);最后的某為,好像手機已經開始引領業界潮流了,折疊屏這玩意不是要砸我這賣手機殼的生意嘛!!好吧,哥不給你們做了,還是跟我的某米、某O混去吧!!
完整代碼:裝飾器模式
實體
繼續來發短信,之前我們用工廠模式解決了多個短信運營商的問題,這回我們要解決的是短信內容模板的問題,對于推廣類的短信來說,根據最新的廣告法,我們是不能出現“全國第一”、“全世界第一”這類的詞語的,當然,一些不太文明的用語我們也是不能使用的,
現在的情況是這樣的,我們有一個很早之前的短信模板類,里面的內容是固定的,老系統依然還是使用這個模板,老系統是面對的內部員工,對語言內容的要求不高,而新系統則需要向全網發送,也就是內外部的用戶都要發送,這時,我們可以用裝飾器模式來對老系統的短信模板進行包裝,其實說簡單點,我們就是用裝飾器來做文本替換的功能,好處呢?當然是可以不去改動原來的模板類中的方法就實作了對老模板內容的修改擴展等,
短信發送類圖

完整原始碼:短信發送裝飾器方法
<?php
// 短信模板介面
interface MessageTemplate
{
public function message();
}
// 假設有很多模板實作了上面的短信模板介面
// 下面這個是其中一個優惠券發送的模板實作
class CouponMessageTemplate implements MessageTemplate
{
public function message()
{
return '優惠券資訊:我們是全國第一的牛X產品哦,送您十張優惠券!';
}
}
// 我們來準備好裝飾上面那個過時的短信模板
abstract class DecoratorMessageTemplate implements MessageTemplate
{
public $template;
public function __construct($template)
{
$this->template = $template;
}
}
// 過濾新廣告法中不允許出現的詞匯
class AdFilterDecoratorMessage extends DecoratorMessageTemplate
{
public function message()
{
return str_replace('全國第一', '全國第二', $this->template->message());
}
}
// 使用我們的大資料部門同事自動生成的新詞庫來過濾敏感詞匯,這塊過濾不是強制要過濾的內容,可選擇使用
class SensitiveFilterDecoratorMessage extends DecoratorMessageTemplate
{
public $bigDataFilterWords = ['牛X'];
public $bigDataReplaceWords = ['好用'];
public function message()
{
return str_replace($this->bigDataFilterWords, $this->bigDataReplaceWords, $this->template->message());
}
}
// 客戶端,發送介面,需要使用模板來進行短信發送
class Message
{
public $msgType = 'old';
public function send(MessageTemplate $mt)
{
// 發送出去咯
if ($this->msgType == 'old') {
echo '面向內網用戶發送' . $mt->message() . PHP_EOL;
} else if ($this->msgType == 'new') {
echo '面向全網用戶發送' . $mt->message() . PHP_EOL;
}
}
}
$template = new CouponMessageTemplate();
$message = new Message();
// 老系統,用不著過濾,只有內部用戶才看得到
$message->send($template);
// 新系統,面向全網發布的,需要過濾一下內容哦
$message->msgType = 'new';
$template = new AdFilterDecoratorMessage($template);
$template = new SensitiveFilterDecoratorMessage($template);
// 過濾完了,發送吧
$message->send($template);
說明
- 裝飾器的最大好處:一是不改變原有代碼的情況下對原有代碼中的內容進行擴展,開放封閉原則;二是每個裝飾器完成自己的功能,單一職責;三是用組合實作了繼承的感覺;
- 最適用于:給老系統進行擴展
- 要小心:過多的裝飾者會把你搞暈的
- 不一定都是對同一個方法進行裝飾,其實裝飾者應該更多的用于對物件的裝飾,對物件進行擴展,這里我們都是針對一個方法的輸出進行裝飾,但僅限此文,裝飾器的應用其實更加廣泛
- 裝飾器的特點是全部都繼承自一個主介面或類,這樣的好處就是回傳的物件是相同的抽象資料,具有相同的行為屬性,否則,就不是裝飾之前的物件,而是一個新物件了
- 有點不好理解沒關系,我們這次的例子其實也很勉強,這個設計模式在《Head First設計模式》中有提到Java的I/O系列介面是使用的這種設計模式:FileInputStream、LineNumberInputStream、BufferInputStream等
- Laravel框架中的中間件管道,這里其實是多種模式的綜合應用,其中也應用到了裝飾器模式:Laravel HTTP——Pipeline 中間件裝飾者模式原始碼分析
- 另外在Laravel中,日志處理這里也是對Monolog進行了裝飾,有興趣的同學可以去了解下
下期看點
又是大伽駕到,電源配接器了解吧?變壓器總見過吧?你可能用過,也可能沒用過,但你一定聽說過這個非常非常出名的配接器模式,
關注公眾號:【硬核專案經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜索【硬核專案經理】
B站ID:482780532
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/234619.html
標籤:PHP
上一篇:Centos8.2云服務器環境安裝Tomcat8.5
下一篇:程式員必備技能
