首頁  >  文章  >  後端開發  >  淺談PHP中的裝飾器模式

淺談PHP中的裝飾器模式

青灯夜游
青灯夜游轉載
2021-06-23 18:40:392905瀏覽

裝飾器模式允許為一個現有的物件添加新的功能,同時又不改變其結構。本篇文章帶大家了解PHP中的裝飾器模式,介紹一下裝飾器的好處以及最適合的場景。

淺談PHP中的裝飾器模式

工廠模式告一段落,我們來研究其他一些模式。不知道各位大佬有沒有嘗試女裝?據說女裝大佬程式設計師很多喲。其實,今天的裝飾模式就跟化妝這件事很像。相信如果有程式媛MM在的話,馬上就能跟你講清楚這個設計模式。

Gof類圖及解釋

裝飾這兩個字,我們暫且把他變成化妝。首先你得有一張臉,然後打底,然後上妝,可以早上來個淡妝上班,也可以下班的時候補成濃妝出去嗨。當然,碼農們下班的時間點正好是能趕上夜場的下半場的。話說回來,不管怎麼化妝,你的臉還是你的臉,有可能可以化成別人不認識的另一個人,但這的的確確還是你的臉。這就是裝飾器,對物件(臉)進行各種裝飾(化妝),讓這個臉更好看(增加職責)。

GoF定義:動態地為一個物件增加一些額外的職責,就增加功能來說,Decorator模式相比產生子類別更為靈活

GoF類別圖:

淺談PHP中的裝飾器模式

程式碼實作:

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混去吧! !

完整程式碼:裝飾模式

https://github.com/zhangyue0503/designpatterns-php/blob/master/04.decorator/source/decorator. php

實例

繼續來發短信,之前我們用工廠模式解決了多個簡訊運營商的問題。這回我們要解決的是簡訊內容模板的問題。對於推廣類的簡訊來說,根據最新的廣告法,我們是不能出現「全國第一」、「全世界第一」這類的詞語的,當然,一些不太文明的用語我們也是不能使用的。

现在的情况是这样的,我们有一个很早之前的短信模板类,里面的内容是固定的,老系统依然还是使用这个模板,老系统是面对的内部员工,对语言内容的要求不高。而新系统则需要向全网发送,也就是内外部的用户都要发送。这时,我们可以用装饰器模式来对老系统的短信模板进行包装。其实说简单点,我们就是用装饰器来做文本替换的功能。好处呢?当然是可以不去改动原来的模板类中的方法就实现了对老模板内容的修改扩展等。

短信发送类图:

淺談PHP中的裝飾器模式

完整源码:短信发送装饰器方法

https://github.com/zhangyue0503/designpatterns-php/blob/master/04.decorator/source/message-decorator.php

<?php
// 短信模板接口
interface MessageTemplate
{
    public function message();
}

// 假设有很多模板实现了上面的短信模板接口
// 下面这个是其中一个优惠券发送的模板实现
class CouponMessageTemplate implements MessageTemplate
{
    public function message()
    {
        return &#39;优惠券信息:我们是全国第一的牛X产品哦,送您十张优惠券!&#39;;
    }
}

// 我们来准备好装饰上面那个过时的短信模板
abstract class DecoratorMessageTemplate implements MessageTemplate
{
    public $template;
    public function __construct($template)
    {
        $this->template = $template;
    }
}

// 过滤新广告法中不允许出现的词汇
class AdFilterDecoratorMessage extends DecoratorMessageTemplate
{
    public function message()
    {
        return str_replace(&#39;全国第一&#39;, &#39;全国第二&#39;, $this->template->message());
    }
}

// 使用我们的大数据部门同事自动生成的新词库来过滤敏感词汇,这块过滤不是强制要过滤的内容,可选择使用
class SensitiveFilterDecoratorMessage extends DecoratorMessageTemplate
{
    public $bigDataFilterWords = [&#39;牛X&#39;];
    public $bigDataReplaceWords = [&#39;好用&#39;];
    public function message()
    {
        return str_replace($this->bigDataFilterWords, $this->bigDataReplaceWords, $this->template->message());
    }
}

// 客户端,发送接口,需要使用模板来进行短信发送
class Message
{
    public $msgType = &#39;old&#39;;
    public function send(MessageTemplate $mt)
    {
        // 发送出去咯
        if ($this->msgType == &#39;old&#39;) {
            echo &#39;面向内网用户发送&#39; . $mt->message() . PHP_EOL;
        } else if ($this->msgType == &#39;new&#39;) {
            echo &#39;面向全网用户发送&#39; . $mt->message() . PHP_EOL;
        }

    }
}

$template = new CouponMessageTemplate();
$message = new Message();

// 老系统,用不着过滤,只有内部用户才看得到
$message->send($template);

// 新系统,面向全网发布的,需要过滤一下内容哦
$message->msgType = &#39;new&#39;;
$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进行了装饰,有兴趣的同学可以去了解下

推荐学习:《PHP视频教程

以上是淺談PHP中的裝飾器模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除