• java设计模式-创建型模式:1-工厂方法模式


    这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤用性。

    包含工厂方法、抽象工厂、建造者、原型、单例。

    工厂方法:
    解释说明:

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

    这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    简单说就是为了提供代码结构的扩展性,屏蔽每⼀一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多ifelse 的方式。当然这可能也有⼀些缺点,⽐如需要实现的类非常多,不易于维护。

    举例说明:

    模拟多种不同商品类型得发放

    模拟积分兑换中的发放多种类型商品,假如现在我们有如下三种类型的商品接口

    类型接口
    优惠券CouponResult sendCoupon(String uId, StringcouponNumber, String uuid)
    实物Boolean deliverGoods(DeliverReq req)
    手机爱奇艺兑换卡void grantToken(String bindMobileNumber, String cardId)

    基础实体以及接口

                              └─design
    │                              ├─card
    │                              │      IQiYiCard.java
    │                              │      IQiYiCardService.java
    │                              │
    │                              ├─coupon
    │                              │      CouponInfo.java
    │                              │      CouponResult.java
    │                              │      CouponService.java
    │                              │
    │                              └─goods
    │                                      DeliverReq.java
    │                                      GoodsInfo.java
    │                                      GoodsService.java
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    从以上接⼝口来看有如下信息:

    1. 三个接口返回类型不不同,有对象类型、布尔类型、还有⼀一个空类型。
    2. 入参不不同,发放优惠券需要仿重、兑换卡需要卡ID、实物商品需要发货位置(对象中含有)。
    3. 另外可能会随着后续的业务的发展,会新增其他种商品类型。因为你所有的开发需求都是随着业务对市场的拓展而带来的。
    使用if else实现:
    │      │                  └─design
    │      │                          AwardReq.java
    │      │                          AwardRes.java
    │      │                          PrizeController.java
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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 "17600551350";
        }
    
    • 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

    缺点:后续扩展难,重构复杂,无限if else代码结构臃肿。

    使用工厂模式实现:
                      └─design
                         │  StoreFactory.java
                          │
                          └─store
                              │  ICommodity.java
                              │
                              └─impl
                                      CardCommodityService.java
                                      CouponCommodityService.java
                                      GoodsCommodityService.java
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ICommodity:自定义发奖接口

    public interface ICommodity {
    
        void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统一性。
    2. 接口的入参包括; 用户ID 、 奖品ID 、 业务ID 以及扩展字段 用于处理理发放实物商品时的收获地址。

    CardCommodityService.java 、CouponCommodityService.java、GoodsCommodityService.java分别是兑换卡,优惠券,实物商品得发放实现接口 如:
    兑换卡:

    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 "17600551350";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 从上⾯面可以看到每⼀一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
    2. 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。
    3. 在统一了了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。

    StoreFactory.java:工厂类入口

    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
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    定义了了一个工厂类,在里面按照类型实现各种商品的服务。可以非常干净整洁的处理代码,后续新增的商品在这里扩展即可。如果不用if 判断,也可以使⽤用switch 或 者map 配置结构,会让代码更更加干净。

    总结:

    优点:避免创建者与具体得产品逻辑耦合、满足单一原则每个业务有自己得单独实现类,满足开闭无需改变调用方法就可以新增不同类型。
    缺点:比如有非常多的奖品类型,那么实现的子类会极速扩张。因此也需要使用其他的模式进行优化。

  • 相关阅读:
    大数据可视化是什么?
    位图BitMap不好用?那来看看进化版本的RoaringBitmap,包您满意
    双馈风电机组备用容量控制策略研究
    内核进程初始化和创建
    Ubuntu20.04环境下Baxter机器人开发环境搭建
    重温 JavaScript 系列(4):实现异步的方法、 promise实现文件读取、Promise的并发处理
    【华为上机真题 2021】分子弹
    Hadoop
    计算机网络 | 体系结构
    MotoGP Ignition:准备好参加 Spotlight 活动!
  • 原文地址:https://blog.csdn.net/weixin_41546244/article/details/127461162