设计模式-创建者模式-工厂方法模式

工厂方法模式

案例:模拟发放奖品

三种奖品:优惠券,实物商品,爱奇艺兑换卡

未重构前的结构:

其中针对不同的奖品,会有不同逻辑的service方法,每个service方法的入参和返回值都不一样

  • 模拟实物商品服务(返回是否发放成功的标志)

    public class GoodsService {
    
        public Boolean deliverGoods(DeliverReq req) {
            System.out.println("模拟发货实物商品一个:" + JSON.toJSONString(req));
            return true;
        }
    
    }
    
  • 模拟优惠券服务(返回针对优惠券的返回结果:code,info)

    public class CouponService {
    
        public CouponResult sendCoupon(String uId, String couponNumber, String uuid) {
            System.out.println("模拟发放优惠券一张:" + uId + "," + couponNumber + "," + uuid);
            return new CouponResult("0000", "发放成功");
        }
    
    }
    
  • 模拟爱奇艺会员卡服务(只输出结果,无返回值)

    public void grantToken(String bindMobileNumber, String cardId) {
        System.out.println("模拟发放爱奇艺会员卡一张:" + bindMobileNumber + "," + cardId);
    }
    
未重构前处理逻辑的代码结构

AwardReq是入参,统一奖品的信息形式

AwardRes是出参,统一发放奖品的返回结果形式

PrizeController的模拟发放奖品的代码逻辑
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";
    }

}

通过AwardReq入参的awardType来判断发放什么奖品,然后执行对应奖品的对应的service服务方法

可以看出,如果只是通过iflese判断来执行不同的service方法,每一次增加一种奖品类型,都要在controller方法里新增一个判断并且在判断里也要重新填写新的service的方法,并且还要增加针对新奖品的service方法的逻辑

重构后的代码结构如下:

  • 首先统一service接口

    public interface ICommodity {
    
        void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
    
    }
    
  • 然后分别针对不同的奖品来实现上述service接口,在这些实现方法里注入各自的单独的service方法,然后在实现方法里调用这些service方法

    • 发放优惠券奖品:

      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());
          }
      
      }
      
    • 发放爱奇艺兑换卡片奖品:

      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";
          }
      
      }
      
    • 发放实物奖品:

      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";
          }
      
      }
      
    • 创建工厂StoreFactory,在工厂里边装载上述的统一接口:Icommodity,然后根据入参的type判断返回什么具体的奖品服务实现方法

      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("不存在的商品服务类型");
          }
      
      }
      

    经过工厂方法重构之后,易于扩展,新增一个奖品类型,只需要新增一个实现Icommodity的服务方法,然后在工厂里增加一个判断返回该服务类即可,优点是: 避免创建者与具体的产品逻辑耦合 、 满足单一职责,每⼀个业务逻辑实现都在所属⾃自⼰己的类中完成 、 满⾜足开闭原则

原文地址:https://www.cnblogs.com/RealGang/p/14611128.html