PHP 設計模式之組合模式

echo_dump發表於2020-06-22

PHP設計模式之組合模式 Composite

組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的物件當作一個單一的物件。組合模式依據樹形結構來組合物件,用來表示部分以
及整體層次。這種型別的設計模式屬於結構型模式,它建立了物件組的樹形結構。

  1. 使用過 laravel-admin 這個快速後臺的同學,應該都使用過它的表單類 Form 建立表單的時候,只需要 new Form,然後新增對應的輸出的表單
    元素就可以了,很快捷,很方便,其實它就是使用了 組合模式

  2. 根據以上的例子,首先需要定義一個輸出的元素(組合元素)的介面 Renderable它裡面也之定義了一個方法 render():string型別的返回引數

interface Renderable
{
    public function render():string;
}
  1. 建立一個根元素Form類,實現介面 Renderable,在render(): string這個方法中,需要返回已經組建好的完整表單元素,在這個方法中我們稍微
    做一下改進,我們需要定義兩個成員變數,都是陣列,一個是我們事先定義好的表單元素陣列類,提供給我們最後呼叫表單方法的時候使用,還有一個提前定義
    好的空陣列,這個是我們最後元件完整表單的時候使用。最後再寫一個魔術方法,呼叫我們的具體表單元素。
class Form implements Renderable
{
    private $action = './index.php';

    private $method = 'get';

    protected $elements = [
        'text' => Text::class,
        'email' => Email::class,
        'password' => Password::class,
        'file' => File::class,
        'radio' => Radio::class,
        'number' => Number::class,
        'hidden' => Hidden::class,
    ];

    protected $items = [];

    public function addElements(string $formName, Renderable $renderable)
    {
        if (!isset($this->elements[$formName])) {
            $this->elements[$formName] = $renderable;
        }
    }

    public function setAction(string $action)
    {
        $this->action = $action;

        return $this;
    }

    public function getAction()
    {
        return $this->action;
    }

    public function setMethod(string $method)
    {
        $this->method = $method;

        return $this;
    }

    public function getMethod()
    {
        return $this->method;
    }

    public function render(): string
    {
        // TODO: Implement render() method.
        $formString = <<<ERT
<form action="{$this->getAction()}" method="{$this->getMethod()}">
ERT;

        foreach ($this->items as $item) {
            $formString .= $item;
        }

        $formString .= <<<TRE
</form>
TRE;

        return $formString;

    }

    public function pushField(string $fields)
    {
        $this->items[] = $fields;

        return $this;
    }

    public function __call($name, $arguments)
    {
        // TODO: Implement __call() method.
        if (!isset($this->elements[$name])) {
            $message = sprintf('this is method %s not exists!', $name);
            throw new RunException($message);
        }

        $class = $this->elements[$name];

        $class = new $class;

        if (is_array($arguments)) {
            $field = $arguments[0];
        } else {
            $field = (string) mt_rand(100, 999);
        }

        $formText = $class->setField($field)->render();

        $this->pushField($formText);

        return $this;
    }
}
  1. 建立一些表單元素,也需要實現介面 Renderable,在各自的render(): string方法中,返回當前建立的子表單,比如這裡列舉建立的text, email, password
class Email extends Field  implements Renderable
{

    public function render(): string
    {
        // TODO: Implement render() method.
        return <<<ERT
<div class="form-group">
    <label for="{$this->getFiled()}">{$this->getFiled()}</label>
    <input type="email" name="{$this->getFiled()}">
</div>
ERT;
    }
}

class Text extends Field implements Renderable
{
    public function render(): string
    {
        // TODO: Implement render() method.
        return <<<ERT
<div class="form-group">
    <label for="{$this->getFiled()}">{$this->getFiled()}</label>
    <input type="text" id="{$this->getFiled()}" name="{$this->getFiled()}">
</div>
ERT;
    }
}

class Password extends Field implements Renderable
{

    public function render(): string
    {
        // TODO: Implement render() method.
        return <<<ERT
<div class="form-group">
    <label for="{$this->getFiled()}">{$this->getFiled()}</label>
    <input type="password" name="{$this->getFiled()}">
</div>
ERT;
    }
}
  1. 測試,我們首先new Form()類,然後新增正常的表單元素,最後輸出到頁面上,得到一個完整的表單
require '../vendor/autoload.php';

use Composite\Form;
//http://local.test.abc/
$form = new Form();

$form->text('username');

$form->password('password');

$form->email('email');
$form->number('number');
$form->file('file');
$form->hidden('hidden');

$style = <<<ERT
<style>
.form-group {
padding-bottom: 20px;
}
</style>
ERT;

echo $style;
echo $form->render();

/**
* <form action="./index.php" method="get"><div class="form-group">
      <label for="username">username</label>
      <input type="text" id="username" name="username">
  </div><div class="form-group">
      <label for="password">password</label>
      <input type="password" name="password">
  </div><div class="form-group">
      <label for="email">email</label>
      <input type="email" name="email">
  </div><div class="form-group">
      <label for="number">number</label>
      <input type="number" name="number">
  </div><div class="form-group">
      <label for="file">file</label>
      <input type="email" name="file">
  </div><div class="form-group">
      <input type="hidden" name="hidden">
  </div></form>
 */
本作品採用《CC 協議》,轉載必須註明作者和本文連結
LIYi ---- github地址

相關文章