• 四句话,让我掌握了工厂模式!


    写在前面:

      初为职场新手,难免会把代码写的一把梭。遇到业务的时候直接if-else干。但是本着不想被开除的原则,还是学习一下设计模式,今天来看看设计模式之工厂模式。

    用需求引出代码:

     最近正在开发抽奖系统,系统当中有个业务是发奖业务,奖品有优惠券,实物商品,第三方优惠券。当用户抽中对应的奖品之后,我们需要有对应的方法来发奖。针对这样的业务我们先来看一下代码一把梭的情况。

    工程结构:

     

     

     

    ⼯程结构上⼀个⼊参对象 AwardReq 、⼀个出参对象 AwardRes ,以及⼀个接⼝类
    PrizeController
     
    先看看ifelse实现需求
    复制代码
    public class PrizeController {

    private Logger logger = LoggerFactory.getLogger(PrizeController.class);

    /**
    * 给用户发奖
    * @param req
    * @return
    */
    public AwardRes awardToUser(AwardReq req) {
    // 把req转为json格式好处理
    String reqJson = JSON.toJSONString(req);
    // 初始化返回结果为空
    AwardRes awardRes = null;
    try {
    logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
    // 按照不同类型方法商品[1优惠券、2实物商品、3第三方兑换卡(爱奇艺)]
    if (req.getAwardType() == 1) {
    // 这是1-00当中的类
    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"));
    // 调用方法来发实物商品,如果发成功返回true
    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";
    }

    }
    复制代码

    如上的代码有什么问题?

    • 业务不变还好(基本不可能),业务如果变更,那就得去修改这个类中的代码,违背开闭原则(对修改关闭,对扩展开放),违背最小知识原则(一个类只干一件事)。
    • 被组长知道了,绩效3.0跑不了,假如离职下一个同事接手了估计直骂娘。

     

     
    接下来我们使用工厂模式重构代码,设计模式没什么可怕的,就是听起来高大上而已,要从战术上藐视他,多练练就学会了。
     
    工程结构:

    为什么叫工厂模式呢?

    • 工厂是生产东西的,买家可以直接去工厂拿东西 ,并且工厂能生产的东西是多样性的。比如食品工厂,可以生产旺旺雪饼,雪糕,华夫饼。那我们需要去进货的时候,只需要带着需求清单去找工厂就行。

    调用关系图:

     

     具体代码实现:

    发送商品接口:

    public interface ICommodity {
      void sendCommodity(String uId, String commodityId, String bizId,
                          Map extMap) throws Exception; 
    }
    • 所有的奖品⽆论是实物、虚拟还是第三⽅,都需要通过我们的程序实现此接⼝进⾏处理,以保证最终⼊参出参的统⼀性。
    • 接⼝的⼊参包括:⽤户ID 、 奖品ID 、 业务ID 以及 扩展字段⽤于处理发放实物商品时的收获地址。
     
    发优惠券:
    复制代码
    public class CouponCommodityService implements ICommodity {

    private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);

    private CouponService couponService = new CouponService();

    @Override
    public void sendCommodity(String uId, String commodityId, String bizId, Map 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());
    }

    }
    复制代码

     

    发实物商品
     
    复制代码
    public class GoodsCommodityService implements ICommodity {

    private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);

    private GoodsService goodsService = new GoodsService();

    @Override
    public void sendCommodity(String uId, String commodityId, String bizId, Map 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";
    }

    }
    复制代码
    发第三⽅兑换卡
    复制代码
    public class CardCommodityService implements ICommodity {
    
        private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
    
        // 模拟注入
        private IQiYiCardService iQiYiCardService = new IQiYiCardService();
    
        @Override
        public void sendCommodity(String uId, String commodityId, String bizId, Map 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";
        }
    
    }
    复制代码
    • 从上⾯可以看到每⼀种奖品的实现都包括在⾃⼰的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。后续在新增的奖品只需要按照此结构进⾏填充即可,⾮常易于维护和扩展。在统⼀了⼊参以及出参后,调⽤⽅不在需要关⼼奖品发放的内部逻辑,按照统⼀的⽅式即可处理。
     
    最重要的工厂:
    复制代码
    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("不存在的商品服务类型");
        }
    
    }
    复制代码
    • 这⾥我们定义了⼀个商店的⼯⼚类,在⾥⾯按照类型实现各种商品的服务。可以⾮常⼲净整洁的处理你的代码,后续新增的商品在这⾥扩展即可。如果你不喜欢 if 判断,也可以使⽤ switch 或者 map 配置结构,会让代码更加⼲净
    总结:
    • 从上到下的优化来看,⼯⼚⽅法模式并不复杂,使用工厂模式之后代码结构更加清晰了,但是还是要根据业务和开发工期来动态决策具体该怎么写。
     
     
     
     
     
     
     
     
     
     
     
     

     

  • 相关阅读:
    Sqoop数据导入操作
    【文件管理】关于上传下载文件的设计
    JAVA+MySQL 图书馆借阅信息管理系统
    电力电子转战数字IC20220820day65——uvm实战1B
    144. 授人以渔 - 如何查找 SAP UI5 官网上没有提到的控件属性的使用明细
    Qt重启windows服务
    关于工作中爬取网站的一些思路记录
    Kubernetes与Docker和Containerd是个什么关系
    Transformer预测 | Pytorch实现基于Transformer的时间序列预测(含单步与多步实验)
    Android 10.0 Launcher3桌面显示多个相同app图标的解决办法
  • 原文地址:https://www.cnblogs.com/YXBLOGXYY/p/16583956.html