PHP設計模式之裝飾器模式

工廠模式告一段落,咱們來研究其餘一些模式。不知道各位大佬有沒有嘗試過女裝?聽說女裝大佬程序員不少喲。其實,今天的裝飾器模式就和化妝這件事很像。相信若是有程序媛MM在的話,立刻就能和你講清楚這個設計模式。php

Gof類圖及解釋

裝飾這兩個字,咱們暫且把他變成化妝。首先你得有一張臉,而後打底,而後上妝,能夠早上來個淡妝上班,也能夠下班的時候補成濃妝出去嗨。固然,碼農們下班的時間點正好是能遇上夜場的下半場的。話說回來,無論怎麼化妝,你的臉仍是你的臉,有可能能夠化成別人不認識的另外一我的,但這的的確確仍是你的臉。這就是裝飾器,對對象(臉)進行各類裝飾(化妝),讓這個臉更好看(增長職責)。laravel

GoF定義:動態地給一個對象添加一些額外的職責,就增長功能來講,Decorator模式相比生成子類更爲靈活git

GoF類圖程序員

裝飾器方法結構類圖

代碼實現github

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

相關文章
相關標籤/搜索