装饰器模式简介
装饰器模式定义
从代码层面而言,是对类的一个扩展或者是修饰,从传统方法而言,我们可以使用继承来对某一个类进行扩展,但是往往会导致会出现非常多的子类,如果我们要想避免这种情况,那么我们就可以使用设计模式中的——装饰器模式。
装饰器模式也称为包装模式是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责,即增加其额外功能,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。简单来说,就是一个东西,然后不断在该东西上添加符合该物件的功能,进行扩展的模式
装饰器模式包含以下角色:
- 抽象组件(Component): 可以是一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;
- 具体组件(ConcreteComponent): 实现/继承抽象组件的一个具体对象,也即被装饰对象;
- 抽象装饰器(Decorator): 通用的装饰具体组件的装饰器,其内部必然有一个属性指向抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个抽象组件,这是强制的通用行为;
- 具体装饰器(ConcreteDecorator): 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。理论上,每个具体装饰器都扩展了 抽象组件对象的一种功能
装饰器模式优缺点:
优点:
- 装饰器模式可拓展性强,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 易于维护,哪个功能出问题直接找相关代码即可
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
缺点:
- 装饰器模式会增加许多子类,会使得代码变得繁多增加程序的复杂性
- 动态装饰在多层装饰时会更复杂
使用场景
- 用于扩展一个类的功能,或者给一个类添加附加职责
- 动态的给一个对象添加功能,这些功能可以再动态的被撤销
- 需要为一批平行的兄弟类进行改装或者加装功能
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时
以下举一个装饰器模式的例子:
比如平时我们都肯定喝过奶茶,奶茶肯定有很多种,而且点的奶茶,还可以加料,比如加珍珠,加椰果之类的。这就可以用装饰器模式处理了。
创建Component对象,规定为MilkTea,行为功能为输出名字和输出价格:
public abstract class MilkTea {
protected abstract String getName();
protected abstract int getPrice();
}
创建ConcreteComponent,规定为焦糖奶茶:
public class CaramelMilkTea extends MilkTea {
@Override
protected String getName() {
return "焦糖奶茶";
}
@Override
protected int getPrice() {
return 20;
}
}
创建Decorator对象,进行静态代理
public abstract class Add extends MilkTea {
private final MilkTea milkTea;
public Add(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
protected String getName() {
return this.milkTea.getName();
}
@Override
protected int getPrice() {
return this.milkTea.getPrice();
}
}
创建多个ConcreteDecorator对象,实现功能扩展(加装饰球、加缎带)
public class Pearl extends Add {
public Pearl(MilkTea milkTea) {
super(milkTea);
}
@Override
protected String getName() {
return super.getName() + ",加了珍珠";
}
@Override
protected int getPrice() {
return super.getPrice() + 5;
}
}
public class Coconut extends Add {
public Coconut(MilkTea milkTea) {
super(milkTea);
}
@Override
protected String getName() {
return super.getName() + ",加了椰果";
}
@Override
protected int getPrice() {
return super.getPrice() + 3;
}
}
测试类:
public class test {
public static void main(String[] args) {
System.out.println("点一杯焦糖奶茶");
MilkTea caramelMilkTea = new CaramelMilkTea();
System.out.println(caramelMilkTea.getName() + ",总价格:" + caramelMilkTea.getPrice() + "元");
System.out.println("加珍珠");
caramelMilkTea = new Pearl(caramelMilkTea);
System.out.println(caramelMilkTea.getName() + ",总价格:" + caramelMilkTea.getPrice() + "元");
System.out.println("再加椰果");
caramelMilkTea = new Coconut(caramelMilkTea);
System.out.println(caramelMilkTea.getName() + ",总价格:" + caramelMilkTea.getPrice() + "元");
}
}
输出结果如下所示:
点一杯焦糖奶茶
焦糖奶茶,总价格:20元
加珍珠
焦糖奶茶,加了珍珠,总价格:25元
再加椰果
焦糖奶茶,加了珍珠,加了椰果,总价格:28元
从结果可以看成功根据需求,获取到了一杯焦糖奶茶加了珍珠和椰果,并计算出价格。
总而言之:
在实际项目中,装饰器模式的运用可以通过遵循一些设计原则和最佳实践来更好地实现。首先,我们应该保持设计简单,避免过度使用装饰器,以及遵循面向对象的基本设计原则。其次,我们应该尽可能地避免重复代码和保持代码的可读性和可维护性。最后,我们应该考虑设计模式的上下文和这些模式的适用性,以及不同场景下应用不同设计模式的灵活性。
装饰器模式满足了设计原则的单一职责原则,可以在自己的装饰类中完成一些额外的功能逻辑拓展,而且不会影响到被装饰的主类,同时可以按需在运行时添加或删除这部分逻辑。
装饰器模式的重点是对抽象类接口方式的使用,同时被实现的接口可以通过构造函数传递其类,由此增加扩展性,并重写方法中可以通过父类实现的功能。
以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git