做者:小傅哥
博客:https://bugstack.cnhtml
沉淀、分享、成长,让本身和他人都能有所收获!😄
好看的代码千篇一概,恶心的程序升职加薪。
java
该说不说几乎是程序员就都知道或者了解设计模式,但大部分小伙伴写代码老是习惯于一把梭。不管多少业务逻辑就一个类几千行,这样的开发也能够概括为三步;定义属性、建立方法、调用展现,Done!只不过开发一时爽,重构火葬场。git
好的代码不仅为了完成现有功能,也会考虑后续扩展。在结构设计上松耦合易读易扩展,在领域实现上高内聚不对外暴漏实现细节不被外部干扰。而这就有点像家里三居(MVC)室、四居(DDD)室的装修,你不会容许几十万的房子把走线水管裸漏在外面,也不会容许把马桶放到厨房,炉灶安装到卫生间。程序员
谁发明了设计模式? 设计模式的概念最先是由 克里斯托佛·亚历山大
在其著做 《建筑模式语言》
中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,埃里希·伽玛
、 约翰·弗利赛德斯
、 拉尔夫·约翰逊
和 理查德·赫尔姆
这四位做者接受了模式的概念。 1994 年, 他们出版了 《设计模式: 可复用面向对象软件的基础》
一书, 将设计模式的概念应用到程序开发领域中。 github
其实有一部分人并无仔细阅读过设计模式的相关书籍和资料,但依旧能够编写出优秀的代码。这主要是因为在通过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,一样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到相似的经历,在学习一些框架的源码时,发现它里的某些设计和你在作开发时同样。编程
我怎么学不会设计模式? 钱也花了,书也买了。代码仍是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千千米,你能记住的只是理论,想上道依旧很慌!设计模式
因此,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给本身。固然这里还须要多加练习,必定是人车合一,才能站在设计模式的基础上构建出更加合理的代码。架构
涉及工程三个,能够经过关注公众号:bugstack虫洞栈
,回复源码下载
获取。你会得到一个链接打开后的列表中编号18
:itstack-demo-design
框架
工程 | 描述 |
---|---|
itstack-demo-design-1-00 | 场景模拟工程,用于提供三组不一样奖品的发放接口 |
itstack-demo-design-1-01 | 使用一坨代码实现业务需求,也是对ifelse的使用 |
itstack-demo-design-1-02 | 经过设计模式优化改造代码,产生对比性从而学习 |
工厂模式又称工厂方法模式,是一种建立型设计模式,其在父类中提供一个建立对象的方法, 容许子类决定实例化对象的类型。ide
这种设计模式也是 Java 开发中最多见的一种模式,它的主要意图是定义一个建立对象的接口,让其子类本身决定实例化哪个工厂类,工厂模式使其建立过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每个功能类中的具体实现逻辑。让外部能够更加简单的只是知道调用便可,同时,这也是去掉众多ifeslse
的方式。固然这可能也有一些缺点,好比须要实现的类很是多,如何去维护,怎样减低开发成本。但这些问题均可以在后续的设计模式结合使用中,逐步下降。
为了可让整个学习的案例更加贴近实际开发,这里模拟互联网中在营销场景下的业务。因为营销场景的复杂、多变、临时的特性,它所须要的设计须要更加深刻,不然会常常面临各类紧急CRUD操做,从而让代码结构混乱不堪,难以维护。
在营销场景中常常会有某个用户作了一些操做;打卡、分享、留言、邀请注册等等,进行返利积分,最后经过积分在兑换商品,从而促活和拉新。
那么在这里咱们模拟积分兑换中的发放多种类型商品,假如如今咱们有以下三种类型的商品接口;
序号 | 类型 | 接口 |
---|---|---|
1 | 优惠券 | CouponResult sendCoupon(String uId, String couponNumber, String uuid) |
2 | 实物商品 | Boolean deliverGoods(DeliverReq req) |
3 | 第三方爱奇艺兑换卡 | void grantToken(String bindMobileNumber, String cardId) |
从以上接口来看有以下信息:
若是不考虑任何扩展性,只为了尽快知足需求,那么对这么几种奖励发放只需使用ifelse语句判断,调用不一样的接口便可知足需求。可能这也是一些刚入门编程的小伙伴,经常使用的方式。接下来咱们就先按照这样的方式来实现业务的需求。
itstack-demo-design-1-01 └── src ├── main │ └── java │ └── org.itstack.demo.design │ ├── AwardReq.java │ ├── AwardRes.java │ └── PrizeController.java └── test └── java └── org.itstack.demo.design.test └── ApiTest.java
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"; } }
ifelse
很是直接的实现出来业务需求的一坨代码,若是仅从业务角度看,研发如期甚至提早实现了功能。ifelse
还会继续增长。写一个单元测试来验证上面编写的接口方式,养成单元测试的好习惯会为你加强代码质量。
编写测试类:
@Test public void test_awardToUser() { PrizeController prizeController = new PrizeController(); System.out.println("\r\n模拟发放优惠券测试\r\n"); // 模拟发放优惠券测试 AwardReq req01 = new AwardReq(); req01.setuId("10001"); req01.setAwardType(1); req01.setAwardNumber("EGM1023938910232121323432"); req01.setBizId("791098764902132"); AwardRes awardRes01 = prizeController.awardToUser(req01); logger.info("请求参数:{}", JSON.toJSON(req01)); logger.info("测试结果:{}", JSON.toJSON(awardRes01)); System.out.println("\r\n模拟方法实物商品\r\n"); // 模拟方法实物商品 AwardReq req02 = new AwardReq(); req02.setuId("10001"); req02.setAwardType(2); req02.setAwardNumber("9820198721311"); req02.setBizId("1023000020112221113"); Map<String,String> extMap = new HashMap<String,String>(); extMap.put("consigneeUserName", "谢飞机"); extMap.put("consigneeUserPhone", "15200292123"); extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"); req02.setExtMap(extMap); commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap); AwardRes awardRes02 = prizeController.awardToUser(req02); logger.info("请求参数:{}", JSON.toJSON(req02)); logger.info("测试结果:{}", JSON.toJSON(awardRes02)); System.out.println("\r\n第三方兑换卡(爱奇艺)\r\n"); AwardReq req03 = new AwardReq(); req03.setuId("10001"); req03.setAwardType(3); req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio"); AwardRes awardRes03 = prizeController.awardToUser(req03); logger.info("请求参数:{}", JSON.toJSON(req03)); logger.info("测试结果:{}", JSON.toJSON(awardRes03)); }
结果:
模拟发放优惠券测试 22:17:55.668 [main] INFO o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"EGM1023938910232121323432","awardType":1,"bizId":"791098764902132","uId":"10001"} 模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132 22:17:55.671 [main] INFO o.i.demo.design.PrizeController - 奖品发放完成10001。 22:17:55.673 [main] INFO org.itstack.demo.test.ApiTest - 请求参数:{"uId":"10001","bizId":"791098764902132","awardNumber":"EGM1023938910232121323432","awardType":1} 22:17:55.674 [main] INFO org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"} 模拟方法实物商品 22:17:55.675 [main] INFO o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"9820198721311","awardType":2,"bizId":"1023000020112221113","extMap":{"consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"},"uId":"10001"} 模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"} 22:17:55.677 [main] INFO o.i.demo.design.PrizeController - 奖品发放完成10001。 22:17:55.677 [main] INFO org.itstack.demo.test.ApiTest - 请求参数:{"extMap":{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"},"uId":"10001","bizId":"1023000020112221113","awardNumber":"9820198721311","awardType":2} 22:17:55.677 [main] INFO org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"} 第三方兑换卡(爱奇艺) 22:17:55.678 [main] INFO o.i.demo.design.PrizeController - 奖品发放开始10001。req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3,"uId":"10001"} 模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio 22:17:55.678 [main] INFO o.i.demo.design.PrizeController - 奖品发放完成10001。 22:17:55.678 [main] INFO org.itstack.demo.test.ApiTest - 请求参数:{"uId":"10001","awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3} 22:17:55.678 [main] INFO org.itstack.demo.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"} Process finished with exit code 0
接下来使用工厂方法模式来进行代码优化,也算是一次很小的重构。整理重构会你会发现代码结构清晰了、也具有了下次新增业务需求的扩展性。但在实际使用中还会对此进行完善,目前的只是抽离出最核心的部分体现到你面前,方便学习。
itstack-demo-design-1-02 └── src ├── main │ └── java │ └── org.itstack.demo.design │ ├── store │ │ ├── impl │ │ │ ├── CardCommodityService.java │ │ │ ├── CouponCommodityService.java │ │ │ └── GoodsCommodityService.java │ │ └── ICommodity.java │ └── StoreFactory.java └── test └── java └── org.itstack.demo.design.test └── ApiTest.java
工厂模式
的技巧。public interface ICommodity { void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception; }
用户ID
、奖品ID
、业务ID
以及扩展字段
用于处理发放实物商品时的收获地址。优惠券
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 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"; } }
第三方兑换卡
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 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
配置结构,会让代码更加干净。编写测试类:
@Test public void test_commodity() throws Exception { StoreFactory storeFactory = new StoreFactory(); // 1. 优惠券 ICommodity commodityService_1 = storeFactory.getCommodityService(1); commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null); // 2. 实物商品 ICommodity commodityService_2 = storeFactory.getCommodityService(2); Map<String,String> extMap = new HashMap<String,String>(); extMap.put("consigneeUserName", "谢飞机"); extMap.put("consigneeUserPhone", "15200292123"); extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"); commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap); // 3. 第三方兑换卡(爱奇艺) ICommodity commodityService_3 = storeFactory.getCommodityService(3); commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null); }
结果:
模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132 22:48:10.922 [main] INFO o.i.d.d.s.i.CouponCommodityService - 请求参数[优惠券] => uId:10001 commodityId:EGM1023938910232121323432 bizId:791098764902132 extMap:null 22:48:10.957 [main] INFO o.i.d.d.s.i.CouponCommodityService - 测试结果[优惠券]:{"code":"0000","info":"发放成功"} 模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"} 22:48:10.962 [main] INFO o.i.d.d.s.impl.GoodsCommodityService - 请求参数[优惠券] => uId:10001 commodityId:9820198721311 bizId:1023000020112221113 extMap:{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"} 22:48:10.962 [main] INFO o.i.d.d.s.impl.GoodsCommodityService - 测试结果[优惠券]:true 模拟发放爱奇艺会员卡一张:15200101232,null 22:48:10.963 [main] INFO o.i.d.d.s.impl.CardCommodityService - 请求参数[爱奇艺兑换卡] => uId:10001 commodityId:AQY1xjkUodl8LO975GdfrYUio bizId:null extMap:null 22:48:10.963 [main] INFO o.i.d.d.s.impl.CardCommodityService - 测试结果[爱奇艺兑换卡]:success Process finished with exit code 0
避免建立者与具体的产品逻辑耦合
、知足单一职责,每个业务逻辑实现都在所属本身的类中完成
、知足开闭原则,无需更改使用调用方就能够在程序中引入新的产品类型
。但这样也会带来一些问题,好比有很是多的奖品类型,那么实现的子类会极速扩张。所以也须要使用其余的模式进行优化,这些在后续的设计模式中会逐步涉及到。Java开发架构篇:初识领域驱动设计DDD落地
Java开发架构篇:DDD模型领域层决策规则树服务设计
Java开发架构篇:领域驱动设计架构基于SpringCloud搭建微服务
11 万字的字节码编程系列合集放送
CodeGuide | 程序员编码指南 Go!
<br/>本代码库是做者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为你们提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。若是本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!