設計模式-工廠方法模式 實戰演習 代碼實現

設計模式-工廠方法模式

1、工廠方法模式介紹

工廠模式,是一種建立型設計模式,其在父類中提供一個建立對象的方法,容許子類決定實例化對象的類型java

工廠模式是 Java 開發中最多見的一種模式,其主要意圖是定義一個建立對象的接口,讓其子類決定實例化哪個工廠類,工廠模式使其建立過程延遲到子類進行。設計模式

簡單說就是爲了提供代碼的可擴展性,屏蔽每個功能類中的具體實現邏輯。讓外部能夠更加簡單的調用。同時,能夠去掉衆多 ifelseide

優勢: 一、一個調用者想建立一個對象,只要知道其名稱就能夠了。 二、擴展性高,若是想增長一個產品,只要擴展一個工廠類就能夠。 三、屏蔽產品的具體實現,調用者只關心產品的接口。測試

缺點:每次增長一個產品時,都須要增長一個具體類和對象實現工廠,使得系統中類的個數成倍增長,在必定程度上增長了系統的複雜度,同時也增長了系統具體類的依賴。這並非什麼好事。優化

2、模擬多種獎品發放

營銷場景中常常會有抽獎活動,咱們在這裏模擬兌換多種類型的商品。設計

假如咱們有以下三種類型的商品接口:code

序號 類型 接口
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體驗卡須要體驗時間、實物獎品須要獎品對象(包含發貨地址等)
  • 可能隨着業務的拓展,會增長其餘商品類型。

3、非工廠模式實現

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());
}

結果:對象

截屏2021-02-24 14.13.22
  • 運行結果正常,知足業務需求。寫的還很快,可是實在難以維護。

4、工廠模式優化代碼

1. 代碼實現

1.1 定義發獎接口

public interface IAward {
    void sendAward(String userId, String awardId, Map<String, Object> map);
}

1.2 實現發獎接口

代金券blog

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("不存在此獎品類型");
    }
}
image-20210224153448134

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);
}

5、總結

工廠模式能夠避免建立者與具體的產品邏輯耦合、知足單一職責,每個業務邏輯實現都在所屬本身的類中完成知足開閉原則,無需修改調用方就能夠在程序中引入新的產品類型

但這樣也會帶來一些問題,好比有很是多的獎品類型,那麼實現的子類會急速擴張。

相關文章
相關標籤/搜索