设计模式之装饰者模式

装饰者模式

装饰者模式的定义

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

装饰者模式是一种“结构型”模式。

装饰者模式的使用场景

  • 需要透明且动态地扩展类的功能时。

装饰者UML类图

装饰者模式UML.png

角色介绍

  • Component:抽象组件。
    可以是一个接口或抽象类,其充当的就是被装饰的原始对象。

  • ConcreteComponent: 组件具体实现类。
    该类是Component类的基本实现,也是我们装饰的具体对象。

  • Decorator: 抽象装饰者。
    顾名思义,其承担的职责就是为了装饰我们的组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然,如果装饰逻辑单一,只有一个的情况下我们可以省略该类直接作为具体的装饰者。

  • ConcreteDecoratorA: 装饰者具体实现类。
    只是对抽象装饰者作出具体的实现。

  • ConcreteDecoratorB: 同上。
    同上。

例子: HeadFirst一书中 星巴兹饮料例子

星巴兹饮料装饰者模式类图:


image.png

具体代码实现如下:

饮料抽象类:

public abstract class Beverage {
    protected String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

黑色烘焙饮料:

public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "Dark Roast Coffee";
    }

    @Override
    public double cost() {
        return .99;
    }

}

脱咖啡因:

public class Decaf extends Beverage {

    public Decaf() {
        description = "Decaf Coffee";
    }

    @Override
    public double cost() {
        return 1.05;
    }
}

浓咖啡:

public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }

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

混合咖啡:

public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend Coffee";
    }

    @Override
    public double cost() {
        return .89;
    }
}

调味品装饰者抽象类:

public abstract class CondimentDecorator extends Beverage {

    @Override
    public abstract String getDescription();

}

具体装饰者:牛奶

public class Milk extends CondimentDecorator {
    private Beverage beverage;

    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }

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

    @Override
    public double cost() {
        return .10 + beverage.cost();
    }
}

具体装饰者:摩卡

public class Mocha extends CondimentDecorator {
    private Beverage beverage;

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

    @Override
    public double cost() {
        return .20 + beverage.cost();
    }

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

具体装饰者:豆浆

public class Soy extends CondimentDecorator {
    private Beverage beverage;

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

    @Override
    public double cost() {
        return .15 + beverage.cost();
    }

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }
}

具体装饰者:奶泡

public class Whip extends CondimentDecorator {
    private Beverage beverage;

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

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

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }
}

测试下我们的程序

public class StarbuzzCoffee {
    public static void main(String[] args){
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + " $" + beverage.cost());

        // 制造 DarkRoast(双倍摩卡奶泡咖啡)
        Beverage beverage2 = new DarkRoast();
        beverage2 = new Mocha(beverage2); // 使用 mocha 装饰它
        beverage2 = new Mocha(beverage2); // 使用第二个 mocha 装饰它
        beverage2 = new Whip(beverage2); // 使用 whip 装饰它
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());


        Beverage beverage3 = new HouseBlend();
        beverage3 = new Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription()
                + " $" + beverage3.cost());
    }
}

测试结果:

Espresso 1.49
House Blend Coffee, Soy, Mocha, Whip $1.34

总结

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方
    式。
  • 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
  • 组合和委托可用于在运行时动态地加上新的行为。
  • 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
  • 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
  • 你可以用无数个装饰者包装一个组件。
  • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。

优点

  • 除了继承,装饰者模式也可以让我们扩展行为。
  • 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。

缺点

  • 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

Android源码中的装饰者模式实现

  • ContextWrapper

参考资料
《Head First 设计模式》
《Android 源码设计模式解析与实战》

你可能感兴趣的:(设计模式之装饰者模式)