天天看点

浅学设计模式之装饰模式 (2/23)

1、装饰模式的概念

装饰模式就是动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。

对于人来说,衣服、鞋子、领带、袜子等都是装饰,装饰模式就是给光着身子的人穿上各种衣服裤子。

对于程序对象来说,每一个功能都是装饰,给一个类增加功能,就是给这个类进行装饰。

2、装饰模式的UML图及对应代码

浅学设计模式之装饰模式 (2/23)

Compoent是一个对象接口,可以给这些对象动态地添加职责:

class interface Component{
    void Operation();
}      

ConcreteComponent类,定义了一个具体的对象,也可以给这个对象添加一些职责:

class ConcreteComponent implement Component{
   
   @Override
   public void Operation(){
       //做一些具体的操作
   } 
}      

Decorator 是装饰抽象类,继承了Component接口,从外类来扩展Component类的功能,但是对于Component来说,是无需要知道Decorator的存在的。

public class Decorator implements Component{
    protected Component component;
    
    //设置Component
    public SetComponent(Component component){
        this.component = component;
    }

    //重写Operation(),实际执行的是Component的Operation()
    @Override
    public void Operation() {
        if(component != null){
           component.Operation();
        }
    }   
}      

ConcreteDecorator 是具体的装饰对象,起到给Component添加职责的功能

我们来实现一下A和B

public class ConcreteDecoratorA extends Decorator {
    
    //本类特有的功能,以区别B
    private String addedState;
    
    @Override
    public void Operation() {
      // 首先运行原Component的Operation()
   super.sampleOperation();
     // 再执行本类的功能,如addState相当于对原Component进行了装饰
     addState = "New State";
     //具体装饰对象A的操作。
    }
}

public class ConcreteDecoratorB extends Decorator {
    
    //本类特有的方法,以区别A
    public AddBehavior() {
    }
    
    @Override
    public void Operation() {
      // 首先运行原Component的Operation()
   super.sampleOperation();
     // 再运行自己的功能
     AddBehavior();
     ....
    }
}      

最后客户端代码点睛之笔:

main(){
            ConcreteComponent c = new ConcreteComponent();
            ConcreteDecoratorA d1 = new  ConcreteDecoratorA();
            ConcreteDecoratorB d2 = new  ConcreteDecoratorB();
            
            //装饰的方法是:
            //首先用 ConcreteComponent实例化c
            //然后用 ConcreteDecoratorA的实例化对象d1来包装c
            //再用ConcreteDecoratorB的对象d2来包装d1
            //最后执行d2的Operation
            d1.setComponent(c);
            d2.setComponent(d1);
            d2.Operation();
            ...
       }      
  • 装饰模式就是利用SetComponent来对对象进行包装的。

    这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象值关心自己的功能,不需要关心如何被添加到对象链当中。

注:不一定要去构造Component类,要善于变通,假如只有一个ConcreteComponent(我们只修饰一个“人”),那么久没有必要些Component类,同时,Decorator就是ConcreteComponent类的子类,如果ConcreteDecorator只有一个(“衣服”只有一件),那么也没有必要构造Decorator,直接让唯一的ConcreteDecorator来继承ConcreteComponent就行啦。

3、经典场景—穿衣服

在这个场景下,我们给一个人穿各种衣服,包括裤子、袜子、鞋子、衣服。

在之前的UML来说,ConcreteComponent可以说是代表着具体的一类人,而这个问题让我们针对这个人,所以我们可以不用构造Component类,或者说 “人”这个类就是Component类。而Decorator很自然就是“服装类”,其子类就是各种衣服,那么我们可以画出下面的UML图:

浅学设计模式之装饰模式 (2/23)

接着按图来写代码:

首先是Person类

class Person{
     public Person(){
     }
     private String name;
     public Person(String name){
     this.name = name;
     }
     public abstract void show(){
     System.out.plintln("穿衣服的rikka");
     }
}      

接着是服饰类Finery:

class Finery extends Person{
     protected Person component;
     //打扮
     public void Decorate(Person component){
     this.component = component;
     }
     
     @Override
     public void show(){
     if(component != null){
        component.show();
       }
     }
}      

接下来就是具体服饰类ConcreteDecorator

class GoldLianzi extends Finery{
     @Override
     public void show(){
       System.out.prinlin("大金链子");
       super.show();
     }
}
class Sunglasses extends Finery{
     @Override
     public void show(){
       System.out.prinlin("墨镜");
       super.show();
     }
}      

最后的客户端代码:

main(){
     Person rikka = new Person("Rikka");
     
     System.out.println("装扮\n");
     
     Sunglasses sunglasses = new Sunglasses();
     GoldLianzi gold = new GoldLianzi();
     Cigarette cigarette = new Cigarette();
     ThugLife thuglife = new ThugLife();
       
     sunglasses.Decorate(rikka);
     gold.Decorate(sunglasses);
     cigarette.Decorate(gold);
     thuglife.Decorate(cigarette);
     thuglife.show();
}      

结果显示:

装扮
ThugLife帽子 香烟 大金链子 墨镜 穿衣服的rikka      

这样,我们就为一个什么都没有穿的人,穿上了各种衣服啦!

同理就是给类加功能。

总结

学了这么多,其实装饰模式是为已有功能动态地添加更多功能的一种设计模式。

问题:在以往,在系统功能需要更新的时候,是向旧类添加新的代码,这些新的代码通常装饰了原有类的核心职责或主要行为,但这种做法的问题就在于:它们在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度。