1. 程式人生 > 實用技巧 >【進階】PHP常見的設計模式,我覺得你還是要搞懂!

【進階】PHP常見的設計模式,我覺得你還是要搞懂!

設計模式六大原則

  • 開放封閉原則:一個軟體實體如類、模組和函式應該對擴充套件開放,對修改關閉。

  • 里氏替換原則:所有引用基類的地方必須能透明地使用其子類的物件.

  • 依賴倒置原則:高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。

  • 單一職責原則:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。

  • 介面隔離原則:客戶端不應該依賴它不需要的介面;一個類對另一個類的依賴應該建立在最小的介面上。

  • 迪米特法則:一個物件應該對其他物件保持最少的瞭解。

一、單例設計模式(Singleton)

所謂單例模式,即在應用程式中最多隻有該類的一個例項存在,一旦建立,就會一直存在於記憶體中!

應用場景:

單例設計模式常應用於資料庫類設計,採用單例模式,只連線一次資料庫,防止開啟多個數據庫連線。

一個單例類應具備以下特點:

單例類不能直接例項化建立,而是隻能由類本身例項化。因此,要獲得這樣的限制效果,建構函式必須標記為private,從而防止類被例項化。

需要一個私有靜態成員變數來儲存類例項和公開一個能訪問到例項的公開靜態方法。

在PHP中,為了防止他人對單例類例項克隆,通常還為其提供一個空的私有__clone()方法。

單例模式的例子:

<?php  

/** 
* Singleton of Database 
*/  
class Database  
{  
  // We need a static private variable to store a Database instance.  
  privatestatic $instance;  

  // Mark as private to prevent it from being instanced.  
  private function__construct()  
  {  
    // Do nothing.  
  }  

  private function__clone()   
  {  
    // Do nothing.  
  }  

  public static function getInstance()   
  {  
    if (!(self::$instance instanceof self)) {  
      self::$instance = new self();  
    }  

    return self::$instance;  
  }  
}  

$a =Database::getInstance();  
$b =Database::getInstance();  

// true  
var_dump($a === $b);  

二、工廠設計模式

要是當操作類的引數變化時,只用改相應的工廠類就可以

工廠設計模式常用於根據輸入引數的不同或者應用程式配置的不同來建立一種專門用來例項化並返回其對應的類的例項。

使用場景: 使用方法 new例項化類,每次例項化只需呼叫工廠類中的方法例項化即可。

優點:由於一個類可能會在很多地方被例項化。當類名或引數發生變化時,工廠模式可簡單快捷的在工廠類下的方法中 一次性修改,避免了一個個的去修改例項化的物件。

我們舉例子,假設矩形、圓都有同樣的一個方法,那麼我們用基類提供的API來建立例項時,通過傳引數來自動建立對應的類的例項,他們都有獲取周長和麵積的功能。

例子

<?php  

interface InterfaceShape   
{  
 function getArea();  
 function getCircumference();  
}  

/** 
* 矩形 
*/  
class Rectangle implements InterfaceShape  
{  
  private $width;  
  private $height;  

  public function __construct($width, $height)  
  {  
    $this->width = $width;  
    $this->height = $height;  
  }  

  public function getArea()   
  {  
    return $this->width* $this->height;  
  }  

  public function getCircumference()  
  {  
    return 2 * $this->width + 2 * $this->height;  
  }  
}  

/** 
* 圓形 
*/  
class Circle implements InterfaceShape  
{  
  private $radius;  

  function __construct($radius)  
  {  
    $this->radius = $radius;  
  }  


  public function getArea()   
  {  
    return M_PI * pow($this->radius, 2);  
  }  

  public function getCircumference()  
  {  
    return 2 * M_PI * $this->radius;  
  }  
}  

/** 
* 形狀工廠類 
*/  
class FactoryShape   
{   
  public static function create()  
  {  
    switch (func_num_args()) {  
      case1:  
      return newCircle(func_get_arg(0));  
      case2:  
      return newRectangle(func_get_arg(0), func_get_arg(1));  
      default:  
        # code...  
        break;  
    }  
  }   
}  

$rect =FactoryShape::create(5, 5);  
// object(Rectangle)#1 (2) { ["width":"Rectangle":private]=> int(5) ["height":"Rectangle":private]=> int(5) }  
var_dump($rect);  
echo "<br>";  

// object(Circle)#2 (1) { ["radius":"Circle":private]=> int(4) }  
$circle =FactoryShape::create(4);  
var_dump($circle);  

三、觀察者設計模式

觀察者模式是挺常見的一種設計模式,使用得當會給程式帶來非常大的便利,使用得不當,會給後來人一種難以維護的想法。

使用場景:使用者登入,需要寫日誌,送積分,參與活動 等使用訊息佇列,把使用者和日誌,積分,活動之間解耦合

什麼是觀察者模式?一個物件通過提供方法允許另一個物件即觀察者 註冊自己)使本身變得可觀察。當可觀察的物件更改時,它會將訊息傳送到已註冊的觀察者。這些觀察者使用該資訊執行的操作與可觀察的物件無關。結果是物件可以相互對話,而不必瞭解原因。觀察者模式是一種事件系統,意味著這一模式允許某個類觀察另一個類的狀態,當被觀察的類狀態發生改變的時候,觀察類可以收到通知並且做出相應的動作;觀察者模式為您提供了避免元件之間緊密耦。看下面例子你就明白了!

<?php  

/* 
觀察者介面 
*/  
interface InterfaceObserver  
{  
  function onListen($sender, $args);  
  function getObserverName();  
}  

// 可被觀察者介面  
interface InterfaceObservable  
{  
  function addObserver($observer);  
  function removeObserver($observer_name);  
}  

// 觀察者抽象類  
abstract class Observer implements InterfaceObserver  
{  
  protected $observer_name;  

  function getObserverName()   
  {  
    return $this->observer_name;  
  }  

  function onListen($sender, $args)  
  {  

  }  
}  

// 可被觀察類  
abstract class Observable implements InterfaceObservable   
{  
  protected $observers = array();  

  public function addObserver($observer)   
  {  
    if ($observerinstanceofInterfaceObserver)   
    {  
      $this->observers[] = $observer;  
    }  
  }  

  public function removeObserver($observer_name)   
  {  
    foreach ($this->observersas $index => $observer)   
    {  
      if ($observer->getObserverName() === $observer_name)   
      {  
        array_splice($this->observers, $index, 1);  
        return;  
      }  
    }  
  }  
}  

// 模擬一個可以被觀察的類  
class A extends Observable   
{  
  public function addListener($listener)   
  {  
    foreach ($this->observersas $observer)   
    {  
      $observer->onListen($this, $listener);  
    }  
  }  
}  

// 模擬一個觀察者類  
class B extends Observer   
{  
  protected $observer_name = 'B';  

  public function onListen($sender, $args)   
  {  
    var_dump($sender);  
    echo "<br>";  
    var_dump($args);  
    echo "<br>";  
  }  
}  

// 模擬另外一個觀察者類  
class C extends Observer   
{  
  protected $observer_name = 'C';  

  public function onListen($sender, $args)   
  {  
    var_dump($sender);  
    echo "<br>";  
    var_dump($args);  
    echo "<br>";  
  }  
}  

$a = new A();  
// 注入觀察者  
$a->addObserver(new B());  
$a->addObserver(new C());  

// 可以看到觀察到的資訊  
$a->addListener('D');  

// 移除觀察者  
$a->removeObserver('B');  

// 列印的資訊:  
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }  
// string(1) "D"  
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }  
// string(1) "D"  

四、介面卡模式

將一個類的介面轉換成客戶希望的另一個介面,介面卡模式使得原本的由於介面不相容而不能一起工作的那些類可以一起工作。

應用場景:老程式碼介面不適應新的介面需求,或者程式碼很多很亂不便於繼續修改,或者使用第三方類庫。

例如:php連線資料庫的方法:mysql,,mysqli,pdo,可以用介面卡統一

//老的程式碼       

class User {      

    private $name;      

    function __construct($name) {      

        $this->name = $name;      

    }      

    public function getName() {      

        return $this->name;      

    }      

}     
//新程式碼,開放平臺標準介面      

interface UserInterface {      

    function getUserName();      

}      

class UserInfo implements UserInterface {      

    protected $user;      

    function __construct($user) {      

        $this->user = $user;      

    }      

    public function getUserName() {      

        return $this->user->getName();      

    }      

}     
$olduser = new User('張三');      

echo $olduser->getName()."n";      

$newuser = new UserInfo($olduser);      

echo $newuser->getUserName()."n";   

五、策略模式

將一組特定的行為和演算法封裝成類,以適應某些特定的上下文環境。

使用場景:個人理解,策略模式是依賴注入,控制反轉的基礎

例如:一個電商網站系統,針對男性女性使用者要各自跳轉到不同的商品類目,並且所有廣告位展示不同的廣告

MaleUserStrategy.php

<?php  

namespace IMooc;  
class MaleUserStrategy implements UserStrategy  {  
    function showAd()  
    {  
        echo "IPhone6";  
    }  

    function showCategory()  
    {  
        echo "電子產品";  
    }  
}   

FemaleUserStrategy.php

<?php  

namespace IMooc;  

class FemaleUserStrategy implements UserStrategy {  
    function showAd()  
    {  
        echo "2014新款女裝";  
    }  
    function showCategory()  
    {  
        echo "女裝";  
    }  
}   

UserStrategy.php

<?php  

namespace IMooc;  

interface UserStrategy {  
    function showAd();  
    function showCategory();  
}     
<?php  
interface FlyBehavior{  
    public function fly();  
}  

class FlyWithWings implements FlyBehavior{  
    public function fly(){  
        echo "Fly With Wings \n";  
    }  
}  

class FlyWithNo implements FlyBehavior{  
    public function fly(){  
        echo "Fly With No Wings \n";  
    }  
}  
class Duck{  
    private $_flyBehavior;  
    public function performFly(){  
        $this->_flyBehavior->fly();  
    }  

    public function setFlyBehavior(FlyBehavior $behavior){  
        $this->_flyBehavior = $behavior;  
    }  
}  

class RubberDuck extends Duck{  
}  
// Test Case  
$duck = new RubberDuck();  

/*  想讓鴨子用翅膀飛行 */  
$duck->setFlyBehavior(new FlyWithWings());  
$duck->performFly();              

/*  想讓鴨子不用翅膀飛行 */  
$duck->setFlyBehavior(new FlyWithNo());  
$duck->performFly();    

六、裝飾器模式

使用場景:當某一功能或方法draw,要滿足不同的功能需求時,可以使用裝飾器模式;實現方式:在方法的類中建addDecorator(新增裝飾器),beforeDraw,afterDraw 3個新方法, 後2個分別放置在要修改的方法draw首尾.然後建立不同的裝器類(其中要包含相同的,beforeDraw,afterDraw方法)能過addDecorator新增進去,然後在beforeDraw,afterDraw中迴圈處理,與觀察者模式使用有點相似

  1. 裝飾器模式(Decorator),可以動態地新增修改類的功能

  2. 一個類提供了一項功能,如果要在修改並新增額外的功能,傳統的程式設計模式,需要寫一個子類繼承它,並重新實現類的方法

  3. 使用裝飾器模式,僅需在執行時新增一個裝飾器物件即可實現,可以實現最大的靈活性

DrawDecorator.php

<?php  
namespace IMooc;  

interface DrawDecorator  
{  
    function beforeDraw();  
    function afterDraw();  
}    

Canvas.php

<?php  
namespace IMooc;  

class Canvas  
{  
    public $data;  
    protected $decorators = array();  

    //Decorator  
    function init($width = 20, $height = 10)  
    {  
        $data = array();  
        for($i = 0; $i < $height; $i++)  
        {  
            for($j = 0; $j < $width; $j++)  
            {  
                $data[$i][$j] = '*';  
            }  
        }  
        $this->data = $data;  
    }  

    function addDecorator(DrawDecorator $decorator)  
    {  
        $this->decorators[] = $decorator;  
    }  

    function beforeDraw()  
    {  
        foreach($this->decorators as $decorator)  
        {  
            $decorator->beforeDraw();  
        }  
    }  

    function afterDraw()  
    {  
        $decorators = array_reverse($this->decorators);  
        foreach($decorators as $decorator)  
        {  
            $decorator->afterDraw();  
        }  
    }  

    function draw()  
    {  
        $this->beforeDraw();  
        foreach($this->data as $line)  
        {  
            foreach($line as $char)  
            {  
                echo $char;  
            }  
            echo "<br />\n";  
        }  
        $this->afterDraw();  
    }  

    function rect($a1, $a2, $b1, $b2)  
    {  
        foreach($this->data as $k1 => $line)  
        {  
            if ($k1 < $a1 or $k1 > $a2) continue;  
            foreach($line as $k2 => $char)  
            {  
                if ($k2 < $b1 or $k2 > $b2) continue;  
                $this->data[$k1][$k2] = ' ';  
            }  
        }  
    }  
} 

ColorDrawDecorator.php

<?php  
namespace IMooc;  

class ColorDrawDecorator implements DrawDecorator  
{  
    protected $color;  
    function __construct($color = 'red')  
    {  
        $this->color = $color;  
    }  
    function beforeDraw()  
    {  
        echo "<div style='color: {$this->color};'>";  
    }  
    function afterDraw()  
    {  
        echo "</div>";  
    }  
}   

index.php

<?php  
define('BASEDIR', __DIR__);  
include BASEDIR.'/IMooc/Loader.php';  
spl_autoload_register('\\IMooc\\Loader::autoload');  

$canvas = new IMooc\Canvas();  
$canvas->init();  
$canvas->addDecorator(new \IMooc\ColorDrawDecorator('green'));  
$canvas->rect(3,6,4,12);  
$canvas->draw();    

點關注,不迷路

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是人才。之前說過,PHP方面的技術點很多,也是因為太多了,實在是寫不過來,寫過來了大家也不會看的太多,所以我這裡把它整理成了PDF和文件,如果有需要的可以

點選進入暗號: PHP+「平臺」


更多學習內容可以訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續更新)

以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業務程式碼寫多了沒有方向感,不知道該從那裡入手去提升,對此我整理了一些資料,包括但不限於:分散式架構、高可擴充套件、高效能、高併發、伺服器效能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell指令碼、Docker、微服務、Nginx等多個知識點高階進階乾貨需要的可以免費分享給大家,需要的可以加入我的 PHP技術交流群