:::

2. 策略模式Stragety Pattern

  1. UML圖:

  1. 例子:動態產生表單
    1. 客戶端實作Form物件,將原本Switch Case...改寫成另一個類別或介面(FormElement),而各個Case寫成各自一類繼承FormElement
<!-- 客戶端 -->
<!DOCTYPE html>
<html lang="zh-TW">
  <head>
    <meta charset="utf-8">
    <title>動態表單</title>
  </head>
  <body>
    <?php
    $form = new Form();
    $form->addElement(new FormElement_Text('name', '李佳玲'));
    $form->display();
    ?>
  </body>
</html>
  1. 定義類別Form
    /**
     *
     * 環境類(Context):用一個ConcreteStrategy對象來配置。維護一個對Strategy對象的引用。可定義一個接口來讓Strategy訪問它的數據。
     * 算法解決類,以提供客戶選擇使用何種解決方案:
     */
    // 改寫原來的Form類別,拿掉add<Type>等方法,改用addElement方法動態表單
    class Form
    {
      protected $_elements = array();
      public function addElement(FormElement $element)
      {
        $this->_elements[$element->getName()] = $element;
      }
      //原來的 displayElement的switch拿掉,直接使用FormElement的output方法
      public function displayElement($name) {
        echo $this->_elements[$name]->output();
      }
      public function display() {
        echo '<form action="" method="post">', "\n";
        foreach ($this->_elements as $name => $element) {
         echo $element->output(), "<br />\n";
        }
         echo '</form>', "\n";
      }
    }
  2. 定義類別FormElement
    /**
    * 策略模式
    * 定義一系列的算法,把每一個算法封裝起來, 並且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化
    *
    */
    //定義一個抽象的 FormElement  類別,其中 output方法負責輸出動態表單(策略類別)
    abstract class FormElement
    {
      protected $_name = null;
      protected $_value = null;
      public function __construct($name, $value)
      {
        $this->_name = $name;
        $this->_value = $value;
      }
      public function getName()
      {
        return $this->_name;
      }
      abstract public function output();
    
    }
    
    //處理文字輸入欄位的類別,繼承FormElement,並覆寫 output方法,輸出input標籤動態表單
    class FormElement_Text extends FormElement
    {
      public function output()
      {
        return '<input type="text" name="'. $this->_name .'" value="' .htmlspecialchars($this->_value) . '" />';
      }
    }
    //圖片類別也是繼承FormElement類別,並覆寫output方法,輸出img標籤動態表單
    class FormElement_Image extends FormElement
    {
      public function output()
      {
        return '<img src="'. $this->_value .'" alt="" />';
      }
    }
    //送出按鈕類別也是繼承FormElement類別,並覆寫output方法,輸出button標籤動態表單
    class FormElement_Submit extends FormElement
    {
      public function output()
      {
        return '<button type="submit">' . htmlspecialchars($this->_value) .'</button>';
      }
    }
    //大量文字框也是繼承FormElement類別,並覆寫output方法,輸出textarea標籤動態表單
    class FormElement_textarea extends FormElement
    {
      protected $_rows;
      public function set_rows($rows){
        $this->_rows=$rows;
      }
      public function output()
      {
        return '<textarea type="textarea" rows="'.$this->_rows.'"  name="'. $this->_name .'">'.htmlspecialchars($this->_value) .' </textarea>';
      }
    }
    //下拉式選單也是繼承FormElement類別,並覆寫output方法,輸出select標籤動態表單
    class FormElement_select extends FormElement
    {
      private $_itemarr= array();
      public function set_itemgroup(Array $itemarr){
        $this->_itemarr=$itemarr;
      }
      public function output()
      {
        $html='<select name="'. $this->_name .'" >';
        foreach($this->_itemarr as $key => $arr){
          $select=($this->_value==$key)?'selected':'';
          $html.='<option value="'.$key.'" '.$select.' >'.$arr.'</option>';
        }
        $html.='</select>';
        return $html;
      }
    }
    //單選框也是繼承FormElement類別,並覆寫output方法,輸出radio標籤動態表單
    class FormElement_radio extends FormElement
    {
      private $_itemarr= array();
      public function set_itemgroup(Array $itemarr){
        $this->_itemarr=$itemarr;
      }
      public function output()
      {
        $html="";
        foreach($this->_itemarr as $key => $arr){
          $checked=($this->_value==$key)?'checked':'';
          $html.='<label class="radio"><input type="radio" name="'. $this->_name .'" id="'. $this->_name .'" value="'. $key .'" '.$checked.'>'.$arr.'</label>';
        }
        return $html;
      }
    }

 

  1. 在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。在策略模式中,我們創建表示各種策略的對象和一個行為隨著策略對象改變而改變的context 對象。策略對象改變context 對象的執行算法。

  2. 介紹:

    1. 意圖:定義一系列的算法,把它們一個個封裝起來, 並且使它們可相互替換。

    2. 主要解決:在有多種算法相似的情況下,使用if...else 所帶來的複雜和難以維護。

    3. 何時使用:一個系統有許多許多類,而區分它們的只是他們直接的行為。

    4. 如何解決:將這些算法封裝成一個一個的類,任意地替換。

    5. 關鍵代碼:實現同一個接口。

    6. 優點:(1)算法可以自由切換。 (2)避免使用多重條件判斷。(3)擴展性良好。

    7. 缺點:(1)策略類會增多。(2)所有策略類都需要對外暴露。

    8. 使用場景:(1)如果在一個系統裡面有許多類,它們之間的區別僅在於它們的行為,那麼使用策略模式可以動態地讓一個對像在許多行為中選擇一種行為。(2)一個系統需要動態地在幾種算法中選擇一種。(3)如果一個對像有很多的行為,如果不用恰當的模式,這些行為就只好使用多重的條件選擇語句來實現。

    9. 注意事項:如果一個系統的策略多於四個,就需要考慮使用混合模式,解決策略類膨脹的問題。