装饰者模式

定义

装饰者(Decorator)模式是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

装饰者模式可以动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

装饰者模式属于对象结构型模式。


要点

  1. 装饰者和被装饰对象有相同的超类型。
  2. 可以用一个或多个装饰者包装一个对象。
  3. 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,
    可以用装饰过的对象代替它。
  4. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
  5. 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰
    对象。

装饰模式主要包含以下角色:
抽象构件(Component):定义一个抽象接口以规范准备接收附加责任的对象
具体构件(Concrete Component):实现抽象构件,通过装饰角色为其添加一些职责
抽象装饰(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
具体装饰(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任

装饰者模式

场景

咖啡店需要设计订单系统,购买咖啡时,也可以要求在其中加入各种调料,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。要根据所加入的调料收取不同的费用,订单系统必须考虑到这些调料部分。

实现

Beverage

/**
 * Beverage是一个接口,相当于抽象的Component
 * 有两个方法,获取饮料产品的描述和价格
 */
public interface Beverage {

    /**
     * 获取饮料的描述
     */
    String getDescription();

    /**
     * 获取饮料的价格
     */
    Double cost();

}

Espresso

/**
 * 浓缩咖啡,相当于具体组件
 */
public class Espresso implements Beverage {
    @Override
    public String getDescription() {
        return "Espresso Coffee";
    }

    @Override
    public Double cost() {
        return 1.99;
    }
}

HouseBlend

/**
 * 家常咖啡,相当于具体组件
 */
public class HouseBlend implements Beverage {
    @Override
    public String getDescription() {
        return "House Blend Coffee";
    }

    @Override
    public Double cost() {
        return 0.89;
    }
}

BaseCondimentDecorator

/**
 * 调料装饰器(抽象)
 */
public abstract class BaseCondimentDecorator implements Beverage {

    /**
     * 使用组合的方式,拥有具体饮料的引用
     */
    protected Beverage beverage;

    protected BaseCondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public abstract String getDescription();

    @Override
    public abstract Double cost();
}

MochaDecorator

/**
 * 摩卡配料,让它扩展BaseCondimentDecorator。
 */
public class MochaDecorator extends BaseCondimentDecorator {

    public MochaDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public Double cost() {
        return beverage.cost() + 0.20;
    }
}

SoyDecorator

/**
 * 豆浆配料,让它扩展BaseCondimentDecorator。
 */
public class SoyDecorator extends BaseCondimentDecorator {

    public SoyDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Soy";
    }

    @Override
    public Double cost() {
        return beverage.cost() + 0.15;
    }
}

WhipDecorator

/**
 * 奶泡配料,让它扩展BaseCondimentDecorator。
 */
public class WhipDecorator extends BaseCondimentDecorator {

    public WhipDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }

    @Override
    public Double cost() {
        return beverage.cost() + 0.10;
    }
}

Test

public class Test {

    public static void main(String[] args) {
        // 订一杯Espresso,不需要调料,打印出它的描述与价格
        Beverage espresso = new Espresso();
        System.out.println("饮料种类: " + espresso.getDescription() + ", 价格: " + espresso.cost());
        // 再来一杯调料为双份摩卡再加奶泡的Espresso咖啡
        espresso = new MochaDecorator(espresso);
        espresso = new MochaDecorator(espresso);
        espresso = new WhipDecorator(espresso);
        System.out.println("饮料种类: " + espresso.getDescription() + ", 价格: " + espresso.cost());

        // 订一杯HouseBlend,不需要调料,打印出它的描述与价格
        Beverage houseBlend = new HouseBlend();
        System.out.println("饮料种类: " + houseBlend.getDescription() + ", 价格: " + houseBlend.cost());
        // 再来一杯调料为豆浆、摩卡、奶泡的HouseBlend咖啡
        houseBlend = new MochaDecorator(houseBlend);
        houseBlend = new SoyDecorator(houseBlend);
        houseBlend = new WhipDecorator(houseBlend);
        System.out.println("饮料种类: " + houseBlend.getDescription() + ", 价格: " + houseBlend.cost());
    }
    
}

输出:
饮料种类: Espresso Coffee, 价格: 1.99
饮料种类: Espresso Coffee, Mocha, Mocha, Whip, 价格: 2.49
饮料种类: House Blend Coffee, 价格: 0.89
饮料种类: House Blend Coffee, Mocha, Soy, Whip, 价格: 1.34

源代码

Click here


总结

  1. 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  2. 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
  3. 当对象的功能要求可以动态地添加,也可以再动态地撤销时。
  4. 装饰者模式是一个符合开放-关闭原则的模式。
  5. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
  6. 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

Java中的 I/O 流体系使用了装饰者模式。
装饰者模式
装饰者模式
FileReader 增加缓冲区而采用的装饰类 BufferedReader 的例子:

BufferedReader in = new BufferedReader(new FileReader("filename.txtn));
String s = in.readLine();
装饰者模式装饰者模式 codedancing 发布了23 篇原创文章 · 获赞 8 · 访问量 6万+ 私信 关注
上一篇:设计模式4 装饰者模式


下一篇:Codeforces Round #746 (Div. 2)