設計模式-工廠方法模式
一、工廠方法模式介紹
工廠模式,是一種創建型設計模式,其在父類中提供一個創建物件的方法,允許子類決定實體化物件的型別,
工廠模式是 Java 開發中最常見的一種模式,其主要意圖是定義一個創建物件的介面,讓其子類決定實體化哪一個工廠類,工廠模式使其創建程序延遲到子類進行,
簡單說就是為了提供代碼的可擴展性,屏蔽每一個功能類中的具體實作邏輯,讓外部可以更加簡單的呼叫,同時,可以去掉眾多 ifelse ,
優點: 1、一個呼叫者想創建一個物件,只要知道其名稱就可以了, 2、擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以, 3、屏蔽產品的具體實作,呼叫者只關心產品的介面,
缺點:每次增加一個產品時,都需要增加一個具體類和物件實作工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴,這并不是什么好事,
二、模擬多種獎品發放
營銷場景中經常會有抽獎活動,我們在這里模擬兌換多種型別的商品,
假如我們有如下三種型別的商品介面:
| 序號 | 型別 | 介面 |
|---|---|---|
| 1 | 代金劵 | VoucherResult sendVoucher(String userId, String voucherId, int voucherNumber) |
| 2 | VIP體驗卡 | void openVIP(String userId, Date date, String time) |
| 3 | 實物獎品 | Boolean deliverGoods(DeliverRequest req) |
- 三個介面回傳型別不同,有物件型別、布爾型別、void
- 入參不同,代金券需要代金券 id 和代金券數量、VIP體驗卡需要體驗時間、實物獎品需要獎品物件(包含發貨地址等)
- 可能隨著業務的拓展,會增加其他商品型別,
三、非工廠模式實作
1. ifelse實作需求
public class PrizeController {
private static final String CODE_SUCCESS = "11111111";
private static final String CODE_FAIL = "00000000";
public AwardResult awardToUser(AwardRequest req) {
AwardResult res = null;
// 按照不同型別分法商品[1代金劵、2VIP體驗卡、3實物獎品]
if (req.getAwardType() == 1) {
VoucherService voucherService = new VoucherService();
VoucherResult voucherResult = voucherService.sendVoucher(req.getUserId(), req.getAwardId(), req.getAwardNumber());
if (CODE_SUCCESS.equals(voucherResult.getCode())) {
res = new AwardResult(CODE_SUCCESS, "成功");
} else {
res = new AwardResult(CODE_FAIL, voucherResult.getInfo());
}
} else if (req.getAwardType() == 2) {
VIPService vipService = new VIPService();
Date date = new Date(System.currentTimeMillis());
vipService.openVip(req.getUserId(), date, req.getTime());
res = new AwardResult(CODE_SUCCESS, "成功");
} else if (req.getAwardType() == 3) {
GoodsService goodsService = new GoodsService();
DeliverRequest deliverRequest = new DeliverRequest();
deliverRequest.setUserId(req.getUserId());
deliverRequest.setUserName(queryUserNameById(req.getUserId()));
deliverRequest.setUserPhone(queryUserPhoneById(req.getUserId()));
deliverRequest.setUserAddress(queryUserAddressById(req.getUserId()));
Boolean isSuccess = goodsService.deliverGoods(deliverRequest);
if (isSuccess) {
res = new AwardResult(CODE_SUCCESS, "成功");
} else {
res = new AwardResult(CODE_FAIL, "失敗");
}
}
return res;
}
public String queryUserNameById(String userId) {
return "Tom";
}
public String queryUserPhoneById(String userId) {
return "123456789";
}
public String queryUserAddressById(String userId) {
return "BeiJin";
}
}
- 如上為使用
ifelse實作業務需求,如果僅從業務角度看,已經實作了基本功能, - 但是經過幾次迭代后,接手這段代碼的研發將十分痛苦,重構成本高,且需要理清之前的每一個介面的使用,測驗回歸驗證時間長,需要全部驗證一次,
2. 測驗驗證
@Test
public void testAwardToUser() {
PrizeController prizeController = new PrizeController();
AwardResult awardResult = null;
System.out.println("******模擬多種獎品發放測驗******");
AwardRequest awardReq = new AwardRequest();
awardReq.setAwardId("0001");
awardReq.setAwardNumber(1);
awardReq.setUserId("10001");
System.out.println("\n------ 代金券的發放 ------");
awardReq.setAwardType(1);
awardResult = prizeController.awardToUser(awardReq);
System.out.println("代金券發放結果:" + awardResult.getRes());
System.out.println("\n------ VIP體驗卡的發放 -------");
awardReq.setAwardType(2);
awardResult = prizeController.awardToUser(awardReq);
System.out.println("VIP體驗卡發放結果:" + awardResult.getRes());
System.out.println("\n------ 實物獎品的發放 -------");
awardReq.setAwardType(3);
awardResult = prizeController.awardToUser(awardReq);
System.out.println("實物獎品發放結果:" + awardResult.getRes());
}
結果:
- 運行結果正常,滿足業務需求,寫的還很快,但是實在難以維護,
四、工廠模式優化代碼
1. 代碼實作
1.1 定義發獎介面
public interface IAward {
void sendAward(String userId, String awardId, Map<String, Object> map);
}
1.2 實作發獎介面
代金券
public class VoucherAwardService implements IAward{
private VoucherService voucherService = new VoucherService();
@Override
public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception{
VoucherResult voucherResult = voucherService.sendVoucher(userId, awardId, (int) map.get("voucherNumber"));
if (PrizeController.CODE_FAIL.equals(voucherResult.getCode())) {
throw new RuntimeException("失敗");
}
}
}
VIP體驗卡
public class VIPAwardService implements IAward{
private VIPService vipService = new VIPService();
@Override
public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception {
Date date = new Date(System.currentTimeMillis());
vipService.openVip(userId, (Date) map.get("data"), (String) map.get("time"));
}
}
實物獎品
public class GoodsAwardService implements IAward{
private GoodsService goodsService = new GoodsService();
@Override
public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception {
DeliverRequest deliverRequest = new DeliverRequest();
deliverRequest.setUserId(userId);
deliverRequest.setUserName(queryUserNameById(userId));
deliverRequest.setUserPhone(queryUserPhoneById(userId));
deliverRequest.setUserAddress(queryUserAddressById(userId));
Boolean isSuccess = goodsService.deliverGoods(deliverRequest);
if (!isSuccess) {
throw new RuntimeException("失敗");
}
}
public String queryUserNameById(String userId) {
return "Tom";
}
public String queryUserPhoneById(String userId) {
return "123456789";
}
public String queryUserAddressById(String userId) {
return "BeiJin";
}
}
- 可以看到每一種獎品的實作都包括在自己的類中,新增、修改或者洗掉都不會影響其他獎品功能的測驗
- 后續再新增的獎品只需要按照此結構進行填充即可,非常易于維護和擴展
- 統一入參和出參后,呼叫方不在需要關心內部的實作邏輯,按照統一方式處理即可
1.3 創建獎品工廠
public class AwardFactory {
public IAward getAwardService(Integer awardType) throws Exception{
if (awardType == null) return null;
if (1 == awardType) return new VoucherAwardService();
if (2 == awardType) return new VIPAwardService();
if (3 == awardType) return new GoodsAwardService();
throw new RuntimeException("不存在此獎品型別");
}
}
2.測驗驗證
@Test
public void testIAward() throws Exception{
AwardFactory awardFactory = new AwardFactory();
System.out.println("******模擬多種獎品發放測驗******");
System.out.println("\n------ 代金券的發放 ------");
IAward awardService1 = awardFactory.getAwardService(1);
Map<String, Object> map1 = new HashMap<>();
map1.put("voucherNumber", 1);
awardService1.sendAward("10001", "123456712", map1);
System.out.println("\n------ VIP體驗卡的發放 -------");
IAward awardService2 = awardFactory.getAwardService(2);
Map<String, Object> map2 = new HashMap<>();
map2.put("data", new Date(System.currentTimeMillis()));
map2.put("time", "23:59:59");
awardService2.sendAward("10002", "123123414", map2);
System.out.println("\n------ 實物獎品的發放 -------");
IAward awardService3 = awardFactory.getAwardService(3);
awardService3.sendAward("10003", "12312451", null);
}
五、總結
工廠模式可以避免創建者與具體的產品邏輯耦合、滿足單一職責,每一個業務邏輯實作都在所屬自己的類中完成、滿足開閉原則,無需修改呼叫方就可以在程式中引入新的產品型別,
但這樣也會帶來一些問題,比如有非常多的獎品型別,那么實作的子類會急速擴張,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/263288.html
標籤:設計模式
