大话设计模式--第六章 装饰模式

时间:2022-07-25
本文章向大家介绍大话设计模式--第六章 装饰模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

今天又学习了一个设计模式---装饰模式

我们来看看装饰模式是如何一步一步引出的. 看看下面这个需求: 给一个人搭配衣服.

  怎么搭配呢? 可以是嘻哈服, 也可以是西装, 也可以是运动装. 就像qq或者一些服饰搭配软件里那样, 给人进行不同的服饰搭配. 展现不同风格。下面来分析一下:

  • 搭配的对象是人, 人就是主体对象
  • 搭配的服饰: 有衣服,裤子, 领带, 帽子, 鞋, 大衣等等. 这些衣服可以随意搭配. 不同的服饰搭配出来不同的风格.

如何用代码实现?

第一步: 一气呵成

public class Person {

    private String name;
    
    public void wearTshirts(){
        System.out.println("穿T恤");
    }
    
    public void wearBigTrouser(){
        System.out.println("穿垮裤");
    }
    
    public void wearSneakers(){
        System.out.println("穿破球鞋");
    }
    
    public void wearSuit(){
        System.out.println("穿西装");
    }
    
    public void wearTie(){
        System.out.println("系领带");
    }
    
    public void wearLeatherShoes(){
        System.out.println("穿皮鞋");
    }

    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }
    
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小菜");
        
        //第一种服饰搭配
        person.wearSuit();
        person.wearTie();
        person.wearLeatherShoes();
        
        //第二种服饰搭配
        person.wearBigTrouser();
        person.wearTshirts();
        person.wearSneakers();
    }
}
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小菜");
        
        //第一种服饰搭配
        System.out.println("第一种服饰搭配");
        person.wearSuit();
        person.wearTie();
        person.wearLeatherShoes();
        
        //第二种服饰搭配
        System.out.println("nn第二种服饰搭配");
        person.wearBigTrouser();
        person.wearTshirts();
        person.wearSneakers();
    }

这里将人这个对象和服饰搭配的内容, 写在一个类里面了 . 很显然, 不好维护, 也不好扩展. 无论干什么都需要修改这个类, 也不符合开放-封闭原则.

-------------------------------------------------------------------

从开放-封闭原则来考虑, 封装变化点. 将变化点抽象出来的特点. 做一下修改.

代码如下:

/**
 * 既然第一步是一个失败的设计: 那么如何优化呢?
 * 首先. 根据封闭开放原则. 抽象变化点. 各种服饰是变化点. 所以将其抽象出来. 
 *         
 * @author samsung
 *
 */
public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void show(){
        System.out.println("装扮的" + this.getName());
    }
    
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小菜");
        
        Clothes tShirts = new TShirts();
        Clothes bigTrouser = new BigTrouser();
        Clothes sneakers = new Sneakers();
        tShirts.show();
        bigTrouser.show();
        sneakers.show();
        person.show();
        
        
        Clothes suit = new Suit();
        Clothes tie = new Tie();
        Clothes leatherShoes = new LeatherShoes();
        
        suit.show();
        tie.show();
        leatherShoes.show();
        person.show();
        
    }
}
package com.designModel.chapter6_decorateModel.step2;

/**
 * 服饰
 *
 */
public abstract class Clothes {

    public abstract void show();
}
package com.designModel.chapter6_decorateModel.step2;

public class BigTrouser extends Clothes {

    public void show() {
        System.out.println("垮裤");
    }
}
package com.designModel.chapter6_decorateModel.step2;

public class LeatherShoes extends Clothes {

    public void show() {
        System.out.println("皮鞋");
    }
}
package com.designModel.chapter6_decorateModel.step2;

public class Sneakers extends Clothes {

    public void show() {
        System.out.println("破球鞋");
    }
}
package com.designModel.chapter6_decorateModel.step2;

public class Suit extends Clothes {

    public void show() {
        System.out.println("西装");
    }
}
package com.designModel.chapter6_decorateModel.step2;

public class Tie extends Clothes {

    public void show() {
        System.out.println("领带");
    }
}
package com.designModel.chapter6_decorateModel.step2;

public class TShirts extends Clothes {

    public void show() {
        System.out.println("大T恤");
    }
}

调用方法

public static void main(String[] args) {
        Person person = new Person();
        person.setName("小菜");
        
        Clothes tShirts = new TShirts();
        Clothes bigTrouser = new BigTrouser();
        Clothes sneakers = new Sneakers();
        tShirts.show();
        bigTrouser.show();
        sneakers.show();
        person.show();
        
        
        Clothes suit = new Suit();
        Clothes tie = new Tie();
        Clothes leatherShoes = new LeatherShoes();
        
        suit.show();
        tie.show();
        leatherShoes.show();
        person.show();
        
    }

下面来分析分析这种方式:

现在如果有一个新的需求, 想要要增加一种运动装, 怎么办呢? 很显然, 增加一个类. 比之前要好得多. 起码不用修改原来的类了. 尽管如此, 这样还是有问题. 看看这段代码   tShirts.show();   bigTrouser.show();   sneakers.show();   person.show(); 这样写, 意味着什么?:   这就是将"大T恤", "垮裤", "破球鞋"和"装扮的小菜"一个词一个词的显示出来. 就好比: 你光着身子, 当着大家的面, 先穿T恤, 再传裤子, 在穿鞋, 仿佛在跳穿衣舞. 难道你穿衣服实在众目睽睽之下穿的么?   问题就在这里: 我们应该在内部组装完成, 然后在显示出来. 你内部怎么穿, 是先穿西装, 再穿T恤, 再加风衣, 大山领带, 皮鞋外面再穿球鞋;   当然也完全可以只穿一条裤衩就完成. 也就是说, 通过服饰组合出一个有个性的人完全可以有无数种方案. 并非是固定的.   这就用到了我们的一个设计模式: 装饰器设计模式.   动态的给一个对象添加一些额外的职责, 就增加功能来说, 装饰模式比生产子类更加灵活的多.

-----------------------------------------------------------------------------

装饰器设计模式

什么是装饰器设计模式?   动态的给一个对象添加一些额外的职责, 就增加功能来说, 装饰模式比生产子类更加灵活的多.

下面来看看他的结构

1. Component 是定义一个对象接口, 可以给这些对象动态的添加职责. 2. ConcreteComponent是定义了一个具体的对象, 也可以给这个对象添加一些职责. 3. Decorator: 装饰抽象类, 继承了Component, 从外类来扩展Componennt的功能.但从Component来说, 无需知道Decorator的存在. 4. ConcreteComponent: 就是具体的装饰对象, 起到给Component添加职责的功能.

下面来看看这个装饰类的代码实现:

Component类

package com.designModel.chapter6_decorateModel.step3;

public abstract class Component {
    abstract void operation();
}

ConcreteComponent类

package com.designModel.chapter6_decorateModel.step3;


public class ConcreteComponent extends Component {
    @Override
    void operation() {
        System.out.println("定义了一个具体对象的操作");
    }
}

Decorator类

package com.designModel.chapter6_decorateModel.step3;

public abstract class Decorator extends Component {
    
    protected Component component;
    public void setComponent(Component component){
        this.component = component;
    }
    
    @Override
    public void operation(){
        if(component != null ){
            component.operation();
        }
    }
}

ConcreteDecoratorA类

package com.designModel.chapter6_decorateModel.step3;

public class ConcreteDecoratorA extends Decorator {

    private String addedState;
    
    public void operation() {
        //首先运行原Component的Operation()方法, 在执行本类的功能, 如addedState, 相当于对原Component进行了装饰.
        super.operation();
        this.setAddedState("new State");
        System.out.println("具体装饰对象A的操作");
    }
    
    public String getAddedState() {
        return addedState;
    }
    public void setAddedState(String addedState) {
        this.addedState = addedState;
    }

}

ConcreteDecoratorB类

package com.designModel.chapter6_decorateModel.step3;

public class ConcreteDecoratorB extends Decorator {
    
    @Override
    public void operation() {
        super.operation();
        //首先运行原Component的Operation()方法, 在执行本类的功能, 如addBehavior(),相当于对原Component进行了装饰.
        addedBehavior();
        System.out.println("具体装饰对象B的操作");
        
    }
    
    //本类独有的方法, 区别于ConcreteDecoratorA
    private void addedBehavior() {
        
    }
}

客户端代码

   public static void main(String[] args) {
        ConcreteComponent cc = new ConcreteComponent();
        ConcreteDecoratorA cda = new ConcreteDecoratorA();
        ConcreteDecoratorB cdb = new ConcreteDecoratorB();
        
        cda.setComponent(cc);
        cdb.setComponent(cda);
        cdb.operation();
    }    

装饰模式就是利用setComponent来对对象进行包装的。这样每个装饰对象的实现和如何使用这个对象分离开了。 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中。

接下来, 我们就将上面的服饰搭配写成装饰器模式。

---------------------------------------------------------------------------------

疑问:理解了上面的设计模式结构图, 有一个Component类, 有一个具体的Component实现类ConcreteComponent,还有一个装饰器类Decorator,以及装饰器的具体实现ConcreteDecoratorA,ConcreteDecoratorB。

那么, 我们的Person类到底是Component类还是ConcreteComponent类呢? Person是具体的对象, 所以他应该是ConcreteComponent类。那么就没有Component类。

学习了设计模式, 我们要会变通. 如果只有一个ConcreteComponent类而没有Component类,那么Decorator就可以直接继承ConcreteComponent类,同样的道理, 如果只有一个ConcreteDecorator, 那么就没有必要建立一个单独的Decorator类, 可以把Decorator和ConcreteDecorator的责任合并为一个类.

服饰搭配的装饰结构图如下:

代码如下:

Person 类

package com.designModel.chapter6_decorateModel.step4;

/**
 * 对于刚才的那个例子, "人"类是Component还是ConcreteComponent呢? 其实"人"类是具体的对象. 是ConcreteComponent.
 * 如果只有一个ConcreteComponent类而没有抽象的Component类, 那么Decorator类可以是ConcreteComponent类的子类。
 * 同样道理,如果只有一个ConcreteDecreator类, 那么久没有必要建立一个段杜的Decorator类, 而把Decorator和ConcreteDecorator
 * 的责任合并成一个类。 
 */
public class Person {
    private String name;

    public void show(){
        System.out.println("装扮的是"+this.name);
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

ClothesDecorator类

package com.designModel.chapter6_decorateModel.step4;

/**
 * 服装类装饰器
 * @author samsung
 *
 */
public abstract class ClothesDecorator extends Person{
    Person component;
    
    public void setComponent(Person component){
        this.component = component;
    }
    
    @Override
    public void show() {
        component.show();
    }
    
    
    
}

BigTrouser类

package com.designModel.chapter6_decorateModel.step4;

public class BigTrouser extends ClothesDecorator {
    @Override
    public void show() {
        super.show();
        System.out.println("垮裤");
    }
}

LeatherShoes类

package com.designModel.chapter6_decorateModel.step4;

public class LeatherShoes extends ClothesDecorator {
    @Override
    public void show() {
        super.show();
        System.out.println("皮鞋");
    }
}

Sneakers类

package com.designModel.chapter6_decorateModel.step4;

public class Sneakers extends ClothesDecorator{
    @Override
    public void show() {
        super.show();
        System.out.println("破洞鞋");
    }
}

Suit类

package com.designModel.chapter6_decorateModel.step4;

public class Suit extends ClothesDecorator {
    @Override
    public void show() {
        super.show();
        System.out.println("西装");
    }
}

Tie类

package com.designModel.chapter6_decorateModel.step4;

public class Tie extends ClothesDecorator {
    @Override
    public void show() {
        super.show();
        System.out.println("领带");
    }
}

TShirts类

package com.designModel.chapter6_decorateModel.step4;

public class TShirts extends ClothesDecorator {
    
    @Override
    public void show() {
        super.show();
        System.out.println("T恤");
    }
    
}

客户端实现:

public static void main(String[] args) {
        Person person = new Person();
        person.setName("小菜");
        
        System.out.println("第一种装扮");
        ClothesDecorator tShirt = new TShirts();
        ClothesDecorator bigTrouser = new BigTrouser();
        ClothesDecorator sneakers = new Sneakers();
        
        tShirt.setComponent(person);
        bigTrouser.setComponent(tShirt);
        sneakers.setComponent(bigTrouser);
        sneakers.show();
        
        System.out.println("第二种装扮");
        LeatherShoes ls = new LeatherShoes();
        Suit su = new Suit();
        Tie tie = new Tie();
        ls.setComponent(person);
        su.setComponent(ls);
        tie.setComponent(su);
        tie.show();
    }

运行结果:

第一种装扮
装扮的是小菜
T恤
垮裤
破洞鞋

第二种装扮
装扮的是小菜
皮鞋
西装
领带

总结:

装饰模式是为已有功能动态的添加更多功能的一种方式.

那么什么时候用它呢?

当系统需要新功能的时候, 是向旧的类中添加新的代码。这些新家的代码通常装饰了原有类的核心职责或主要行为,比如用西装或者嘻哈服来装饰小菜。但这种做法的问题在于,他们在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,就像期初的那个“人”类,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需求.而装饰模式提供了一个很好的解决方案,他把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象, 因此,需要执行特殊行为时, 客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象。所以,就出现了上面的情况, 我可以通过装饰, 让你全副武装到牙齿,也可以让你只挂一丝到内裤。

装饰模式的优点:

  把类中的装饰功能从类中搬移出去,这样可以简化原有的类。更大的好处是,把类的核心职责和装饰功能区分开。而且可以去除相关类中重复的装饰逻辑。