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

本文详细介绍了工厂模式在处理不同商品发放场景中的应用,通过对比传统ifelse实现与工厂模式实现,突显了工厂模式在代码结构、可扩展性和维护性上的优势。工厂模式使得奖品发放的逻辑更加模块化,降低了耦合度,并方便后续新增商品类型的扩展。同时,文章也指出了工厂模式可能存在的缺点,如子类数量过多时的维护挑战,并提出可通过其他模式优化。
摘要由CSDN通过智能技术生成

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

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

工厂方法:
解释说明:

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

这种设计模式也是 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. 入参不不同,发放优惠券需要仿重、兑换卡需要卡ID、实物商品需要发货位置(对象中含有)。
  3. 另外可能会随着后续的业务的发展,会新增其他种商品类型。因为你所有的开发需求都是随着业务对市场的拓展而带来的。
使用if else实现:
│      │                  └─design
│      │                          AwardReq.java
│      │                          AwardRes.java
│      │                          PrizeController.java

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

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

使用工厂模式实现:
                  └─design
                     │  StoreFactory.java
                      │
                      └─store
                          │  ICommodity.java
                          │
                          └─impl
                                  CardCommodityService.java
                                  CouponCommodityService.java
                                  GoodsCommodityService.java

ICommodity:自定义发奖接口

public interface ICommodity {

    void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;

}
  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. 在统一了了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。

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

}

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

总结:

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值