什么是装饰者模式?
装饰者模式属于结构型设计模式之一,主要目的是通过包装对象而不是继承来扩展功能。这种模式允许用户动态地为对象添加新的行为而无需修改其源代码。这与继承相比提供了一种更为灵活的方式来扩展功能。
装饰者模式的关键组成部分:
抽象组件(Component):定义核心业务接口。
具体组件(ConcreteComponent):核心业务实现类,实现了抽象组件接口。
装饰器(Decorator):持有抽象组件的引用,实现抽象组件接口,用来扩展功能。
适用场景
想增强现有类的功能,但又不想改变这个类的原有代码。
想无需修改原有代码的情况下即可使用对象。
通过继承来扩展对象行为的方案难以实现或者根本不可行。
实践
下面是一个基础的点餐功能。现在点餐的基础功能上新增,折扣、饮料两个功能。
基础服务
接口
public interface OrderService {
double getPrice(); // 获取价格
void placeOrder(); // 下单
}
实现类:
@Service
public class OrderServiceImpl implements OrderService {
private final double price = 50.0; // 固定价格
@Override
public double getPrice() {
return price;
}
@Override
public void placeOrder() {
System.out.println("单价: " + getPrice());
}
}
扩展功能
我们将实现两个新功能:折扣和饮料。这两个功能将分别使用装饰者模式进行实现。
折扣装饰者
我们将创建一个折扣装饰者,允许在订单上应用折扣
public class DiscountDecorator implements OrderService {
private final OrderService orderService; // 被装饰的对象
private final double discount; // 折扣金额
public DiscountDecorator(OrderService orderService, double discount) {
this.orderService = orderService;
this.discount = discount;
}
@Override
public double getPrice() {
return orderService.getPrice() - discount; // 返回折扣后的价格
}
@Override
public void placeOrder() {
orderService.placeOrder();
System.out.println("折扣: " + discount);
}
}
饮料服务接口和实现
添加一个饮料接口和其实现类,以支持饮料功能。
接口
public interface DrinkService {
double getPrice(); // 获取饮料价格
}
实现类
@Service
public class DrinkServiceImpl implements DrinkService {
private final double price = 0; // 初始化饮料价格
@Override
public double getPrice() {
return price;
}
}
饮料装饰者
public abstract class DrinkServiceDecorator implements DrinkService {
protected DrinkService drinkService;
public DrinkServiceDecorator(DrinkService drinkService) {
this.drinkService = drinkService;
}
@Override
public double getPrice() {
return drinkService.getPrice();
}
}
ColaDecorator.java 可乐类
public class ColaDecorator extends DrinkServiceDecorator {
private final double additionalPrice = 5.0; // 可乐附加价格
public ColaDecorator(DrinkService drinkService) {
super(drinkService);
}
@Override
public double getPrice() {
return super.getPrice() + additionalPrice; // 添加可乐价格
}
}
OrangeJuiceDecorator.java 橙汁类
public class OrangeJuiceDecorator extends DrinkServiceDecorator {
private final double additionalPrice = 6.0; // 橙汁附加价格
public OrangeJuiceDecorator(DrinkService drinkService) {
super(drinkService);
}
@Override
public double getPrice() {
return super.getPrice() + additionalPrice; // 添加橙汁价格
}
}
修改控制器以支持新功能
@RestController
public class OrderController {
private OrderService orderService; //订单服务
private DrinkService drinkService; // 饮料服务
public OrderController(OrderService orderService, DrinkService drinkService) {
this.orderService = orderService;
this.drinkService = drinkService;
}
@GetMapping("/placeOrder")
public String placeOrder(@RequestParam(value = "drinkType", required = false) String drinkType,
@RequestParam(value = "discount", required = false) Double discount) {
double totalPrice = orderService.getPrice();
// 处理饮料逻辑
if ("cola".equalsIgnoreCase(drinkType)) {
drinkService = new ColaDecorator(drinkService);
} else if ("orange".equalsIgnoreCase(drinkType)) {
drinkService = new OrangeJuiceDecorator(drinkService);
}
// 处理折扣逻辑
if (discount != null) {
orderService = new DiscountDecorator(orderService, discount);
}
totalPrice = drinkService.getPrice() + orderService.getPrice(); // 计算折扣后的订单价格
orderService.placeOrder();
System.out.println("添加的饮料价格: " + drinkService.getPrice());
return "总金额: + " + totalPrice;
}
}
测试
基础功能
保证以前的功能不受影响:直接下单符合预期50
下单并同时添加可乐和折扣:
单价50,可乐5,折扣20,符合预期35
总结
装饰者模式比继承较为灵活,不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
继承:在编译时定义类的行为,任何新功能的添加都需要创建新的子类,一旦编译,类的行为就已经固定,无法在运行时更改。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。