装饰器模式(Decorator Pattern)是一种结构型设计模式,它可以在不改变原有对象的基础上,动态地给对象添加新的职责和行为。 该模式的核心思想是以递归的方式,通过将对象包装在装饰器对象中,来不断添加新的行为和职责。装饰器模式的结构包括以下角色:
传统设计模式讲解时使用的示例代码,大都采用与读者日常生活接解的业务系统没有多大关联关系。以致大部分读者无法做到学以致用,学完就忘记。本文采用使用日常生活中随处可见的电商相关业务来编写实现代码
假设我们有一个电商平台,需要对商品进行价格计算,同时还需要对价格进行打折和折扣券的处理,我们可以使用装饰器模式来实现该功能。
首先定义一个抽象构件Component,表示商品的基本行为:
public interface Component {
double getPrice();
}
然后定义具体构件ConcreteComponent,表示一个具体的商品对象:
public class ConcreteComponent implements Component {
private double price;
public ConcreteComponent(double price) {
this.price = price;
}
@Override
public double getPrice() {
return price;
}
}
接着定义抽象装饰器Decorator,用于扩展Component的职责:
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override public double getPrice() {
return component.getPrice();
}
}
最后定义具体装饰器ConcreteDecorator,实现抽象装饰器接口,扩展Component的职责,并维护一个指向Component的引用。
例如,我们可以定义一个DiscountDecorator,用于对商品价格进行打折处理:
public class DiscountDecorator extends Decorator {
private double discount;
public DiscountDecorator(Component component, double discount) {
super(component); this.discount = discount;
}
@Override
public double getPrice() {
return component.getPrice() * discount;
}
}
我们还可以定义一个CouponDecorator,用于对商品价格进行折扣券处理:
public class CouponDecorator extends Decorator {
private double coupon;
public CouponDecorator(Component component, double coupon) {
super(component);
this.coupon = coupon;
}
@Override
public double getPrice() {
return component.getPrice() - coupon;
}
}
这样,我们就可以通过组合不同的装饰器对象,对商品的价格进行灵活的计算和处理:
Component product = new ConcreteComponent(100.0);
// 打八折
product = new DiscountDecorator(product, 0.8);
// 减10元
product = new CouponDecorator(product, 10.0);
// 计算最终价格
double price = product.getPrice();
下面展示了如何通过嵌套多个装饰器对象来实现复杂的功能:
interface Component { void operation(); }
class ConcreteComponent implements Component { public void operation() { System.out.println("ConcreteComponent operation"); } }
abstract class Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
public void operation() { component.operation(); } }
class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); }
public void operation() { super.operation(); addBehaviorA(); }
private void addBehaviorA() { System.out.println("add Behavior A"); } }
class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); }
public void operation() { super.operation(); addBehaviorB(); }
private void addBehaviorB() { System.out.println("add Behavior B"); } }
class ConcreteDecoratorC extends Decorator { public ConcreteDecoratorC(Component component) { super(component); }
public void operation() { super.operation(); addBehaviorC(); }
private void addBehaviorC() { System.out.println("add Behavior C"); } }
public class DecoratorDemo { public static void main(String[] args) { Component component = new ConcreteComponent(); Component decoratorA = new ConcreteDecoratorA(component); Component decoratorB = new ConcreteDecoratorB(decoratorA); Component decoratorC = new ConcreteDecoratorC(decoratorB); decoratorC.operation(); } } ```
在这个示例中,我们定义了一个抽象组件类 `Component` 和一个具体组件类 `ConcreteComponent` ,还定义了三个具体装饰器类 `ConcreteDecoratorA`、`ConcreteDecoratorB` 和 `ConcreteDecoratorC`。它们都继承自 `Decorator` 类,并通过嵌套其他装饰器对象来增加自己的职责。
在 `main()` 方法中,我们首先创建了一个具体组件对象 `ConcreteComponent` ,然后依次创建了三个装饰器对象 `ConcreteDecoratorA`、`ConcreteDecoratorB` 和 `ConcreteDecoratorC`,并将它们嵌套在一起使用。最后调用 `decoratorC.operation()` 方法输出结果,并观察其中添加了哪些行为。
这个示例展示了如何使用装饰器模式动态地为组件对象添加职责,并通过嵌套多个装饰器对象来实现复杂的功能。由于每个装饰器对象和被装饰对象具有相同的接口,因此可以方便地组合多个装饰器对象,并使得整个系统变得更加灵活。
为什么被装饰对象和装饰对象具有相同的接口 ?
在装饰器模式中,被装饰对象和装饰对象具有相同的接口,是因为这样做可以保证代码的灵活性和可扩展性。
首先,在面向对象编程中,接口是一种非常基础的概念,它提供了一个抽象层次,定义了一个类或者对象应该具有的属性和方法。通过将被装饰对象和装饰对象都实现同一个接口,我们可以保证它们可以互相替换,而且客户端不需要知道具体使用的是哪个对象。
其次,被装饰对象和装饰对象具有相同的接口可以让我们方便地组合多个装饰器,从而实现更加复杂的功能。装饰器模式允许我们动态地添加或删除对象的职责,每个装饰对象都可以通过嵌套其他装饰器来增加自己的职责,同时还能够保持相同的接口,使得整个系统变得更加灵活。
最后,在实际开发过程中,通常会出现一些基于框架或者基于接口的设计方式,这种设计方式可以使得程序实现更加灵活、易于维护和修改。因此,使用相同的接口可以促进代码的复用和替换,也可以使得代码更加具有可读性和可维护性。
综上所述,被装饰对象和装饰对象具有相同的接口是为了保证代码的灵活性和可扩展性,并且方便地组合多个装饰器来实现更加复杂的功能。