PHP Design Patterns (4) -- Decorator Patterns

My design pattern series articles
php design pattern (1) -- observer pattern -- spl standard writing
php design pattern (2) -- observer pattern -- using trait to improve writing
php design pattern (3) -- responsibility Chain (Chain of Responsibility) Pattern
PHP Design Pattern (4) -- Decorator pattern

analysis of

online clichés will not be said.

The picture comes from the Red and Black Alliance:


In the above picture, the Component corresponds to my Display
ConcreteComponet here, and the BasicDisplay
Decorator corresponds to my Border.
The remaining two correspond to the FullBorder and SiderBorder

decorators. Applicable occasions: Assuming that an object has a certain function, In some cases, this function needs to be enhanced, but in some cases, the original function needs to be used. Use decorators in this case.

Both decorators and chains of responsibility will have a long chain of object associations. The difference is that the chain of responsibility emphasizes different processing of the same message, rather than functional enhancement. Chain of responsibility is multiple functions , and decorator is one function, but this function sometimes needs some small changes, and it is the same function after changes .

Java's stream uses decorators, indicating that decorators can also change the representation of the result. In short, functional enhancement is a general thing, and it can do a lot of things.

Note: Like the observer and the chain of responsibility, the specific relationship between the class and the class, the sequence, etc., are all placed in the client code!

Now we construct the requirement.

Suppose, we have a document that needs to be printed by the printer, but we want to print some borders to make the document more beautiful, and the border has nothing to do with the content of the document. But the same function: print. So we use decorators.

Code

<?php
/**
 * Decorator pattern learning code.
 *
 * is at the top level and represents the functionality of the entire design pattern example: printing a string
 *
 * This program can also wrap multi-line text, but the code is a little more complicated, which is not conducive to seeing the design pattern clearly.
 */
abstract class Display
{
    public abstract function getColumns(); //Get the horizontal word count and delegate the responsibility to subclasses, so abstract, the same below
    //Observing the subclass, we can see that as long as one class is used,
    //Require all classes to have this method!

    public abstract function getRows(); //Get the number of vertical rows and delegate the responsibility to the subclass
    public abstract function getRowText($row);//Get the string of row row

    public function show() { //Because the implementation of this method is fixed, write it here
        for ($i = 0; $i < $this->getRows(); $i++) {
            echo $this->getRowText($i) . PHP_EOL;
        }
        echo PHP_EOL;
    }
}

/**
 * Note that this class must be wrapped in the core, unlike other classes, although they all inherit the Display class
 * So I named it basic
 */
class BasicDisplay extends Display
{
    private $string; //The innermost string that must be printed

    public function __construct($string) { //It needs to be specified externally
        $this->string = $string;
    }

    public function getColumns() { //Attention! , called only by a certain class, but written to every class! !
        return strlen($this->string);
    }

    public function getRows() { //The core only prints one row
        return 1;
    }

    public function getRowText($row) { //Return only when row is 0
        if ($row == 0) {
            return $this->string;
        } else {
            return null;
        }
    }

}


/**
 * Because there are many kinds of outer frames, the commonality is extracted to form this abstract class, among which,
 * Also determines the constructors and properties that each decorator subclass has, usually objects belonging to a common interface
 */
abstract class Border extends Display
{
    protected $display; //Note: It is an object of the same interface, php
    //Not like java can express types, but actually yes

    protected function __construct(Display $display) { //As you can see later, subclasses can actually extend the constructor
        $this->display = $display;
    }
}

/**
 * The outline class that outputs specific characters (specified by outside the program) on both sides of the characters,
 * Indirectly inherit Display through Border
 *
 */
class SideBorder extends Border
{
    //The characters used for decoration will be written to both sides
    private $borderChar;                              

    public function __construct(Display $display, $ch) {//Note that the constructor is overridden.
        parent::__construct($display);
        $this->borderChar = $ch;
    }

    public function getColumns() {// Add one character to the left and right, so add 2 to the width
        return 1+ $this->display->getColumns() + 1;
    }

    public function getRows() {
        return $this->display->getRows();
    }

    /**
     * The final display effect is like |hello, world|
     * The | on both sides of it is just an example, it is passed in from the outside.
     * Depending on the type of php, there is no character class, so make sure to pass in only one character. There is no judgment here, and exceptions can also be thrown.
     */
    public function getRowText($row) { // Note that this is actually in a loop, just doing the same thing for each row.
        return $this->borderChar . $this->display->getRowText($row) . $this->borderChar;
    }
}

/**
 * The frame class that wraps the character in it
 * Indirectly inherit Display through Border
 *
 */
class FullBorder extends Border
{
    private $borderChar;

    public function __construct(Display $display) {
        parent::__construct($display);
    }

    //These methods are very important to ensure the upper and lower character alignment (assuming the character width is equal)
    //Note: Although this method of other classes does not seem to be used,
    //Actually used here, so that this class can know the character width of the kernel inside
    public function getColumns() {
        return 1 + $this->display->getColumns() + 1;
    }

    public function getRows() {
        return 1 + $this->display->getRows() + 1;
    }

    /**
     * After determining the number of rows as the core content plus 2, see getRows above, you can output decorations at the top and bottom
     * +-------------------+
     * +-------------------+
     *
     * Then, print | characters on both sides of the content
     */
    public function getRowText($row) {
        if ($row == 0) { // row 1
            return '+' . $this->makeLine('-', $this->display->getColumns()) . '+';
        } elseif ($row == $this->display->getRows() + 1) { // The last row, = the original total number of rows + 1, because the number of rows is counted from 1, and the row counts from 0.
            return '+' . $this->makeLine('-', $this->display->getColumns()) . '+';
        } else {
            return '|' . $this->display->getRowText($row - 1) . '|';//-1 is because there is a misplacement and an extra line
        }
    }

    private function makeLine($ch, $count)  {
        $s = '';
        for ($i = 0; $i < $count; $i++) {
            $s .= $ch;
        }
        return $s;
    }

}

// print "Hello, world" without any decorations
$b1 = new BasicDisplay('Hello, world.');
$b1->show();

//Add the decorative character '#' to the left and right sides of b1
$b2 = new SideBorder($b1, '#');
$b2->show();

//Add b2 to the decorative frame
$b3 = new FullBorder($b2);
$b3->show();

//b4 adds multiple frames to the outside of the core, please carefully observe the corresponding relationship between the graphics and each decorator, it is very interesting.
$b4 = new SideBorder(
          new FullBorder (
              new FullBorder (
                  new SideBorder(
                      new FullBorder (
                          new BasicDisplay('Hello, world.')
                      ), '*'
                  )
              )
          ), '/'
      );
$b4->show();


The results show, very delicate
Hello, world.

#Hello, world.#

+---------------+
|#Hello, world.#|
+---------------+

/+-------------------+/
/|+-----------------+|/
/||*+-------------+*||/
/||*|Hello, world.|*||/
/||*+-------------+*||/
/|+-----------------+|/
/+-------------------+/



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326529276&siteId=291194637