结构型模式 - 装饰者模式 (Decorator Pattern)
在展开讲装饰者模式之前,不得不提一下代理模式,因为这两者在一定的层度上是有相似性的, 通过对比可以让我们更好的理解装饰者.
定义与核心目的
- 装饰者模式
- 定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
- 核心目的:主要是为对象添加额外的功能,强调的是对对象功能的增强和扩展,是在不改变原有对象结构的基础上,给对象增加新的行为。
- 代理模式
- 定义:为其他对象提供一种代理以控制对这个对象的访问。
- 核心目的:主要是控制对对象的访问,代理对象充当了客户端和目标对象之间的中介,客户端通过代理对象来间接访问目标对象,从而可以在访问前后进行一些额外的操作,如权限验证、缓存等。
接下来通过代码对比一下装饰者模式、代理模式
// 装饰者模式
// 定义一个接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("执行基本操作");
}
}
// 装饰者抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰者
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("添加额外操作");
}
}
// 测试代码
public class DecoratorPatternExample {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratedComponent = new ConcreteDecorator(component);
decoratedComponent.operation();
}
}
如果把上面装饰者的例子中 Decorator 类去掉抽象 abstract 修饰, 然后把持有的 Component 改成具体对象 ConcreteComponent 并在构造方法里面直接创建要代理的对象, 那它就变成静态代理模式!!!
下面这是一个代理模式例子, 来看看它俩的相似之处吧.
// 定义一个接口
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("执行真实请求");
}
}
// 代理主题
class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
System.out.println("在请求前进行一些操作");
realSubject.request();
System.out.println("在请求后进行一些操作");
}
}
// 测试代码
public class ProxyPatternExample {
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject();
proxy.request();
}
}
再看一个经典的装饰者模式, 来加强一下装饰者的理解
// 抽象组件:饮品
interface Beverage {
String getDescription();
double cost();
}
// 具体组件:浓缩咖啡
class Espresso implements Beverage {
@Override
public String getDescription() {
return "浓缩咖啡";
}
@Override
public double cost() {
return 2.0;
}
}
// 抽象装饰者:调料
abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
// 具体装饰者:牛奶
class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ",加牛奶";
}
@Override
public double cost() {
return beverage.cost() + 0.5;
}
}
// 具体装饰者:巧克力
class Chocolate extends CondimentDecorator {
public Chocolate(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ",加巧克力";
}
@Override
public double cost() {
return beverage.cost() + 1.0;
}
}
// 测试类
public class CoffeeShopExample {
public static void main(String[] args) {
// 创建一个浓缩咖啡
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");
// 给浓缩咖啡加牛奶
Beverage espressoWithMilk = new Milk(espresso);
System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");
// 给加了牛奶的浓缩咖啡再加巧克力
Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);
System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");
}
}
这时候我们可以再创建一个装饰者, 比如配送方式的装饰者,看顾客是到店自取还是外卖配送, 因为这两种方式价格是不一样的.
这时候我们只需要另外建一个具体装饰者类即可.
// 抽象配送装饰者
abstract class DeliveryDecorator implements Beverage {
protected Beverage beverage;
public DeliveryDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
// 具体配送装饰者:外卖配送
class DeliveryByCourier extends DeliveryDecorator {
public DeliveryByCourier(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ",外卖配送";
}
@Override
public double cost() {
return beverage.cost() + 3.0; // 假设外卖配送费 3 元
}
}
// 具体配送装饰者:到店自取
class PickupInStore extends DeliveryDecorator {
public PickupInStore(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ",到店自取";
}
@Override
public double cost() {
return beverage.cost(); // 到店自取无额外费用
}
}
// 测试类
public class CoffeeShopExample {
public static void main(String[] args) {
// // 创建一个浓缩咖啡
// Beverage espresso = new Espresso();
// System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");
// // 给浓缩咖啡加牛奶
// Beverage espressoWithMilk = new Milk(espresso);
// System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");
// // 给加了牛奶的浓缩咖啡再加巧克力
// Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);
// System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");
// 使用的时候就可以 2 选 1 种配送
// 选择外卖配送
Beverage espressoWithAllAndDelivery = new DeliveryByCourier(espressoWithMilkAndChocolate);
System.out.println(espressoWithAllAndDelivery.getDescription() + ",价格:" + espressoWithAllAndDelivery.cost() + " 元");
// 选择到店自取
Beverage espressoWithAllAndPickup = new PickupInStore(espressoWithMilkAndChocolate);
System.out.println(espressoWithAllAndPickup.getDescription() + ",价格:" + espressoWithAllAndPickup.cost() + " 元");
}
}
这时候假设我们需求又双叒变更了, 我们只需要增加装饰者, 比如说要按顾客的会员等级对订单总额打折处理… 发挥你的想象吧, 它有无限的可能~