首頁>技術>

這5種最常見的設計模式裡面沒有單例模式(Singleton),很多人很討厭這種模式。那麼這五種設計模式到底是哪些呢?

工廠模式

(抽象工廠、工廠方法、簡單工廠)

工廠模式存在的意義,在於用new關鍵詞例項化一個物件之後還遠遠不能滿足要求,更重要的是對其進行初始化賦值。比如說微服務當中,需要建立一個http client例項呼叫另一個微服務API。僅僅例項化一個GuzzleHttp\Client肯定是不夠的,比如你還需要配置url和header(比如說jwt token),這樣一來每次需要傳送請求的時候,可以拿來即用,而不是每次都重新配置。

其次值得注意的是,設計模式提倡的是應該依賴抽象類或者介面去程式設計,在依賴注入的時候才使用實現類,保持最大的靈活性和可維護性。

比如一個使用者類(Client)需要寫日誌,日誌有幾種選擇,可以寫到檔案系統、Redis或者資料庫。

interface LoggerFactory {  public function create (): Logger; }interface Logger{  public function log ();}class RedisLogger implements Logger {  private RedisClient $client;    public function __construct (RedisClient $client) {    $this->client = $client;  }    public function log ($msg, $context) {    //save log to redis server      }}class RedisLoggerFactory implements LoggerFactory {  public function create (): Logger {      $redisClient = ... // 此處是一段比較複雜的操作      return new RedisLogger ($redisClient);       }}

LoggerFactory是工廠方法介面,而Logger則是模式裡的產品介面。RedisLoggerFactory是具體工廠方法類,而RedisLogger則是具體產品類。

策略模式(Strategy)

這個模式昨天寫過,請閱讀《PHP版Gof23:策略(Strategy)模式——把一族演算法封裝在不同子類裡》

介面卡模式(Adapter)

介面卡模式有物件介面卡和類介面卡兩種。下面這種情況應該屬於物件介面卡模式,具體的表現是介面卡類關聯適配者(Adaptee)類。

class Storage {        private $source;        public function __constructor(AdapterInterface $source) {        $this->source = $source;    }            public function getOne(int $id) : ?object {        return $this->source->find($id);    }        public function getAll(array $criteria = []) : Collection {        return $this->source->findAll($criteria);    }}interface AdapterInterface {    public function find(int $id) : ?object;    public function findAll(array $criteria = []) : Collection;}class MySqlAdapter implements AdapterInterface {         // ... 上下文          public function find(int $id) : ?object {         $data = $this->mysql->fetchRow(['id' => $id]);     }               public function findAll(array $criteria = []) : Collection {                  $data = $this->mysql->fetchAll($criteria);     }        // ... 上下文}

介面卡模式的應用場景:我們有兩個(介面或者)類,一個是目標類(Target),另一個是適配者類。這兩種類(或者介面)都不可被修改,目標類需要呼叫適配者類的方法,但是二者不相容。打個比方說,中國碼工帶著筆記本去歐洲出差,遇到了插座和筆記本不相容的問題。很明顯,我們不能改變膝上型電腦,也不能改變歐洲的電源插口,怎麼辦呢?只能買一個轉接頭,讓中國的筆記本能夠接通歐洲的電源。如果你財力雄厚,毫不猶豫地在本地買了一個新筆記本,這種情況下介面卡模式就失去了意義。

在上面的例子裡,Target是AdapterInterface,它定義了find和findAll兩個介面方法。適配者類是某種mysql類,它沒有find和findAll這兩個函式,因此產生了不相容的問題。因此例子中採用了物件介面卡的思路,MySqlAdapter實現了AdapterInterface (也就是目標介面),同時關聯了這種mysql類,問題也得到了解決。

觀察者模式(Observer)

觀察者模式又稱為釋出/訂閱模式,這個模式的應用非常非常廣泛,而且威力非常大。在wordpress裡面的鉤子機制,可以透過add_action增加一個鉤子,然後在do_action處呼叫。

我們來看一個簡單一點的例子。

class Theater {        public function present(Movie $movie) : void {                $this->getEventManager()            ->notify(new Event(Event::START, $movie));                $movie->play();        $movie->pause(5);                $this->getEventManager()            ->notify(new Event(Event::PAUSE, $movie));                $movie->finish();        $this->getEventManager()            ->notify(new Event(Event::END, $movie));    }}$theater = new Theater();$theater    ->getEventManager()    ->listen(Event::START, new MessagesListener())    ->listen(Event::START, new LightsListener())    ->listen(Event::PAUSE, new BreakListener())        ->listen(Event::PAUSE, new AdvertisementListener())    ->listen(Event::END, new FeedbackListener())    ->listen(Event::END, new CleaningListener());    $theater->present($movie);

這裡的Event::{START|PAUSE|END}就相當於WP_Hook中的tag。而notify這個方法就類似於WP_Hook裡面的do_action。

在觀察者模式之前,所有的監聽器需要被顯性的關聯到目標類裡。這樣一來可擴充套件性就大大降低了,而顯性關聯的類也和目標類建立了耦合關係。觀察者模式中,這種顯性的關聯解除了,因此耦合關係就不存在了。

裝飾模式(Decor)

裝飾模式是一種結構型模式,目的是在動態的給同一基類的派生類增加額外的功能。

初學者可能對“動態的”這件事情會感到很迷惑,下面我們用一個例項來聊一下“動態的”是什麼意思。

<?phpabstract class Burger {  abstract public function getPrice () : float;  abstract public function getIngredients () : array;    public function __toString () : string {     return 'A burger with ' . implode (', ', $this->getIngredients ()) . ', the total price is '.$this->getPrice ();  }}class BasicBurger extends Burger{  private $price = 10.0;  private $ingredients = ['bread', 'patty'];    public function getPrice () : float {   return $this->price;   }    public function getIngredients () : array {     return $this->ingredients;  }}abstract class BurgerAddon extends Burger {  protected Burger $burger;  public function __construct (Burger $burger) {    $this->burger = $burger;  }    abstract public function getPrice () : float;  abstract public function getIngredients () : array;}class OnionBurgerAddon extends BurgerAddon{  public function getPrice () : float {    return $this->burger->getPrice () + 1.5;  }    public function getIngredients () : array {    return array_merge($this->burger->getIngredients (), ['onion']);  }}class MushroomBurgerAddon extends BurgerAddon{  public function getPrice () : float {    return $this->burger->getPrice () + 3.5;  }    public function getIngredients () : array {    return array_merge($this->burger->getIngredients (), ['mushroom']);  }} $burger = new BasicBurger ();$burger = new OnionBurgerAddon ($burger);$burger = new MushroomBurgerAddon ($burger);echo $burger;// 輸出結果:A burger with bread, patty, onion, mushroom, the total price is 15?>

裝飾模式和代理模式有一些相似,區別也是比較明顯的。裝飾模式會對被裝飾的類的職能進行加強,比如上面的例子當中,透過兩層裝飾,漢堡增加了洋蔥和烤蘑菇,職能(responsibility)得到了加強。但是代理模式對被代理的類的職能不做任何修改。

參考:https://medium.com/@ivorobioff/the-5-most-common-design-patterns-in-php-applications-7f33b6b7d8d6

本文在原文基礎上做了比較大的修改。

12
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 應用上雲2小時燒掉近50萬,創始人:差點破產,簡直噩夢