作者:小傅哥 博客:https://
沉淀、分享、成长,让自己和他人都能有所收获!😄
一、前言
好看的代码千篇一律,恶心的程序升职加薪。
该说不说几乎是程序员就都知道或者了解设计模式,但大部分小伙伴写代码总是习惯于一把梭。无论多少业务逻辑就一个类几千行,这样的开发也可以归纳为三步;定义属性、创建方法、调用展示,Done!只不过开发一时爽,重构火葬场。
好的代码不只为了完成现有功能,也会考虑后续扩展。在结构设计上松耦合易读易扩展,在领域实现上高内聚不对外暴漏实现细节不被外部干扰。而这就有点像家里三居(MVC)室、四居(DDD)室的装修,你不会允许几十万的房子把走线水管裸漏在外面,也不会允许把马桶放到厨房,炉灶安装到卫生间。
谁发明了设计模式? 设计模式的概念最早是由 克里斯托佛·亚历山大
在其著作 《建筑模式语言》
中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,埃里希·伽玛
、 约翰·弗利赛德斯
、 拉尔夫·约翰逊
和 理查德·赫尔姆
这四位作者接受了模式的概念。 1994 年, 他们出版了 《设计模式: 可复用面向对象软件的基础》
一书, 将设计模式的概念应用到程序开发领域中。
其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。
我怎么学不会设计模式? 钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌!
所以 ,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是人车合一 ,才能站在设计模式的基础上构建出更加合理的代码。
二、开发环境
JDK 1.8
Idea + Maven
涉及工程三个,可以通过关注公众号 :bugstack虫洞栈
,回复源码下载
获取。你会获得一个连接打开后的列表中编号18
:itstack-demo-design
工程 描述 itstack-demo-design-1-00 场景模拟工程,用于提供三组不同奖品的发放接口 itstack-demo-design-1-01 使用一坨代码实现业务需求,也是对ifelse的使用 itstack-demo-design-1-02 通过设计模式优化改造代码,产生对比性从而学习
1-00,1 代表着第一个设计模式,工厂方法模式 1-00,00 代表模拟的场景 1-01,01 代表第一种实现方案,后续 02 03 以此类推
二、工厂方法模式介绍
工厂方法模式,图片来自 refactoringguru.cn
工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多ifeslse
的方式。当然这可能也有一些缺点,比如需要实现的类非常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使用中,逐步降低。
三、模拟发奖多种商品
为了可以让整个学习的案例更加贴近实际开发,这里模拟互联网中在营销场景下的业务。由于营销场景的复杂、多变、临时的特性,它所需要的设计需要更加深入,否则会经常面临各种紧急CRUD操作,从而让代码结构混乱不堪,难以维护。
在营销场景中经常会有某个用户做了一些操作;打卡、分享、留言、邀请注册等等,进行返利积分,最后通过积分在兑换商品,从而促活和拉新。
那么在这里我们模拟积分兑换中的发放多种类型商品,假如现在我们有如下三种类型的商品接口;
序号 类型 接口 1 优惠券 CouponResult sendCoupon(String uId, String couponNumber, String uuid)
2 实物商品 Boolean deliverGoods(DeliverReq req)
3 第三方爱奇艺兑换卡 void grantToken(String bindMobileNumber, String cardId)
从以上接口来看有如下信息:
三个接口返回类型不同,有对象类型、布尔类型、还有一个空类型。 入参不同,发放优惠券需要仿重、兑换卡需要卡ID、实物商品需要发货位置(对象中含有)。 另外可能会随着后续的业务的发展,会新增其他种商品类型。因为你所有的开发需求都是随着业务对市场的拓展而带来的。
四、用一坨坨代码实现
如果不考虑任何扩展性,只为了尽快满足需求,那么对这么几种奖励发放只需使用ifelse语句判断,调用不同的接口即可满足需求。可能这也是一些刚入门编程的小伙伴,常用的方式。接下来我们就先按照这样的方式来实现业务的需求。
1. 工程结构
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
2. ifelse实现需求
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
还会继续增加。
3. 测试验证
写一个单元测试来验证上面编写的接口方式,养成单元测试的好习惯会为你增强代码质量。
编写测试类:
@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
运行结果正常,满足当前所有业务产品需求,写的还很快。但!实在难以为维护!
五、工厂模式优化代码
接下来使用工厂方法模式来进行代码优化,也算是一次很小的重构 。整理重构会你会发现代码结构清晰了、也具备了下次新增业务需求的扩展性。但在实际使用中还会对此进行完善,目前的只是抽离出最核心的部分体现到你面前,方便学习。
1. 工程结构
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
首先,从上面的工程结构中你是否一些感觉,比如;它看上去清晰了、这样分层可以更好扩展了、似乎可以想象到每一个类做了什么。 如果还不能理解为什么这样修改,也没有关系。因为你是在通过这样的文章,来学习设计模式的魅力。并且再获取源码后,进行实际操作几次也就慢慢掌握了工厂模式
的技巧。
2. 代码实现
2.1 定义发奖接口
public interface ICommodity {
void sendCommodity ( String uId, String commodityId, String bizId, Map< String, String> extMap) throws Exception;
}
所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统一性。 接口的入参包括;用户ID
、奖品ID
、业务ID
以及扩展字段
用于处理发放实物商品时的收获地址。
2.2 实现奖品发放接口
优惠券
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" ;
}
}
从上面可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。 在统一了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。
2.3 创建商店工厂
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
配置结构,会让代码更加干净。 另外很多代码检查软件和编码要求,不喜欢if语句后面不写扩展,这里是为了更加干净的向你体现逻辑。在实际的业务编码中可以添加括号。
3. 测试验证
编写测试类:
@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!
本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!