23种设计模式之装饰者模式

一、定义

在没必要改变原类文件和使用继承的状况下,动态地扩展一个对象的功能。它是经过建立一个包装对象,也就是装饰者来包裹真实的对象。因此装饰者能够动态地给一个对象添加一个额外的职责。就增长功能来讲,装饰者模式相比生成子类更加灵活。编程

二、模式结构

装饰者模式由四部分组成:bash

  • Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可使客户端以一致的方式处理未被装饰的对象以及装饰以后的对象,实现客户端的透明操做。
  • ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰者能够给它增长额外的职责(方法)。
  • Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增长职责,可是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,经过该引用能够调用装饰以前构件对象的方法,并经过其子类扩展该方法,以达到装饰的目的。
  • ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每个具体装饰类都定义了一些新的行为,它能够调用在抽象装饰类中定义的方法,并能够增长新的方法用以扩充对象的行为。

三、实例

3.1 Coffee抽象类(抽象构件)架构

public abstract class Coffee {
    public String description;
    
    public String getDescription() {
        return description;
    }
    
    public void serDescription(String description) {
        this.description = description;
    }
    
    public abstract double cost();
}
复制代码

3.2 Coffee类(具体构件)ide

public class Mocha extends Coffee {
    
    public Mocha() {
        description = "Mocha";
    }
    
    @Override
    public double cost() {
        return 15.90;
    }
}
复制代码
public class Espresso extends Coffee {
    
    public Espresso() {
        description = "Espresso";
    }
    
    @Override
    public double cost() {
        return 12.50;
    }
}
复制代码

3.3 调料CondimentDecorator抽象类(抽象装饰类)ui

public abstract class CondimentDecorator extends Coffee {
    
    @Override
    public abstract String getDescription();
}
复制代码

3.4 调料类(具体装饰类)this

public class Milk extends CondimentDecorator {
    
    private Coffee coffee;
    
    public Milk(Coffee coffee) {
        this.coffee = coffee;
    }
    
    @Override
    public String getDescription() {
        return coffee.getDescription() + ", Mike";
    }
    
    @Override
    public double cost() {
        return coffee.cost() + 2.0;
    }
}
复制代码
public class Sugar extends CondimentDecorator {
    
    private Coffee coffee;
    
    public Sugar(Coffee coffee) {
        this.coffee = coffee;
    }
    
    @Override
    public String getDescription() {
        return coffee.getDescription() + ", Sugar";
    }
    
    @Override
    public double cost() {
        return coffee.cost() + 1.0;
    }
}
复制代码

3.5 客户端调用spa

public class Client {
    
    public static void main(String arg[]) {
        
        Coffee mocha = new Mocha();
        mocha = new Sugar(mocha);
        mocha = new Sugar(mocha);
        System.out.println(mocha.getDescription() + "$" + mocha.cost());
        
        Coffee espresso = new Espresso();
        espresso = new Milk(espresso);
        espresso = new Sugar(espresso);
        System.out.println(espresso.getDescription() + "$" + espresso.cost());
    }
}
复制代码

四、适用场景

  • 须要扩展一个类的功能,或给一个类添加附加职责。
  • 须要动态的给一个对象添加功能,这些功能能够在动态的撤销。
  • 须要增长由一些基本功能的排列组合而产生的很是大量的功能,从而使继承关系变得不现实。
  • 当不能采用生成子类的方法进行扩充时。一种状况是,能够由大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增加。另外一种状况多是由于类定义被隐藏,或类定义不能用于生成子类。

五、在Java IO流中的应用

六、优缺点

6.1 优势
  • 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会致使类的个数急剧增长。
  • 经过使用不一样的具体装饰类以及这些装饰类的排列组合,能够创造出不少不一样行为的组合。
  • 被装饰者与装饰者解耦,被装饰者能够不知道装饰者的存在,同时新增功能时原有代码也无需改变,符合“开闭原则”。
6.2 缺点
  • 装饰模式会致使设计中出现许多小类,若是过分使用,会使程序变得很复杂。
  • 装饰模式比继承更加灵活机动的特性,同时也意味着比继承更加易于出错,拍错也很困难,对于屡次装饰的对象,调试时寻找错误可能须要逐级排查,较为繁琐。
  • 装饰模式是针对抽象组件(Component)类型编程。可是,若是你要针对具体组件编程时,就应该从新思考你的应用架构,已经装饰者是否合适。

七、透明装饰模式和半透明装饰模式

7.1 透明装饰模式

在透明装饰模式中,要求客户端彻底针对抽象编程,装饰模式的透明性要求客户端程序不该该将对象声明为具体构件类型或具体装饰类型,而应该所有声明为抽象构件抽象。设计

7.2 半透明装饰模式

透明装饰模式的设计难度较大,并且有时咱们须要单独调用新增的业务方法。为了可以调用新增方法,咱们不得不用具体装饰类型来定义装饰以后的对象,而具体构件类型仍是可使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式。调试

半透明装饰模式能够给系统带来更多的灵活性,设计至关简单,使用起来也很是方便;可是其最大的缺点在于不能实现对同一个对象的屡次装饰,并且客户端须要有区别地对待装饰以前的对象和装饰以后的对象。code

特别声明:一、如若文中有错之处,欢迎大神指出。 二、文章是参考网上一些大神的文章,本身整理出来的,如如有侵权,可联系我删除。

相关文章
相关标签/搜索