为何使用装饰模式
在软件开发过程中,当对系统扩展功能时,希望复用当前系统中已存在现存组件,并对其进行动态扩展以实现新功能。此时,在不修改现存组件的情况下,同时对这些组件进行复用扩展。
举个例子,手机本身具备照相功能,但画质感人,爱美的人都希望在拍照的同时具有美化效果,因此,扩展出滤镜、瘦脸等功能。针对这个例子,来看看是否采用装饰模式的方式之间的区别:
- 不采用装饰模式;
重复创建类,会导致类爆炸;
如果希望其它组件(如相机)也具备扩展功能,也需要重新创建类;
- 采用装饰模式;
现在我们了解了如何复用现存组件进行扩展的方式,对装饰模式应该有一定的了解。
装饰模式
装饰模式:指在不改变现有对象结构的情况下,动态地给该对象添加额外职责的模式。下面是装饰模式的UML类图:
装饰模式主要包含以下角色:
- 抽象构件(Component):抽象接口,定义规范用于接收扩展功能的对象;
- 具体构件(ComponentA):实现抽象构件,通过装饰角色添加额外功能;
- 抽象装饰角色(Decorator):继承抽象构件,包含具体构件实例;
- 具体装饰角色(DecoratorA):继承抽象装饰,为具体构件添加额外功能;
装饰模式中,具体构件对象和装饰对象共同实现同一接口,同时使用组合关系,装饰角色对象包裹具体构件对象,并在保持具体构件对象的类结构不变的前提下,扩展额外的功能。
代码实现
根据上述手机拍照功能的例子,进行代码实现。
- 抽象构件
public interface Product {
void photograph();
}
- 具体构件
public class Phone implements Product {
@Override
public void photograph() {
System.out.println("手机拍照功能");
}
}
- 抽象装饰者
public abstract class AbstractFunction implements Product {
private Product product;
public AbstractFunction(Product product) {
this.product = product;
}
@Override
public void photograph() {
product.photograph();
}
}
- 具体装饰者
public class FilterFunction extends AbstractFunction {
public FilterFunction(Product product) {
super(product);
}
@Override
public void photograph() {
super.photograph();
filter();
}
public void filter(){
System.out.println("滤镜效果");
}
}
public class ThinFunction extends AbstractFunction {
public ThinFunction(Product product) {
super(product);
}
@Override
public void photograph() {
super.photograph();
thin();
}
public void thin(){
System.out.println("瘦脸功能");
}
}
- 测试类
public class DecoratorTest {
public static void main(String[] args) {
Product phone = new Phone();
FilterFunction filter = new FilterFunction(phone);
filter.photograph();
System.out.println("===================");
ThinFunction thin = new ThinFunction(phone);
thin.photograph();
System.out.println("===================");
//组合多个功能。
Product phone1 = new Phone();
phone1 = new FilterFunction(phone1);
phone1 = new ThinFunction(phone1);
phone1.photograph();
}
}
总结
- 装饰模式扩展对象功能的方式更加灵活;
- 可以设计不同的具体装饰类,组合多个不同行为的功能;
- 缺点是:装饰模式会增加很多子类,如果过度使用会使程序变得复杂;
- 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件;
- 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并;
使用场景
- 给现有类添加附加功能,而且不能采用生成子类的方式进行扩展的情况;
- 当需要扩展一组功能进行排列组合,而导致产生很多实现类时,使用继承关系很难实现,就可以考虑使用装饰模式;
- 当对象的功能要求可以动态添加、撤销的情况;