定义
Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。它是一种对象结构型模式。
类图
- Component: 抽象构件,是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件。
- ConcreteComponent: 具体构件,待装饰的实现对象
- Decorator: 抽象装饰类,一般是一个抽象类,实现接口或者抽象方法。
- ConcreteDecorator: 具体装饰类
优缺点
优点
- 装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。
- 装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
- 装饰模式可以动态地扩展一个实现类的功能,这不需要多说,装饰模式的定义就是如此。
缺点
- 多层装饰会产生一些额外的对象以及装饰类对象,增加系统的复杂度,加大学习与理解的难度;
- 多层装饰的问题排查也会更加复杂。
应用
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类)。
实现
想必大家都去星巴克喝过咖啡或者饮料吧,星巴克的咖啡可以选择加糖加牛奶,加糖加牛奶可以当做装饰类,装饰咖啡,让它变得更有味道!
饮料接口,有价格和具体描述方法:
package com.chenly.designpattern.decorator;
/**
* 饮料
*
* @author chenly
* @create 2020-11-22 20:40
*/
public interface Drink {
int getPrice();
String getDescription();
}
饮料具体实现,咖啡
package com.chenly.designpattern.decorator;
/**
* 咖啡
*
* @author chenly
* @create 2020-11-22 20:40
*/
public class Cafe implements Drink {
private int price = 20;
private String description = "这是一杯咖啡";
@Override
public int getPrice() {
return price;
}
@Override
public String getDescription() {
return description;
}
}
装饰类接口,这里没有放方法,所以这个接口其实可有可无
package com.chenly.designpattern.decorator;
/**
* @author chenly
* @create 2020-11-22 20:42
*/
public abstract class CafeDecorator implements Drink{
//可以没有方法,也可以在这里做一些特殊的处理
}
两个具体装饰类:
package com.chenly.designpattern.decorator;
/**
* 加牛奶
*
* @author chenly
* @create 2020-11-22 20:47
*/
public class MilkCafe extends CafeDecorator {
private Drink drink;
private int price = 5;
private String description = "加牛奶";
public MilkCafe(Drink drink) {
this.drink = drink;
}
@Override
public int getPrice() {
return price + drink.getPrice();
}
@Override
public String getDescription() {
return drink.getDescription() + description;
}
}
package com.chenly.designpattern.decorator;
/**
* 加糖
*
* @author chenly
* @create 2020-11-22 20:44
*/
public class SugarCafe extends CafeDecorator {
private Drink drink;
private int price = 2;
private String description = "加糖";
public SugarCafe(Drink drink) {
this.drink = drink;
}
@Override
public int getPrice() {
return price + drink.getPrice();
}
@Override
public String getDescription() {
return drink.getDescription() + description;
}
}
总结
装饰模式的装饰器抽象类很多时候可以被简化省略,装饰模式的应用也十分广泛,例如,guava中的Ordering
就运用了装饰模式:
Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(sortKeyFunction)
每个类都继承自Ordering,同时持有一个Ordering实例,大鱼吃小鱼,小鱼吃虾米的链式形式。
在Spring项目中我们常常会为了某个类的扩展性,定义一个没有实现的钩子接口,代码里调用钩子接口的方法,只要实现了钩子接口就能在实例化中拿到这个bean并执行我们的装饰方法,完成装饰的效果,这也是一种装饰。