什麼是裝飾者模式?
裝飾者模式屬於結構型設計模式之一,主要目的是通過包裝對象而不是繼承來擴展功能。這種模式允許用户動態地為對象添加新的行為而無需修改其源代碼。這與繼承相比提供了一種更為靈活的方式來擴展功能。
裝飾者模式的關鍵組成部分:
抽象組件(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
總結
裝飾者模式比繼承較為靈活,不改變原有對象的情況下,動態的給一個對象擴展功能,即插即用。
繼承:在編譯時定義類的行為,任何新功能的添加都需要創建新的子類,一旦編譯,類的行為就已經固定,無法在運行時更改。
參考
https://refactoringguru.cn/design-patterns/decorator