前言

参考:虫洞栈

工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类
决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子
类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的
只是知道调用即可,同时,这也是去掉众多 ifelse 的方式。当然这可能也有一些缺点,比如需要实现
的类非常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使用中,逐步
降低。

模拟发奖多种商品

现在我们有如下三种类型的商品接口:

序号 类型 接口
1 优惠券 CouponResult sendCoupon(String uId, String
couponNumber, String uuid)
2 实物商品 Boolean deliverGoods(DeliverReq req)
3 第三方爱奇艺兑换卡 void grantToken(String bindMobileNumber, String cardId)
  1. 定义发奖接口

    import java.util.Map;/*** 发奖接口** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 14:50*/
    public interface ICommodityService {void sendCommodity(String uId, String commodityId, String bizId,Map<String, String> extMap) throws Exception;
    }
    

    所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统一性。
    接口的入参包括:用户ID 、 奖品ID 、 业务ID 以及 扩展字段 用于处理发放实物商品时的收获址。

  2. 实现奖品发放接口
    2.1 优惠券

    package com.xiya.designpatterns.store.impl;import com.alibaba.fastjson.JSON;
    import com.xiya.designpatterns.coupon.CouponResult;
    import com.xiya.designpatterns.coupon.CouponService;
    import com.xiya.designpatterns.store.ICommodityService;
    import lombok.extern.slf4j.Slf4j;import java.util.Map;/*** 优惠券** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 14:53*/
    @Slf4j
    public class CouponCommodityService implements ICommodityService {private CouponService couponService = new CouponService();@Overridepublic void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);log.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));log.info("测试结果[优惠券]:{}", JSON.toJSON(couponResult));if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());}
    }
    

    2.2 实物商品

    package com.xiya.designpatterns.store.impl;import com.alibaba.fastjson.JSON;
    import com.xiya.designpatterns.goods.DeliverReq;
    import com.xiya.designpatterns.goods.GoodsService;
    import com.xiya.designpatterns.store.ICommodityService;
    import lombok.extern.slf4j.Slf4j;import java.util.Map;/*** 实物奖励** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 15:14*/
    @Slf4j
    public class GoodsCommodityService implements ICommodityService {private GoodsService goodsService = new GoodsService();@Overridepublic 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);log.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));log.info("测试结果[优惠券]:{}", isSuccess);if (!isSuccess) throw new RuntimeException("实物商品发放失败");}private String queryUserName(String uId) {return "花花";}private String queryUserPhoneNumber(String uId) {return "15200101232";}
    }
    

    2.3 第三⽅兑换卡

    package com.xiya.designpatterns.store.impl;import com.alibaba.fastjson.JSON;
    import com.xiya.designpatterns.card.IQiYiCardService;
    import com.xiya.designpatterns.store.ICommodityService;
    import lombok.extern.slf4j.Slf4j;import java.util.Map;/*** 第三方兑换卡** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 15:22*/
    @Slf4j
    public class CardCommodityService implements ICommodityService {// 模拟注⼊private IQiYiCardService iQiYiCardService = new IQiYiCardService();@Overridepublic void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {String mobile = queryUserMobile(uId);iQiYiCardService.grantToken(mobile, bizId);log.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));log.info("测试结果[爱奇艺兑换卡]:success");}private String queryUserMobile(String uId) {return "15200101232";}
    }
    

    从上面可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
    后续在新增的奖品只需要按照此结构进⾏填充即可,⾮常易于维护和扩展。
    在统一了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。

  3. 创建商店⼯⼚

    package com.xiya.designpatterns.store;import com.xiya.designpatterns.store.impl.CardCommodityService;
    import com.xiya.designpatterns.store.impl.CouponCommodityService;
    import com.xiya.designpatterns.store.impl.GoodsCommodityService;
    import lombok.extern.slf4j.Slf4j;/*** 商店⼯⼚** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 16:00*/
    @Slf4j
    public class StoreFactory {public ICommodityService 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 配置结构,会让代码更加干净。

  4. 测试验证

    package com.xiya.designpatterns.store;import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;import java.util.HashMap;
    import java.util.Map;/*** @author ZHANGCHAO* @version 1.0.0* @date 2022/1/28 16:04*/
    @Slf4j
    class StoreFactoryTest {@Testvoid test_factory() throws Exception {StoreFactory storeFactory = new StoreFactory();// 1.优惠券ICommodityService commodityService1 = storeFactory.getCommodityService(1);commodityService1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);// 2.实物商品ICommodityService commodityService2 = storeFactory.getCommodityService(2);Map<String, String> extMap = new HashMap<>();extMap.put("consigneeUserName", "谢飞机");extMap.put("consigneeUserPhone", "15200292123");extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");commodityService2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<>() {{put("consigneeUserName", "谢飞机");put("consigneeUserPhone", "15200292123");put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");}});// 3.第三方兑换卡(爱奇艺)ICommodityService commodityService3 = storeFactory.getCommodityService(3);commodityService3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);}}
    

    运行结果正常,既满⾜了业务产品需求,也满足了自己对代码的追求。这样的代码部署上线运行,内心不会恐慌,不会觉得半夜会有电话。

附项目结构图:

总结

  • 从上到下的优化来看,工厂方法模式并不复杂,甚至这样的开发结构在你有所理解后,会发现更加简单了。
  • 那么这样的开发的好处知道后,也可以总结出来它的优点; 避免创建者与具体的产品逻辑耦合 、 满足单一职责,每一个业务逻辑实现都在所属自己的类中完成 、 满足开闭原则,无需更改使用调用方就可以在程序中引入新的产品类型 。但这样也会带来一些问题,比如有非常多的奖品类型,那么实现的子类会极速扩张。因此也需要使用其他的模式进行优化,这些在后续的设计模式中会逐步涉及到。
  • 从案例入手看设计模式往往要比看理论学的更加容易,因为案例是缩短理论到上手的最佳方式,如果你已经有所收获,一定要去尝试实操。

通往架构师之路系列之Java设计模式(二)工厂方法模式相关推荐

  1. 一文叫你弄懂Java设计模式之工厂方法模式:图解+日志记录器代码实例

    文章目录 详解Java设计模式之工厂方法模式 案例引入工厂方法模式 工厂方法模式 定义 案例分析 UML类图分析 代码分析 工厂方法的重载 工厂方法的隐藏 模式优点 模式缺点 模式适用环境 详解Jav ...

  2. JAVA架构师之路十六:设计模式之责任链模式

    JAVA架构师之路十五:设计模式之策略模式 责任链模式 1. 责任链模式 2. 登陆案例 3. 登陆案例优化 人生的游戏不在于拿了一副好牌,而在于怎样去打好坏牌,世上没有常胜将军,勇于超越自我者才能得 ...

  3. Java设计模式之工厂方法模式与抽象工厂模式

    2019独角兽企业重金招聘Python工程师标准>>> 一.前期回顾 上一篇<Java设计模式之单例模式>详细介绍了单例模式,介绍了单例模式的使用场景,优缺点,同时也写了 ...

  4. Java 设计模式之工厂方法模式

    本文为笔者学习<Head First设计模式>的笔记,并加入笔者自己的理解和归纳总结 工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类 ...

  5. 最简单java设计模式:工厂方法模式

    前言 在前一篇文章讲解了一下简单工厂模式,在上篇文章中,我们也讲到了简单工厂模式的缺点,就是不满足开闭原则,这对于软件的设计来说,是不太好的,而下面讲解的工厂方法模式,正是为了弥补简单工厂模式的缺点, ...

  6. Java设计模式 之 工厂方法模式

    1. 使用设计模式的好处:可提高代码的重复性,让代码更容易被他人理解,保证代码的可靠性. 2. 工厂模式定义:就是创建一个工厂类来创建你需要的类,工厂模式包括工厂模式和抽象工厂模式,抽象工厂模式是工厂 ...

  7. java设计模式通俗_通俗易懂的Java设计模式之工厂方法模式

    一 .工厂方法(Factory Method)模式 工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负 ...

  8. java设计模式之工厂方法模式

    工厂方法模式是类的创建模式,又叫虚拟构造模式(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式. 工厂方法模式的用意是定义一个创建产品的工厂接口, ...

  9. java设计模式1——工厂方法模式(Factory Method)

    工厂方法模式分为三种: 1.普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建.首先看下关系图: 举例如下:(我们举一个发送邮件和短信的例子) 首先,创建二者的共同接口: pub ...

最新文章

  1. 《1---关于解决MySQL在控制台插入中文乱码问题》
  2. AngularUI Router
  3. 如何在代码中将menu隐藏_如何在40行代码中将机器学习用于光学/光子学应用
  4. 告别自注意力,谷歌为Transformer打造新内核Synthesizer
  5. Ansys节点数据批量一键导出脚本生成CSV (ansys数据导出利用matlab脚本)
  6. 如何“神还原”数据中心? 阿里联合NTU打造了工业级精度的仿真沙盘!
  7. [转载]JavaScript 的轻框架开发
  8. Dropout也能自动化了,谷歌Quoc Le等人利用强化学习自动找寻模型专用Dropout
  9. 响应信息有json和html,获取HTML响应而不是Json响应
  10. Android 系统调试(2)---android debug 方法
  11. Windows mobile 下读取手机SIM卡信息
  12. OWASP Hakcing Lab在线漏洞环境
  13. python tqdm模块的简单使用
  14. 公有云安全修炼之路,郭靖和周伯通带你走
  15. 阿里easyexcel读取excel流程初探
  16. 一键查询自己名下所有微信账户
  17. 创业教父马云的经典语录
  18. Microchip最新推出的ATMEGA4809-XPRO开发板简介
  19. 荣耀3路由器设置虚拟服务器,荣耀路由3怎么设置上网?(电脑)
  20. js中appendChild的用法

热门文章

  1. 分享一组超喜欢的粉色樱花高清壁纸~
  2. Qt串口通信实时曲线上位机源代码
  3. OpenStack计算节点nove启动报错
  4. 百度AICA迎来毕业季,55位新晋“首席AI架构师”推进产业智能化
  5. c 中服务器多次接受消息,c/s模拟高并发服务器端线程池接收问题
  6. 多设备时设置default serial的方法
  7. 60天造个火箭给你玩玩,你想要不?
  8. DDK 2003 SP1 官方下载地址
  9. Unikernels 解读
  10. 转载:整车CAN网络介绍---CAN--PT CAN--Chassis CAN--Body CAN--Info CAN