装饰者模式 : 动态地将责任附加到对象上,或者说属性附加,如果要扩展,比继承的方式更加具有弹性
装饰者模式的优点与缺点
- 优点:装饰者模式与继承目的都是扩展对象的功能,但是装饰者模式可以提供比继承更多的灵活性。
- 缺点:由于装饰者模式更加灵活的特性,导致加入各种不同的小类,导致复杂度提升,不便于理解
UML图
- MilKyTea:装饰者与被装饰者的父类,可以是接口或者抽象类,可以用来定义行为和属性
- ReadTea、SeasonTea:被装饰者,定义具体行为和属性
- DecroateMilkyTea: 装饰者的抽象类,继承与MikyTea 用来装饰被装饰者。可以是抽象类与接口
- Pudding、IceCream:装饰者的具体实现,用来装饰被装饰者的具体行为与属性
案例
平时工作之余,我们会叫上基友们一起点杯奶茶放松放松. 奶茶店有各种各样不同种类的奶茶让我们挑选, 但是我们不知道具体价格与品种,代码如下
定义奶茶基类 MilkyTea .java
public abstract class MilkyTea {
/**
* 奶茶名
*/
public String name;
public String getName() {
return name;
}
/**
* 奶茶价格
*
* @return
*/
abstract public double cost();
}
知道奶茶之后,我们就要选择不同种类和价格的奶茶,比如我平时就喜欢点红茶拿铁
定义具体被装饰者 RedTea.java
public class RedTea extends MilkyTea {
public RedTea() {
name = "红茶拿铁";
}
@Override
public double cost() {
return 16;
}
}
当我们选好奶茶与价格之后.我们会发现还有配料可以选择,比如布丁、燕麦、冰淇淋等,这个时候我们在没有了解装饰者模式之前可能立马会想到去父类加入几个字段就没问题了。虽然这样很快就能解决问题,但是如果是大型项目,突然来需求要改动,如果又回去改动之前的代码可能会引起别的地方错误,也说明我们程序的扩展型不强,并且还违背了 设计原则的 开闭原则, 这个时候我们就可以使用装饰者模式,给奶茶 动态添加布丁、燕麦等属性
开闭原则 一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。就相当于说,不修改原代码的情况下,扩展需求功能。
定义装饰者基类 DecorateMilkyTea .java
public abstract class DecorateMilkyTea extends MilkyTea {
/**
* 获取奶茶名
*
* @return
*/
public abstract String getName();
}
定义装饰者 IceCream.java
public class IceCream extends DecorateMilkyTea {
//奶茶基类
private MilkyTea milkyTeal;
public IceCream(MilkyTea milkyTea) {
//获取被装饰者
this.milkyTeal = milkyTea;
}
@Override
public String getName() {
return milkyTeal.getName() + ",添加冰淇淋";
}
@Override
public double cost() {
return milkyTeal.cost() + 6.0;
}
}
最后我们根据我们自己选择的奶茶+配料来计算结果
//红茶
MilkyTea milkyTea = new RedTea();
System.out.println(milkyTea.getName() + " === " + milkyTea.cost());
//红茶的基础上增加布丁配料(装饰者)
milkyTea = new Pudding(milkyTea);
System.out.println(milkyTea.getName() + " === " + milkyTea.cost());
//四季拿铁
MilkyTea seasonTea = new SeasonTea();
//司机拿铁的基础上增加布丁与冰淇淋配料(装饰者)
seasonTea = new IceCream(seasonTea);
seasonTea = new Pudding(seasonTea);
System.out.println(seasonTea.getName() + " === " + seasonTea.cost());
打印结果
红茶拿铁 总计花费 16.0
红茶拿铁,添加布丁 总计花费 20.2
四季拿铁,添加冰淇淋,添加布丁 总计花费 22.2
总结
- 装饰者和被装饰者对象有相同的超类型,在需要原始对象(被装饰者)的场合,都可以用装饰过得对象代替原始对象。
- 可以用一个或多个装饰者包装一个对象(被装饰者)
- 装饰者模式 是多态的另一种体现
- 装饰者模式 会导致出现很多小对象,过度使用会程序变的复杂