装饰器模式
装饰模式就是将其实现的功能拆分成一个个装饰器类,然后这些装饰器类实现功能并组装起来达成预期功能,还可以在不知不觉中增加其他装饰器增加新功能,而被装饰类是无感知的
1.装饰器模式的本质
装饰模式的本质 其实可以简单的理解为"给对象添加新功能的灵活方式"
(一)关键点:
- 动态组合:可以在运行时添加或修改对象的功能,而不需要改变对象的原始代码。
- 相同接口:装饰器和被装饰的对象实现相同的接口,这样可以方便地替换和使用。
- 组合而非继承:与其通过继承创建新的类,装饰器模式通过将一个对象包装在另一个对象中来扩展功能,这样更加灵活。
(二)举个例子:
想象一下你有一个咖啡对象,基本的咖啡只包含咖啡本身。现在你想要给咖啡添加不同的配料,比如牛奶、糖或香料。你可以创建几个装饰器:
- 牛奶装饰器:给咖啡添加牛奶。
- 糖装饰器:给咖啡添加糖。
每个装饰器都实现相同的接口,这样你可以将它们组合在一起。例如,你可以先用牛奶装饰器装饰咖啡,再用糖装饰器装饰牛奶咖啡,得到一杯牛奶糖咖啡,而不需要修改咖啡的原始类。
2.何时选用装饰器模式
以下情况下可以考虑使用装饰器模式:
- 动态添加功能:当你需要在不改变现有对象接口的前提下,动态地为对象添加额外的功能或修改其行为时,装饰器模式非常适合。
- 组合优于继承:如果你想通过组合的方式来扩展对象功能,避免因继承导致的类数量激增(类爆炸)或静态绑定的限制,装饰器模式是一个不错的选择。
- 运行时功能调整:在运行时需要灵活地添加、移除或修改对象功能,而不影响其他对象时,装饰器模式能够很好地满足这个需求。
- 最小化现有代码修改:当你希望在不对现有代码进行大规模修改的情况下,对现有对象进行功能扩展时,装饰器模式提供了一种优雅的解决方案。
3.装饰器的优缺点
(一)优点:
- 灵活性:可以随时为对象添加新功能,不用修改原来的代码。
- 扩展性:可以通过组合多个装饰器来增加功能,避免创建很多复杂的子类。
- 清晰职责:每个装饰器专注于特定的功能,使代码更易读、更易维护。
- 减少类数量:用装饰器来增加功能,比通过继承创建大量子类要简洁得多。
(二)缺点:
- 复杂性:使用多个装饰器时,代码结构可能变得复杂,理解起来比较困难。
- 性能问题:每添加一个装饰器,就会增加一次方法调用,可能影响性能。
- 调试难:调试的时候需要逐层检查,可能会比较麻烦。
- 可能过于复杂:如果用得太多,代码可能会变得难以理解和维护。
4、代码实现
不使用设计模式写法
1.咖啡类
java
/**
* 咖啡接口
*/
interface Coffee {
/**
* 获取描述信息
* @return
*/
String getDescription();
/**
* 获取花费
* @return
*/
double getCost();
}
/**
* 黑咖啡
*/
class BlackCoffee implements Coffee {
@Override
public String getDescription() {
return "黑咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
2、测试类
java
public class Client {
public static void main(String[] args) {
// 订一杯黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
}
}
3.结果
tex
订单:黑咖啡
总价:2.0
功能实现完,又想给咖啡加糖,以为直接在BlackCoffee
类中加个方法就行了,但实际上不能这么做,这么做违反了开闭原则。
那怎么实现新增功能呢,新增一个子类,让他继承BlackCoffee
即可,但如果还要加功能就还要新增子类,子类或越来越庞大。
4.给咖啡加糖
java
/**
* 加糖黑咖啡
*/
class SugarBlackCoffee extends BlackCoffee {
@Override
public String getDescription() {
return super.getDescription() + ", 加糖";
}
@Override
public double getCost() {
return super.getCost() + 0.3;
}
}
5.修改测试类
java
public class Client {
public static void main(String[] args) {
// 加糖黑咖啡
Coffee sugarBlackCoffee = new SugarBlackCoffee();
System.out.println("订单:" + sugarBlackCoffee.getDescription());
System.out.println("总价:" + sugarBlackCoffee.getCost());
}
}
6.结果
tex
订单:黑咖啡, 加糖
总价:2.3
如果想要在不继承类的情况下增加功能,一种较好的方式是增加装饰器模式
使用设计模式优化
1.定义组件接口及真实实现对象
java
/**
* 咖啡接口
*/
interface Coffee {
/**
* 获取描述信息
* @return
*/
String getDescription();
/**
* 获取花费
* @return
*/
double getCost();
}
/**
* 黑咖啡
*/
class BlackCoffee implements Coffee {
@Override
public String getDescription() {
return "黑咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
2.装饰器抽象类及具体的装饰器对象
java
/**
* 咖啡装饰器抽象类
*/
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
/**
* 真实加糖装饰器
*/
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", 加糖";
}
@Override
public double getCost() {
return super.getCost() + 0.3;
}
}
/**
* 真实加奶装饰器
*/
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", 加牛奶";
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
}
3.测试类
java
public class Main {
public static void main(String[] args) {
// 订一杯黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 再加糖
coffee = new SugarDecorator(coffee);
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 加两份糖,一份奶的黑咖啡
MilkDecorator coffee2 = new MilkDecorator(new SugarDecorator(new SugarDecorator(new BlackCoffee())));
System.out.println("订单:" + coffee2.getDescription());
System.out.println("总价:" + coffee2.getCost());
}
}
4.结果
tex
订单:黑咖啡
总价:2.0
订单:黑咖啡, 加牛奶
总价:2.5
订单:黑咖啡, 加牛奶, 加糖
总价:2.8
订单:黑咖啡, 加糖, 加糖, 加牛奶
总价:3.0999999999999996