工廠模式和策略模式:
在Java的設(shè)計模式中拥刻,工廠模式和策略模式都是很常見且實用的模式扁位。它們各自解決了不同的問題蒜危,在復雜的系統(tǒng)設(shè)計中墨技,這兩種模式往往會結(jié)合使用役拴,以提升代碼的靈活性糊探、可維護性和擴展性。
1河闰、工廠模式(Factory Pattern)是一種創(chuàng)建型設(shè)計模式科平,旨在定義一個接口來創(chuàng)建對象,但讓子類決定實例化哪一個類姜性。工廠模式讓一個類的實例化延遲到其子類瞪慧。
2、策略模式(Strategy Pattern)是一種行為型設(shè)計模式部念,定義了一系列算法弃酌,并將每個算法封裝起來,使它們可以互換使用儡炼。策略模式使得算法可以在不影響客戶端的情況下發(fā)生變化妓湘。
1. 需求背景
假設(shè)我們需要開發(fā)一個訂單處理系統(tǒng),系統(tǒng)需要根據(jù)不同的支付方式(如信用卡乌询、PayPal榜贴、比特幣等)進行不同的處理。不同的支付方式有不同的處理策略妹田,并且系統(tǒng)需要支持將來擴展新的支付方式唬党。
2. 設(shè)計思路
使用策略模式:將不同的支付方式封裝為不同的策略,實現(xiàn)支付的多樣化處理鬼佣。
使用工廠模式:工廠模式負責根據(jù)用戶選擇的支付方式創(chuàng)建和選擇相應(yīng)的支付策略對象驶拱。執(zhí)行業(yè)務(wù)邏輯。
業(yè)務(wù)場景:
1晶衷、營銷系統(tǒng)屯烦,根據(jù)不同業(yè)務(wù)渠道推送消息坷随,比如:微信消息推送、釘釘工作通知驻龟、短信温眉、郵件通知
2、系統(tǒng)支付翁狐,業(yè)務(wù)選擇不同支付方式类溢,執(zhí)行不同的業(yè)務(wù)邏輯和處理措施。比如:微信露懒、支付寶闯冷、銀聯(lián)等
3、系統(tǒng)登錄懈词,根據(jù)需求選擇不同登錄方式蛇耀,觸發(fā)后臺不同的業(yè)務(wù)執(zhí)行邏輯。比如:微信坎弯、釘釘纺涤、短信、支付寶等不同的登錄渠道進行授權(quán)抠忘,完成業(yè)務(wù)系統(tǒng)登錄
解決方案(如下圖):
1撩炊、在業(yè)務(wù)層使用 if-else 判斷類型,去執(zhí)行不同的業(yè)務(wù)邏輯崎脉。
2拧咳、使用工廠模式+策略設(shè)計模式,同時結(jié)合SpringBoot注解選擇不同的策略囚灼,實現(xiàn)不同的業(yè)務(wù)邏輯骆膝。
代碼示例
定義枚舉
@AllArgsConstructor
@Getter
public enum SaleOrderEnum {
SHOP_SALE_ORDER("10001", "店鋪業(yè)務(wù)零售單"),
SUPPLY_SALE_ORDER("10002","供應(yīng)鏈直銷單"),
;
private static final Map<Long, SaleOrderEnum > ORDER_MAP = new HashMap<>(32);
static {
for (SaleOrderEnum orderStatusEnum : SaleOrderEnum .values()) {
ORDER_STATUS_MAP.put(orderStatusEnum.getCode(), orderStatusEnum);
}
}
public static SaleOrderEnum findByCode(String code) {
// 這里可以直接調(diào)用getSaleOrdeEnum()方法也可以獲取。
for (SaleOrderEnum orderEnum : SaleOrderEnum.values()) {
if (orderEnum.getCode().equals(code)) {
return orderEnum;
}
}
throw new IllegalArgumentException("Please check the passed parameters");
}
private String code;
private String name;
public static SaleOrderEnum getSaleOrdeEnum(Long code) {
return ORDER_STATUS_MAP.get(code);
}
}
定義注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OrderStrategyType {
SaleOrderEnum orderType();
}
描述:定義這個注解的目的灶体,是在不同的策略上使用的阅签,在工廠中就可以根據(jù)注解查找到所有的策略,并保存進 map 中映射起來赃春,一旦有參數(shù)進來愉择,就去 map 中去查找對應(yīng)的策略,就可以執(zhí)行對應(yīng)的方法了织中,減少if-else代碼塊優(yōu)化代碼锥涕,同時可以保證代碼的擴展性。
定義策略類的接口
首先定義一個策略接口狭吼,然后編寫業(yè)務(wù)類去實現(xiàn)
public interface OrderStrategy {
/**
* 查詢數(shù)據(jù)方法
*
* @return 返回結(jié)果
*/
List<Object> executeOrderBiz();
}
1层坠、門店業(yè)務(wù)實現(xiàn)類、實現(xiàn)工單策略處理接口刁笙,編寫業(yè)務(wù)執(zhí)行邏輯
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SHOP_SALE_ORDER)
public class ShopSaleOrderStrategy implements OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("執(zhí)行門店銷售單業(yè)務(wù)功能......");
return Arrays.asList("業(yè)務(wù)工單1", "業(yè)務(wù)工單2", "業(yè)務(wù)工單3");
}
}
2破花、供應(yīng)鏈業(yè)務(wù)實現(xiàn)類谦趣、實現(xiàn)工單策略處理接口,編寫業(yè)務(wù)執(zhí)行邏輯
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SUPPLY_SALE_ORDER)
public class SupplySaleOrderStrategy implements OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("執(zhí)行供應(yīng)鏈銷售單業(yè)務(wù)功能......");
return Arrays.asList("供應(yīng)鏈工單1","供應(yīng)鏈工單2");
}
}
注意:我們要把具體的業(yè)務(wù)實現(xiàn)類策略交給 Spring 管理座每,所以加上了 @Component 注解前鹅。
定義工廠類(業(yè)務(wù)調(diào)用通用入口)
@Component
@RequiredArgsConstructor
public class OrderStrategyFactory {
/**
* Spring會注入所有OrderStrategy的業(yè)務(wù)實現(xiàn)類
*/
private final List<OrderStrategy> strategyList;
private static Map<SaleOrderEnum, OrderStrategy> map = new ConcurrentHashMap<>();
public static OrderStrategy getStrategy(String code){
return map.get(SaleOrderEnum.findByCode(code));
}
public OrderStrategyFactory(List<OrderStrategy> strategyList) {
this.strategyList = strategyList;
}
@PostConstruct
public void init(){
strategyList.forEach(i -> {
OrderStrategyType annotation = AnnotationUtils.findAnnotation(i.getClass(), OrderStrategyType.class);
map.put(annotation.orderType(),i);
});
}
}
上面案例代碼中我們已經(jīng)把具體的策略,像 SupplySaleOrderStrategy峭梳、ShopSaleOrderStrategy 這些策略的實現(xiàn)舰绘,交給 spring 管理了,也就是說我們可以從 spring 的容器中獲取到對應(yīng)的 bean葱椭,當然也可以進行依賴注入,這就是Spring中IOC容器特性孵运。
所以在工廠類中秦陋,我們定義了一個 strategyList 屬性,并且使用 @RequiredArgsConstructor 注解治笨,就是使用構(gòu)造器的方式將屬性注入驳概。所以當 spring 容器啟動的時候,就會查找所有策略大磺,并注入到工廠類的屬性當中抡句。這是 spring 的依賴注入的原理探膊。
我們使用 @PostContruct 注解杠愧,當工廠類加載完畢之后,就會執(zhí)行 init()方法逞壁。這時候流济,因為 orderStrategyFactory 這個 bean 已經(jīng)在 spring 容器中加載完畢了,并且也注入了屬性腌闯,所以這個 strategyList 就有值了绳瘟,我們可以遍歷這個列表,獲取到對應(yīng) OrderStrategyType 這個注解的信息姿骏,然后放入到 map 當中糖声,后面我們就可以根據(jù)枚舉類型將不同的策略對應(yīng)起來了。
使用層面:業(yè)務(wù)調(diào)用示例(使用案例)
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping(("/getList")
public List<Object> getList(@RequestParam String type){
OrderStrategy strategy = OrderStrategyFactory.getStrategy(type);
return strategy.executeOrderBiz();
}
}
總結(jié):
這樣就可以避免代碼中的if-else分瘦,同時可以動態(tài)增加業(yè)務(wù)實現(xiàn)類蘸泻。
比如增加其他業(yè)務(wù)實現(xiàn)類,直接實現(xiàn)OrderStrategy 接口即可嘲玫,同時修改每個執(zhí)行業(yè)務(wù)的邏輯悦施,不會相互影響。使用時去团,我們就可以在枚舉類中加入對應(yīng)的類型抡诞,在具體的策略實現(xiàn)類中加入注解穷蛹,并指定對應(yīng)的枚舉類型,這樣我們就可以完成我們的業(yè)務(wù)需求了昼汗。
補充描述:上面實現(xiàn)接口肴熏、也可以繼承抽象類
定義業(yè)務(wù)抽象類
public abstract class OrderStrategy {
public abstract List<Object> executeOrderBiz();
}
使用案例:將實現(xiàn)類中原來的implements 變成extends 其他代碼完全一致
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SHOP_SALE_ORDER)
public class ShopSaleOrderStrategy extends OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("執(zhí)行門店銷售單業(yè)務(wù)功能......");
return Arrays.asList("業(yè)務(wù)工單1", "業(yè)務(wù)工單2", "業(yè)務(wù)工單3");
}
}