PHP面試高薪寶典系列: 幾種常見的設計模式(附完整程式碼)

一隻碼發表於2023-02-24

當簡歷上不經意間寫上了精通OOP搞物件這一大招時候,面試官也肯定也會毫不留情的開啟八股文模式,OOP三大特性是啥理解嗎,搞物件的幾個原則需要注意什麼,IOC原理清楚嗎?

此時面對面試官這個能唬住就能拿到50k的物件,自然也可以自信滿滿的開啟裝逼模式背起了他喜歡聽的八股文,面對物件程式設計的特性有封裝,繼承,多型。遵循的基本原則有單一職責原則,里氏替換原則,依賴倒置原則,介面隔離原則。反正整體思想就是高內聚低耦合,只和一個妹紙處物件,愛意滿滿,和其他女生少點瓜葛。

這時候面試官想必覺得你很理解怎麼處物件,把OOP理解的如此透徹,但是總覺得少了點什麼,搞物件不能停留在嘴上面吧,還是實際的擼碼。於是乎,他讓你寫出自己知道的搞物件模式和套路。

白慌,程式碼記得牢擼起來也是小問題。淡定的告訴面試官設計模式是日常程式設計搞物件的時候大佬總結出來的一些套路,按照這些套路更容易讓物件(程式碼)搞得舒服,這些設計模式套路大概也就23種左右,工作中比較常用的也就那麼幾種, 我寫下你看看這套路能不能成。

單例模式

單例模式,這種套路是最常見的,人如其名,就是一心一意的處一個物件,不管別人怎麼干擾就是隻有這一個物件。很明顯,優點就是愛意單一,花銷(記憶體)低不好地方就是限制了自由,難以擴充套件,通常這種巨大的單一物件多種愛意的表達方式有違背單一職責

要實現這種無論怎麼操作都是同一個物件,首先肯定不能直接使用new的方式,因為new一個就多一個物件。要把建構函式私有化,其次既然不能new那物件哪裡來,想必就要使用靜態方法獲取例項物件,在類的內部進行這個new操作,當然內部操作new出來的例項需要先儲存在類私有靜態變數才能透過類的靜態方法獲取內部的例項。也就是常說的三公一私。

class Singleton {
  private static $_instance; //儲存例項
  private function __construct(){} //禁止直接外部例項化
  private function __clone(){} //禁止被clone
  public static function getInstance(){ 
  //獲取儲存在私有靜態變數裡的內部例項
      if (!self::$_instance) {
          self::$_instance = new self();
      }
      return self::$_instance;
  }
}

一般單例模式經常使用於配置檔案的載入,以及資料庫的連線例項,可以減少連線數,以及程式碼執行過程中佔用的記憶體。

觀察者

觀察者模式,吃瓜群眾觀察者首先對自己感興趣的事件做個訂閱註冊,比如陳老師事件,不用一直心心念的去想著,只要加個訂閱對這種事件吃瓜開車的群,等有了陳老師事件的風吹草動,就自動地進行自己預定的吃瓜討論邏輯

/****
 * 事件類
 */
class Event{
   private $_observers = [];
   function addObserver(Observer $observer)
  {  
         $this->_observers[] = $observer;
  }
 function notify() {
      foreach ($this->_observers as $observer) {
            $observer->update();
      }
  }
}

//觀察者介面
interface Observer{
  function update();
}

//觀察者類
class Observer1 implements Observer {
  function update() {
      echo "oberver1 running" . PHP_EOL;
  }
}
class Observer2 implements Observer {
  function update() {
  echo "oberver2 running" . PHP_EOL;
  }
}
$event = new Event();
$event->addObserver(new Observer1()); //為事件增加觀察者
$event->addObserver(new Observer2());
$event->notify();  //通知觀察者進行更新動作

觀察者模式中觀察者只需要訂閱吃瓜訊息, 具體如何吃瓜(收到事件如何處理)可以單獨的去做,這就很好的實現了非同步解耦的效果,在laravel框架中事件處理的方式使用的就是觀察者模式。

簡單工廠

簡單工廠使用單一工廠例項的靜態方法直接返回產品例項,簡單粗暴

//簡單工廠
interface People{
    function like();
}
class Man implements People
{
    function like()
    {
       echo "我喜歡電子產品" . PHP_EOL;
    }
}
class Women implements People
{
    public function like()
    {
        echo "我喜歡化妝品" . PHP_EOL;
    }
}
class Factory
{
    static function createMan()
    {
        return new Man();
    }
    static function createdWomen()
    {
        return new Women();
    }
}
$man = Factory::createMan();
$man->like();
$women = Factory::createdWomen();
$women->like();

工廠方法

對比簡單工廠,單一工廠類抽象出多個工廠類。一個工廠類負責批次生產一個產品例項,增加一個產品需要增加一個工廠單獨生產,不影響原有工廠類

//定義要生產類的介面
interface Product
{
    public function getName();
}
//實現生產類A
class ProductA implements Product
{
    public function getName()
    {
        return 'ProductA';
    }
}
//定義抽象工廠類
abstract class Factory
{
    abstract public function createProduct();
}
//實現生產產品A的工廠類方法
class FactoryA extends Factory
{
    public function createProduct()
    {
        return new ProductA();
    }
}
$factoryA = new FactoryA();
$productA = $factoryA->createProduct();
echo $productA->getName();

抽象工廠

進一步抽象工廠類,使一個工廠類具有分組流水線功能。生產多個零件組裝成一個成品,從而減少工廠類。

interface Mouse{     //滑鼠產品介面(核心類)
      public function getMouse();  //獲取滑鼠
  }
class DellMouse implements Mouse   //戴爾類產品
  {
      public function getMouse(){
          echo "我是戴爾滑鼠<br>";
      }
  }
class LenovoMouse implements Mouse   //聯想類產品
  {
      public function getMouse(){
          echo "我是聯想滑鼠<br>";
      }
  }
interface Keybo{     //鍵盤產品介面(核心類)
      public function getKeybo();  //獲取鍵盤
  }
class DellKeybo implements Keybo   //戴爾類產品
  {
      public function getKeybo(){
          echo "我是戴爾鍵盤<br>";
      }
  }
class LenovoKeybo implements Keybo   //聯想類產品
  {
      public function getKeybo(){
          echo "我是聯想鍵盤<br>";
      }
  }

 abstract class Factory{      //抽象工廠類(核心工廠類)
      abstract static function createMouse(); //生產滑鼠
      abstract static function createKeybo();//生產鍵盤
  }
class DellFactory extends Factory   //戴爾牌工廠繼承工廠類
  {
      public static function createMouse()
      {
          return new DellMouse();   //工廠建立戴爾滑鼠類物件
      }
      public static function createKeybo()
      {
          return new DellKeybo();   //工廠建立戴爾鍵盤類物件
      }
  }
class LenovoFactory extends Factory   //聯想牌工廠繼承工廠類
  {
      public static function createMouse()
      {
          return new LenovoMouse();   //工廠建立聯想滑鼠類物件
      }
      public static function createKeybo()
      {
          return new LenovoKeybo();   //工廠建立聯想鍵盤類物件
      }
  }

 $dell_keybo  =  DellFactory::createKeybo(); 
 echo  $dell_keybo->getKeybo();  //輸出“我是聯想滑鼠” 
 $lenovo_mouse  =  LenovoFactory::createMouse();  
 echo  $lenovo_mouse->getMouse(); //輸出“我是聯想鍵盤”

介面卡模式

透過介面卡類把本身不能直接使用、要適配的類進行一個封裝適配成目標抽象類,便於直接呼叫。
比如laravel中的佇列驅動可以實現redis佇列,同步佇列,mysql佇列任意切換。本身就是規定了要實現適配的佇列基本操作方法,至於各種驅動方式下不能進行直接呼叫的操作方法要進行適配完成

//目標類
interface Target
{
    public function request();
}
//要進行適配的類
class Adaptee
{
    public function specificRequest()
    {
        echo '特殊請求!' . PHP_EOL;
  }
}
//介面卡
class Adapter implements Target
{
    private $adaptee;

    public function  __construct(Adaptee  $adaptee)
    {
        $this->adaptee = $adaptee;
    }
    public function request()
    {
        $this->adaptee->specificRequest();
        // TODO: Implement request() method.
    }
}
$adapter  = new Adapter(new Adaptee());
$adapter->request();

策略模式

統籌大局的策略呼叫者能修改接收不同的策略處理邏輯,最終得到不同的處理結果。

/****
 * 策略模式
 */
interface Strategy{
  public function doLogic($data);
}
class StrategyA implements Strategy {
  public function doLogic($data){
 sort($data);
 return $data;
  }
}
class StrategyB implements Strategy {
  public function doLogic($data){
 ksort($data);
 return $data;
  }
}

class Context{
  private $strategy = null;
 function __construct(Strategy $strategy){
  $this->strategy = $strategy;
  }
  function doLogic ($data) {
  return $this->strategy->doLogic($data);
  }
}

$res = (new Context(new StrategyB()))->doLogic(["a" =>1 , "b"]);

職責鏈

職責鏈模式簡單可以理解為在一個組織架構中,每個人扮演的角色不同,被賦予的職責不同。例如現實生活中,作為小小的一個擼碼程式猿,一般組織架構中都會有自己的直屬leader,然後有技術部CTO,以及老闆,每當我們請假的時候都需要提請假審批,請一天需要直屬領導,如果是請3天或者更久需要CTO,CEO來一個個審批,挨個臨幸,最終形成了一條權利責任鏈。

//職責角色抽象類
abstract class Handler
{
    protected $leader;

    public function setLeader($leader) {
        $this->leader = $leader;
    }

    abstract public function handleRequest($request);
}
//直屬領導批三天的假
class LeaderHandler extends Handler
{
    public function  handleRequest($request)
    {
        if ($request == '請加3天') {
            echo "直屬領導批示請假成功" . PHP_EOL;
        } elseif ($this->leader){
            $this->leader->handleRequest($request);
        }
        // TODO: Implement handleRequest() method.
    }
}
//老闆批5天的假
class BossHandler extends Handler
{
    public function  handleRequest($request)
    {
        if ($request == '請加5天') {
            echo "老闆批示請假成功" . PHP_EOL;
        } elseif ($this->leader){
            $this->leader->handleRequest($request);
        }
        // TODO: Implement handleRequest() method.
    }
}
$request = '請加5天';
$leader = new LeaderHandler();
$leader->setLeader(new BossHandler());
$leader->handleRequest($request);
本作品採用《CC 協議》,轉載必須註明作者和本文連結
技術變現,讓程式碼多一份價值

相關文章