【设计模式】六:装饰模式

// 需求,给一个人穿衣服的功能,类似于qq秀这种

// 第一版,不使用面向对象特性
// 服务端
class Person
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function wearTshirts()
    {
        echo "tshirt
";
    }
    public function wearBigTrouser()
    {
        echo "大垮裤
";
    }
    public function wearSneakers()
    {
        echo "破球鞋
";
    }
    public function wearSuit()
    {
        echo "西装
";
    }
    public function wearTie()
    {
        echo "领带
";
    }
    public function wearLeatherShoes()
    {
        echo "皮鞋
";
    }
    public function show()
    {
        echo "装扮的" . $this->name;
    }
}

// 客户端
$person = new Person("小菜");
echo "第一种装绑
";
$person->wearTshirts();
$person->wearBigTrouser();
$person->wearLeatherShoes();
$person->show();

// 此时有新的需求,需要上新的皮肤,新加了超人的内裤,如果用此种方式,只能是直接修改person类,增加新的方法,但是这样违背了开放封闭原则
// 开放封闭原则是:对于扩展是开发的,对于修改是封闭的,也就是如果加新的需求,我允许你进行扩展,但是不能进行直接修改原来的类,如果修改会
// 影响原来的方法,而且不易维护

2

// 装饰模式第二版

// 这一版进行了封装,继承

class Person
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function show()
    {
        echo '装扮的' . $this->name;
    }
}

// 服装抽象类
abstract class Finery
{
    public abstract function show();
}

class Tshirt extends Finery
{
    public function show()
    {
        // TODO: Implement show() method.
        echo "rshirt 
";
    }
}

class BigTrouser extends Finery
{
    public function show()
    {
        // TODO: Implement show() method.
        echo "垮裤 
";
    }
}

class Sneakers extends Finery
{
    public function show()
    {
        // TODO: Implement show() method.
        echo "球鞋 
";
    }
}

class Suit extends Finery
{
    public function show()
    {
        // TODO: Implement show() method.
        echo "西装 
";
    }
}

$person = new Person('小菜');
$ts = new Tshirt();
$tr = new BigTrouser();
$sn = new Sneakers();
$ts->show();
$tr->show();
$sn->show();
$person->show();

// 遵循了开放封闭原则,将可能发生的变化封装起来,当接到新的需求,只需要增加新的子类就可以了,而且不会影响之前的功能
// 但此处,各个装饰的实现,都是显示的调用,我们需要将装饰的东西内部组装完成,然后再显示出来
// 我们需要把所需的功能按正确的顺序在内部串联起来进行控制,因此需要使用新的模式,【装饰模式】

3

/**
 * 装饰模式
 * 定义:动态的给一个对象添加一些额外的职能,就增加功能来说,装饰模式比生成子类更加的灵活
 * 结构:
 *  Component 定义一个对象接口,可以给这些对象动态的添加职责
 *      ConcreteComponent   定义一个具体的对象,也可以给这个对象添加一些职责
 *      Decorator           装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于component来说是无需知道decorator的存在
 *          ConcreteDecoratorA
 *          ConcreteDecoratorB  就是具体的装饰对象,起到给Component添加职责的功能
 *
 */

// 代码实现     component   基类抽象类
abstract class Component
{
    public abstract function operation();
}

// 代码实现     concreteComponent           一个具体的对象,继承自基类抽象接口
class ConcreteComponent extends Component
{
    public function operation()
    {
        // TODO: Implement operation() method.
        echo '具体对象的操作0';
    }
}

// 代码实现     Decorator       此为装饰抽象类,继承自基类抽象接口,实现基类中的方法
abstract class Decorator extends Component
{
    protected $component;
    public function setComponent(Component $component)
    {
        $this->component = $component;
    }
    public function operation()
    {
        // 重写operation方法
        if ($this->component != null) {
            // 执行父类的operation方法
            $this->component->operation();
        }
    }
}

// 代码实现 ConcreteDecoratorA      此时具体的装饰对象,继承自装饰对象基类
class ConcreteDecoratorA extends Decorator
{
    private $addedState; // 本类独有功能,区别于ConcreteDecoratorB类

    public function operation()
    {
        parent::operation(); // 先运行父类的此方法
        $this->addedState = 'new state';
        echo '具体装饰对象a的操作';
    }
}

// 代码实现 ConcreteDecoratorB      具体的装饰对象
class ConcreteDecoratorB extends Decorator
{

    public function operation()
    {
        parent::operation(); // 先运行父类的此方法
        $this->addedBehavior();
        echo '具体装饰对象b的操作';
    }

    private function addedBehavior()
    {
        echo 'aaa';
    }
}

// 客户端执行代码
$c = new ConcreteComponent();
$d1 = new ConcreteDecoratorA();
$d2 = new ConcreteDecoratorB();

// 装饰的方法是,首先用ConcreteComponent实例化对象c,然后用装饰类a的实例对象d1来包装c,再用装饰类b的实例对象d2来包装d1,最终执行d2的operation
$d1->setComponent($c);
$d2->setComponent($d1);
$d2->operation();

/**
 * 总结:个人理解
 * 1,实现装饰模式,首先定义一个抽象类接口,约定好了方法,之后的操作,都需要实现我的这个方法;
 * 2,此时我有这个抽象类了,我需要继承这个抽象类,然后实现其中约定的方法
 * 3,比如说上边我已经成功建好的一个人,那接下来就是需要对这个人进行装饰了,因为有很多种装饰办法,所以我需要有一个装饰基类
 * 4,定义一个装饰基类,这个装饰类继承自最初的那个抽象类接口,同时新增一个方法,装饰的时候,需要制定对谁进行装饰
 * 5,装饰基类也创建了,接下来就需要继承装饰基类,来实现各种各样的装饰对象了,创建了装饰对象a,装饰对象b,分别实现不同的装饰
 *
 * 上述实现中,先使用d1 (穿裤子)对c(人)进行了装饰,然后用d2(穿鞋)再对d1(已经穿好裤子的人)进行装饰
 *
 * 核心:在装饰基类中,方法 setComponent, 使用此方法类对对象进行包装,这样每个装饰对象的实现就和如何使用这个对象分开了,每个装饰对象只关心
 *      自己的功能,不需要关心如何被添加到对象链当中
 */

4

/**
 * 使用装饰者模式实现 qq秀中,给人穿衣服等功能
 * 结构:
 *  人类
 *      服饰装饰类
 *          具体的穿tshirt类
 *          具体的穿鞋类
 */

// 人基类
class Person
{
    private $name ='aa';
    public function __construct($name)
    {
        $this->name = $name;
    }

    public function show()
    {
        echo '装饰的' . $this->name;
    }
}

// 服饰类  继承自人类
class Finery extends Person
{
    protected $person;
    public function __construct()
    {

    }
    public function decorate(Person $person)
    {
        $this->person = $person;
    }
    public function show()
    {
        if ($this->person != null) {
            $this->person->show();
        }
    }
}

// 具体的tshirt类
class TShirt extends Finery
{
    public function show()
    {
        parent::show(); // TODO: Change the autogenerated stub
        echo '穿tshirt';
    }
}

// 穿垮裤类
class BigTrouser extends Finery
{
    public function show()
    {
        parent::show(); // TODO: Change the autogenerated stub
        echo '穿裤子';
    }
}

// 客户端代码
$person = new Person('小菜');
$ts = new TShirt();
$big = new BigTrouser();

// 传tshirt
$ts->decorate($person);
$big->decorate($ts);
$big->show();

/**
 * 未使用装饰模式时客户端的实现
 * $person = new Person('小菜');
 * $ts = new Tshirt();
 * $tr = new BigTrouser();
 * $sn = new Sneakers();
 * $ts->show();
 * $tr->show();
 * $sn->show();
 * $person->show();
 *
 * 相比较,使用了装饰模式后,不是显示的对一个人进行穿衣服,穿鞋;现在是先得到一个人的类,然后对人进行tshirt装饰,再对穿好tshirt的人进行鞋的装饰
 */

/**
 * 总结:
 *  装饰模式是为已有功能动态的添加更多功能呢的一种方式
 *      当需要新功能的时候,是向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,但问题就在于,在向原有类中加了新的字段,方法等后
 *  使的原有的类变的复杂了,而这些新加入的东西仅仅是为了满足一些只在某种特定的情况下才会执行的特殊行为的需要
 *      此时装饰模式就是很好的解决方案,把每个要装饰的功能放在单独的类中,并让这个类包装他所需要装饰的对象,因此当需要执行特殊行为时,客户代码就可以在运行时
 *  根据需要有选择的,按顺序的使用装饰功能包装对象
 *
 *      装饰模式的优点:把类中的装饰功能从类中移除,这样简化了原有的类
 *      这样做的更大好处就是有效的把类的核心职责和装饰功能分开了,而且可以去除相关类中重复的装饰逻辑
 *      装饰模式的装饰顺序很重要,比如加密数据和过滤敏感词汇,如果先加密了数据,然后进行敏感词汇过滤,那可能就出现问题,最理想的情况是保证装饰类之间彼此
 *  独立,这样就可以使用任意顺序进行组合了
 */
原文地址:https://www.cnblogs.com/rcltocode/p/10615483.html