装饰者模式

修饰模式(装饰者模式),是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。–维基百科
装饰者UML类图(摘自维基百科):
装饰者模式

  1. Component(基类)
    它是被装饰者和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作
  2. ConcreteComponent(被装饰者)
    它是Component的子类,用于定义具体的构件对象,实现了在抽象装饰类中声明的方法,装饰类可以给它增加额外的职责
  3. Decorator(抽象装饰类)
    它也是Component的子类,用于给具体装饰者增加职责,同样是所有具体装饰者的共同的继承的抽象类,但是具体职责在其子类中实现。它维护一个指向Component对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的
  4. ConcreteDecorator(具体装饰类)
    是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰

生活中的火锅,有多种锅底,比如牛油火锅、鸳鸯火锅、酸菜火锅…,以牛油火锅为例,牛油火锅里包含多种套餐,如套餐A、套餐B,若在套餐A、B外再加羊排、鸡排等菜品需要支付套餐额外的费用,若计算吃一顿火锅的花费,以上可把火锅可以看作Component(基类),牛油火锅为ConcreteComponent(被装饰者),套餐A、B为Decorator(抽象装饰类),添加羊排、鸡排看作成ConcreteDecorator(具体装饰类),下面通过代码说明:
Component:

public interface HotPot {
    float cost();
    String desc();
}

ConcreteComponent:

public class ButterHotPot implements HotPot{
    @Override
    public float cost() {
        return 50f;
    }

    @Override
    public String desc() {
        return "";
    }
}

Decorator:

public abstract class ApackageButterHopPot implements HotPot{
    @Override
    public abstract float cost();
    public String desc(){
        return "套餐A";
    }
}

ConcreteDecorator:

public class ChickenChops extends ApackageButterHopPot {
    HotPot hotPot;

    public ChickenChops(HotPot hotPot) {
        this.hotPot = hotPot;
    }

    @Override
    public float cost() {
        return hotPot.cost() + 20f;
    }

    @Override
    public String desc() {
        return hotPot.desc()+"新增了鸡排";
    }
}

public class LambChops extends ApackageButterHopPot {
    HotPot hotPot;

    public LambChops(HotPot hotPot) {
        this.hotPot = hotPot;
    }

    @Override
    public float cost() {
        return hotPot.cost() + 50f;
    }

    @Override
    public String desc() {
        return hotPot.desc() + "新增了羊排";
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        HotPot hotPot = new ButterHotPot();
        hotPot = new LambChops(hotPot);
        hotPot = new ChickenChops(hotPot);
        System.out.println("总共消费了:"+hotPot.cost()+"元"+","+hotPot.desc());

    }
}

以上通过简单的例子说明装饰模式的应用,其实不难发现装饰者、被装饰者都继承同一基类(Component),装饰类用来修饰被装饰类,但是被装饰类一般会作为构造函数的参数被装饰类所引用,用来获取被装饰者的状态信息,用来将自身的功能加强;装饰模式非常灵活,通过不同的装饰类使得被装饰类功能增强,JAVA里的Java I/O Streams是非常经典的装饰者模式.

参考文章

  1. https://blog.****.net/a553181867/article/details/52108423