修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。
通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,类不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味着要为每一种组合创建一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。一个修饰模式的示例是JAVA里的Java I/O Streams的实现。
代码示例:
abstract class Cake
{
protected String description = null;
public abstract void printDescription();
}
class WhiteCake extends Cake
{
public WhiteCake()
{
description = new String("white cake");
}
public void printDescription()
{
System.out.println("this is a " + description);
}
}
/*
之所以要定义一个并未进行任何实现的“装饰者抽象类”,是由于:如果修饰类,直接
实现接口并添加装饰,则会表现为“基本实现类”和装饰类位于同一层上,显然不符合大家
通常的理解习惯。
*/
abstract class DecoratorCake extends Cake
{
public abstract void printDescription();
}
class MilkCake extends DecoratorCake
{
private Cake cake;
public MilkCake(Cake cake)
{
this.cake = cake;
}
public void printDescription()
{
this.cake.printDescription();
otherFunc();
}
private void otherFunc()
{
System.out.println("add milk");
}
}
class EggCake extends DecoratorCake
{
private Cake cake;
public EggCake(Cake cake)
{
this.cake = cake;
}
public void printDescription()
{
this.cake.printDescription();
otherFunc();
}
private void otherFunc()
{
System.out.println("add egg");
}
}
class Main
{
public static void main(String[] args)
{
//测试用例
Cake whiteCake = new WhiteCake();
whiteCake.printDescription();
//多层修饰
DecoratorCake multiCake = new EggCake(new MilkCake(new WhiteCake()));
multiCake.printDescription();
}
}
要点总结
1)继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
2)在设计中,应该允许行为可以被扩展,而无须修改现有的代码。
3)组合和委托可用于在运行时动态地加上新的行为。
4)除了继承,装饰者模式也可以让我们扩展行为。
5)装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6)装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
7)装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8)可以用无数个装饰者包装一个组件。
9)装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
10)装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。