lizema lizema
首页
  • js

    • js
  • Git相关

    • 《Git》
  • 设计模式

    • 设计模式
  • java
  • jdk
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • HTML
  • CSS
  • 学习方法
  • 敏捷开发心得
  • 心情杂货
  • 实用技巧
  • GPT相关
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

malize

各自努力,顶峰相见。
首页
  • js

    • js
  • Git相关

    • 《Git》
  • 设计模式

    • 设计模式
  • java
  • jdk
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • HTML
  • CSS
  • 学习方法
  • 敏捷开发心得
  • 心情杂货
  • 实用技巧
  • GPT相关
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 工作中用到的设计模式
  • 享元模式
  • 单例模式
  • 原型模式
  • 备忘录模式
  • 外观模式
  • 工厂方法模式
    • 模板方法模式
    • 策略模式
    • 装饰模式
    • 访问者模式
    • 责任链模式
    • 适配器模式
    • 中介者模式
    • 设计模式
    malize
    2020-11-18
    目录

    工厂方法模式

    # 工厂方法模式

    # 一、

    1. 原理:工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

    2. 优缺点

      1. 1 优点
      • 封装对象创建:工厂方法将对象的创建逻辑封装在子类中,使得客户端代码与具体的实现类解耦,从而提高了系统的灵活性和可扩展性。
      • 遵循开放/封闭原则:当需要添加新的产品时,只需要创建新的工厂子类,不需要修改现有的代码,符合开放/封闭原则(OCP)。
      • 代码复用性:通过继承和多态性,工厂方法模式可以复用代码,减少重复代码的编写。
      • 代码维护性:由于工厂方法模式将创建逻辑集中在一起,修改时只需要调整工厂方法,而不需要在多个地方修改创建代码,从而提高了代码的可维护性。
      • 支持对象的延迟初始化:工厂方法模式可以支持对象的延迟初始化,即对象的创建可以在需要时才进行,从而节省系统资源。
      1. 2 缺点
      • 增加系统复杂度:工厂方法模式引入了额外的类和接口,增加了系统的复杂度,尤其是在系统中有许多不同的产品和对应的工厂时,这种复杂性尤为明显。
      • 可能导致类的爆炸:由于每一个具体产品都需要一个对应的具体工厂类,这可能导致类的数量急剧增加,从而使得系统维护变得困难。
      • 需要较高的理解成本:相对于简单的实例化方式,工厂方法模式的理解和实现需要更高的技术水平和理解成本,特别是对于初学者而言。
      • 实现困难:对于一些简单的对象创建,使用工厂方法模式可能显得过于复杂和不必要,从而降低开发效率。
    3. 适用场景

      • 需要灵活创建对象的场景:当系统中的对象创建逻辑复杂或对象种类繁多时,工厂方法模式能够提供很好的灵活性。
      • 需要封装具体实现的场景:当系统不需要关心具体产品的创建过程,而只需要关注产品的接口时,工厂方法模式是一个很好的选择。
      • 可能需要扩展的场景:当系统需要经常添加新的产品时,工厂方法模式能够方便地扩展系统而不影响已有代码。

    # 二、优化前代码

    1. 工厂结构
    itstack-demo-design-1-01
    └── src
        ├── main
        │   └── java
        │       └── org.itstack.demo.design
        │           ├── AwardReq.java
        │           ├── AwardRes.java
        │           └── PrizeController.java 
        └── test
             └── java
                 └── org.itstack.demo.design.test
                     └── ApiTest.java
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1. 奖品控制类
    public class PrizeController {
    
        private Logger logger = LoggerFactory.getLogger(PrizeController.class);
    
        public AwardRes awardToUser(AwardReq req) {
            String reqJson = JSON.toJSONString(req);
            AwardRes awardRes = null;
            try {
                logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
                // 按照不同类型方法商品[1优惠券、2实物商品、3第三方兑换卡(爱奇艺)]
                if (req.getAwardType() == 1) {
                    CouponService couponService = new CouponService();
                    CouponResult couponResult = couponService.sendCoupon(req.getuId(), req.getAwardNumber(), req.getBizId());
                    if ("0000".equals(couponResult.getCode())) {
                        awardRes = new AwardRes("0000", "发放成功");
                    } else {
                        awardRes = new AwardRes("0001", couponResult.getInfo());
                    }
                } else if (req.getAwardType() == 2) {
                    GoodsService goodsService = new GoodsService();
                    DeliverReq deliverReq = new DeliverReq();
                    deliverReq.setUserName(queryUserName(req.getuId()));
                    deliverReq.setUserPhone(queryUserPhoneNumber(req.getuId()));
                    deliverReq.setSku(req.getAwardNumber());
                    deliverReq.setOrderId(req.getBizId());
                    deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));
                    deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
                    deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
                    Boolean isSuccess = goodsService.deliverGoods(deliverReq);
                    if (isSuccess) {
                        awardRes = new AwardRes("0000", "发放成功");
                    } else {
                        awardRes = new AwardRes("0001", "发放失败");
                    }
                } else if (req.getAwardType() == 3) {
                    String bindMobileNumber = queryUserPhoneNumber(req.getuId());
                    IQiYiCardService iQiYiCardService = new IQiYiCardService();
                    iQiYiCardService.grantToken(bindMobileNumber, req.getAwardNumber());
                    awardRes = new AwardRes("0000", "发放成功");
                }
                logger.info("奖品发放完成{}。", req.getuId());
            } catch (Exception e) {
                logger.error("奖品发放失败{}。req:{}", req.getuId(), reqJson, e);
                awardRes = new AwardRes("0001", e.getMessage());
            }
    
            return awardRes;
        }
    
        private String queryUserName(String uId) {
            return "花花";
        }
    
        private String queryUserPhoneNumber(String uId) {
            return "15200101232";
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    1. 测试验证
    @Test
    public void test_awardToUser() {
        PrizeController prizeController = new PrizeController();
        System.out.println("\r\n模拟发放优惠券测试\r\n");
        // 模拟发放优惠券测试
        AwardReq req01 = new AwardReq();
        req01.setuId("10001");
        req01.setAwardType(1);
        req01.setAwardNumber("EGM1023938910232121323432");
        req01.setBizId("791098764902132");
        AwardRes awardRes01 = prizeController.awardToUser(req01);
        logger.info("请求参数:{}", JSON.toJSON(req01));
        logger.info("测试结果:{}", JSON.toJSON(awardRes01));
        System.out.println("\r\n模拟方法实物商品\r\n");
        // 模拟方法实物商品
        AwardReq req02 = new AwardReq();
        req02.setuId("10001");
        req02.setAwardType(2);
        req02.setAwardNumber("9820198721311");
        req02.setBizId("1023000020112221113");
        Map<String,String> extMap = new HashMap<String,String>();
        extMap.put("consigneeUserName", "谢飞机");
        extMap.put("consigneeUserPhone", "15200292123");
        extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
        req02.setExtMap(extMap);
    
        commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap);
    
        AwardRes awardRes02 = prizeController.awardToUser(req02);
        logger.info("请求参数:{}", JSON.toJSON(req02));
        logger.info("测试结果:{}", JSON.toJSON(awardRes02));
        System.out.println("\r\n第三方兑换卡(爱奇艺)\r\n");
        AwardReq req03 = new AwardReq();
        req03.setuId("10001");
        req03.setAwardType(3);
        req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");
        AwardRes awardRes03 = prizeController.awardToUser(req03);
        logger.info("请求参数:{}", JSON.toJSON(req03));
        logger.info("测试结果:{}", JSON.toJSON(awardRes03));
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    模拟发放优惠券测试
    
    00:31:58.494 [main] INFO  o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"EGM1023938910232121323432","awardType":1,"bizId":"791098764902132","uId":"10001"}
    模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
    00:31:58.500 [main] INFO  o.i.demo.design.PrizeController - 奖品发放完成10001。
    00:31:58.502 [main] INFO  org.itstack.demo.test.ApiTest - 请求参数:{"uId":"10001","bizId":"791098764902132","awardNumber":"EGM1023938910232121323432","awardType":1}
    00:31:58.504 [main] INFO  org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
    
    模拟方法实物商品
    
    00:31:58.505 [main] INFO  o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"9820198721311","awardType":2,"bizId":"1023000020112221113","extMap":{"consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"},"uId":"10001"}
    模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
    00:31:58.507 [main] INFO  o.i.demo.design.PrizeController - 奖品发放完成10001。
    00:31:58.508 [main] INFO  org.itstack.demo.test.ApiTest - 请求参数:{"extMap":{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"},"uId":"10001","bizId":"1023000020112221113","awardNumber":"9820198721311","awardType":2}
    00:31:58.508 [main] INFO  org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
    
    第三方兑换卡(爱奇艺)
    
    00:31:58.508 [main] INFO  o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3,"uId":"10001"}
    模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio
    00:31:58.509 [main] INFO  o.i.demo.design.PrizeController - 奖品发放完成10001。
    00:31:58.509 [main] INFO  org.itstack.demo.test.ApiTest - 请求参数:{"uId":"10001","awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3}
    00:31:58.509 [main] INFO  org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    # 三、优化后

    1. 工厂结构
    itstack-demo-design-1-02
    └── src
        ├── main
        │   └── java
        │       └── org.itstack.demo.design
        │           ├── store    
        │           │   ├── impl
        │           │   │   ├── CardCommodityService.java
        │           │   │   ├── CouponCommodityService.java 
        │           │   │   └── GoodsCommodityService.java  
        │           │   └── ICommodity.java
        │           └── StoreFactory.java 
        └── test
             └── java
                 └── org.itstack.demo.design.test
                     └── ApiTest.java
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    1. UML图

    2. 发奖接口

    public interface ICommodity {
    
        void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
    
    }
    
    1
    2
    3
    4
    5
    1. 实现奖品发放接口(发奖工厂)

      优惠劵

    public class CouponCommodityService implements ICommodity {
    
        private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
    
        private CouponService couponService = new CouponService();
    
        public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
            CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);
            logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
            logger.info("测试结果[优惠券]:{}", JSON.toJSON(couponResult));
            if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    ​ 事物商品

    public class GoodsCommodityService implements ICommodity {
    
        private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);
    
        private GoodsService goodsService = new GoodsService();
    
        public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
            DeliverReq deliverReq = new DeliverReq();
            deliverReq.setUserName(queryUserName(uId));
            deliverReq.setUserPhone(queryUserPhoneNumber(uId));
            deliverReq.setSku(commodityId);
            deliverReq.setOrderId(bizId);
            deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));
            deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
            deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
    
            Boolean isSuccess = goodsService.deliverGoods(deliverReq);
    
            logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
            logger.info("测试结果[优惠券]:{}", isSuccess);
    
            if (!isSuccess) throw new RuntimeException("实物商品发放失败");
        }
    
        private String queryUserName(String uId) {
            return "花花";
        }
    
        private String queryUserPhoneNumber(String uId) {
            return "15200101232";
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    ​ 第三方兑换卡

    public class CardCommodityService implements ICommodity {
    
        private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
    
        // 模拟注入
        private IQiYiCardService iQiYiCardService = new IQiYiCardService();
    
        public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
            String mobile = queryUserMobile(uId);
            iQiYiCardService.grantToken(mobile, bizId);
            logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
            logger.info("测试结果[爱奇艺兑换卡]:success");
        }
    
        private String queryUserMobile(String uId) {
            return "15200101232";
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    1. 创建商店工厂
    public class StoreFactory {
    
        public ICommodity getCommodityService(Integer commodityType) {
            if (null == commodityType) return null;
            if (1 == commodityType) return new CouponCommodityService();
            if (2 == commodityType) return new GoodsCommodityService();
            if (3 == commodityType) return new CardCommodityService();
            throw new RuntimeException("不存在的商品服务类型");
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1. 测试验证
    @Test
    public void test_commodity() throws Exception {
        StoreFactory storeFactory = new StoreFactory();
        // 1. 优惠券
        ICommodity commodityService_1 = storeFactory.getCommodityService(1);
        commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
        // 2. 实物商品
        ICommodity commodityService_2 = storeFactory.getCommodityService(2);
        
        Map<String,String> extMap = new HashMap<String,String>();
        extMap.put("consigneeUserName", "谢飞机");
        extMap.put("consigneeUserPhone", "15200292123");
        extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
    
        commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap);
        // 3. 第三方兑换卡(爱奇艺)
        ICommodity commodityService_3 = storeFactory.getCommodityService(3);
        commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
    }
        
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    ​ 结果:

    模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
    22:48:10.922 [main] INFO  o.i.d.d.s.i.CouponCommodityService - 请求参数[优惠券] => uId:10001 commodityId:EGM1023938910232121323432 bizId:791098764902132 extMap:null
    22:48:10.957 [main] INFO  o.i.d.d.s.i.CouponCommodityService - 测试结果[优惠券]:{"code":"0000","info":"发放成功"}
    模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
    22:48:10.962 [main] INFO  o.i.d.d.s.impl.GoodsCommodityService - 请求参数[优惠券] => uId:10001 commodityId:9820198721311 bizId:1023000020112221113 extMap:{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"}
    22:48:10.962 [main] INFO  o.i.d.d.s.impl.GoodsCommodityService - 测试结果[优惠券]:true
    模拟发放爱奇艺会员卡一张:15200101232,null
    22:48:10.963 [main] INFO  o.i.d.d.s.impl.CardCommodityService - 请求参数[爱奇艺兑换卡] => uId:10001 commodityId:AQY1xjkUodl8LO975GdfrYUio bizId:null extMap:null
    22:48:10.963 [main] INFO  o.i.d.d.s.impl.CardCommodityService - 测试结果[爱奇艺兑换卡]:success
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 四、实现方式

    1. 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。
    2. 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。
    3. 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将**创建产品的代码移入工厂方法 **。
    4. 现在, 为工厂方法中的每种产品编写一个创建者子类, 然后在子类中重写工厂方法, 并将基本方法中的相关创建代码移动到工厂方法中。
    5. 如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数。

    结合案例理解

    1. 源代码产品是GoodsService、CouponService、IQiYiCardService(可以加个接口Service声明对这些产品都有意义的方法)
    2. 工厂接口ICommodity,子工厂CouponCommodityService、GoodsCommodityService、CardCommodityService
    3. 各个子工厂实现对各自产品的操作
    4. 操作工厂getCommodityService(Integer commodityType),根据commodityType获取你想要的工厂
    编辑 (opens new window)
    #重学Java设计模式
    外观模式
    模板方法模式

    ← 外观模式 模板方法模式→

    最近更新
    01
    其他
    02
    其他
    03
    名人总结
    08-27
    更多文章>
    Theme by Vdoing
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式