1.工厂方式模式概述
工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的,只是知道调用即可。主要解决接口选择的问题。
1.1 工厂方法模式优缺点
优点:
- 用户只需要知道具体工厂的名称就可以得到所需要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
1.2 工厂方法模式核心结构
工厂方法模式是在简单工厂模式上的改进,主要包含如下几个角色及组件:
- 抽象工厂(Creator):整个工厂模式的核心角色,它与应用无关,主要在创建模式中规范和产品对应的工厂对象的标准化定义。
- 具体工厂(ConcreteCreator):实现了抽象工厂的具体工厂类,该类型是和应用直接交互的具体实现类,在应用程序中调用,用于创建产品对象。
- 抽象产品(Product):工厂方法模式创建的所有类型的超级父类,该类型和具体业务有关,用于规范工厂方法模式中创建的方法对象具备的公共特征行为。
- 具体产品(Concrete Product):该类型实现了抽象产品 父类,是工厂方法模式中具体创建的实例对象。
1.3 UML 类图
2. 案例实现
案例描述: 模拟营销场景下用户通过积分兑换不同类型的商品(如兑换卡、优惠券、实物商品等)
现在有如下三种类型的商品接口:
序号 | 类型 | 接口 |
---|---|---|
1 | 优惠券 | CouponResult sendCoupon(String uId, String couponNumber, String uuid) |
2 | 实物商品 | Boolean deliverGoods(DeliverReq req) |
3 | 第三方爱奇艺兑换卡 | CouponResult sendCoupon(String uId, String couponNumber, String uuid) |
代码实现
2.1 一坨坨if/else实现
2.1.1 工程结构
FACTORY-METHOD-MODE-00
├─src
│ └─main
│ └─java
│ └─org
│ └─itstack
│ └─factorymethod
│ └─design
│ ├─card
│ │ IQiYiCard.java
│ │ IQiYiCardService.java
│ ├─coupon
│ │ CouponInfo.java
│ │ CouponResult.java
│ │ CouponService.java
│ └─goods
│ DeliverReq.java
│ GoodsInfo.java
│ GoodsService.java
FACTORY-METHOD-MODE-01
├─src
│ ├─main
│ │ └─java
│ │ └─org
│ │ └─itstack
│ │ └─factorymethod
│ │ └─design
│ │ AwardReq.java
│ │ AwardRes.java
│ │ PrizeController.java
│ └─test
│ └─java
│ └─org
│ └─itstack
│ └─factorymethod
│ └─test
│ └─ ApiTest.java
└─ pom.xml
2.1.2 if/else 代码实现
factory-method-mode-00
模拟爱奇艺会员卡服务
/**
* 模拟爱奇艺会员卡服务
*/
public class IQiYiCardService {
/**
* 生成token
*
* @param bindMobileNumber 绑定的手机号码
* @param cardId 会员卡Id
*/
public void grantToken(String bindMobileNumber, String cardId) {
System.out.println("模拟发放爱奇艺会员卡一张:" + bindMobileNumber + "," + cardId);
}
}
模拟优惠券服务
/**
* 优惠券返回结果对象
*/
public class CouponResult {
/**
* 编码
*/
private String code;
/**
* 描述
*/
private String info;
// ...get set 构造函数
}
/**
* 模拟优惠券服务
*/
public class CouponService {
/**
* 模拟发放优惠券
* @param uId 优惠券Id
* @param couponNumber 优惠券数量
* @param uuid uuid
* @return
*/
public CouponResult sendCoupon(String uId, String couponNumber, String uuid) {
System.out.println("模拟发放优惠券一张:" + uId + "," + couponNumber + "," + uuid);
return new CouponResult("0000", "发放成功");
}
}
模拟发放实物商品服务
/**
* 实物商品请求对象
*/
public class DeliverReq {
/**
* 用户姓名
*/
private String userName;
/**
* 用户手机
*/
private String userPhone;
/**
* 商品SKU
*/
private String sku;
/**
* 订单ID
*/
private String orderId;
/**
* 收件人姓名
*/
private String consigneeUserName;
/**
* 收件手机
*/
private String consigneeUserPhone;
/**
* 收件人地址
*/
private String consigneeUserAddress;
//getter setter
}
/**
* 模拟实物商品服务
*/
public class GoodsService {
/**
* 是否发放实物奖品
* @param req
* @return
*/
public Boolean deliverGoods(DeliverReq req) {
System.out.println("模拟发货实物商品一个:" + JSON.toJSONString(req));
return true;
}
}
factory-method-mode-01
/**
* 发奖请求对象
*/
public class AwardReq {
/**
* 用户唯一ID
*/
private String uId;
/**
* 奖品类型(可以用枚举定义);1优惠券、2实物商品、3第三方兑换卡(爱奇艺)
*/
private Integer awardType;
/**
* 奖品编号;sku、couponNumber、cardId
*/
private String awardNumber;
/**
* 业务ID,防重复
*/
private String bizId;
/**
* 扩展信息
*/
private Map<String, String> extMap;
// get set
}
/**
* 发奖响应对象
*/
public class AwardRes {
/**
* 编码
*/
private String code;
/**
* 信息
*/
private String info;
// get set
}
/**
* 模拟发奖服务
*/
public class PrizeController {
private final Logger logger = LoggerFactory.getLogger(PrizeController.class);
/**
* 模拟给用户发奖
*
* @param req
* @return
*/
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.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
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 还会继续增加。
2.1.3 测试验证
@Test
public void test_awardToUser() {
PrizeController prizeController = new PrizeController();
System.out.println("\r\n模拟发放优惠券测试\r\n");
// 模拟发放优惠券测试
AwardReq couponReq = new AwardReq();
couponReq.setuId("10001");
couponReq.setAwardType(1);
couponReq.setAwardNumber("EGM1023938910232121323432");
couponReq.setBizId("791098764902132");
AwardRes couponAwardRes01 = prizeController.awardToUser(couponReq);
logger.info("请求参数:{}", JSON.toJSON(couponReq));
logger.info("测试结果:{}", JSON.toJSON(couponAwardRes01));
System.out.println("---------------------------------------------------------");
System.out.println("\r\n模拟发放实物商品\r\n");
AwardReq goodsReq = new AwardReq();
goodsReq.setuId("10001");
goodsReq.setAwardType(2);
goodsReq.setAwardNumber("9820198721311");
goodsReq.setBizId("1023000020112221113");
goodsReq.setExtMap(new HashMap<String,String>(){
{
put("consigneeUserName","蟹不肉");
put("consigneeUserPhone","15200292123");
put("consigneeUserAddress","上海市.浦东新区.高行镇.东靖路393弄.#44号1002");
}
});
AwardRes goodsAwardRes01 = prizeController.awardToUser(goodsReq);
logger.info("请求参数:{}", JSON.toJSON(goodsReq));
logger.info("测试结果:{}", JSON.toJSON(goodsAwardRes01));
System.out.println("---------------------------------------------------------");
System.out.println("\r\n第三方兑换卡(爱奇艺)\r\n");
AwardReq iQiYiCardReq = new AwardReq();
iQiYiCardReq.setuId("10001");
iQiYiCardReq.setAwardType(3);
iQiYiCardReq.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");
AwardRes iQiYiCardAwardRes = prizeController.awardToUser(iQiYiCardReq);
logger.info("请求参数:{}", JSON.toJSON(iQiYiCardReq));
logger.info("测试结果:{}", JSON.toJSON(iQiYiCardAwardRes));
}
结果
模拟发放优惠券测试
16:18:29.874 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"EGM1023938910232121323432","awardType":1,"bizId":"791098764902132","uId":"10001"}
模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
16:18:29.883 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.884 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"uId":"10001","bizId":"791098764902132","awardNumber":"EGM1023938910232121323432","awardType":1}
16:18:29.889 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
---------------------------------------------------------
模拟发放实物商品
16:18:29.890 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"9820198721311","awardType":2,"bizId":"1023000020112221113","extMap":{"consigneeUserName":"蟹不肉","consigneeUserPhone":"15200292123","consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002"},"uId":"10001"}
模拟发货实物商品一个:{"consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002","consigneeUserName":"蟹不肉","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
16:18:29.897 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.897 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"extMap":{"consigneeUserName":"蟹不肉","consigneeUserAddress":"上海市.浦东新区.高行镇.东靖路393弄.#44号1002","consigneeUserPhone":"15200292123"},"uId":"10001","bizId":"1023000020112221113","awardNumber":"9820198721311","awardType":2}
16:18:29.897 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
---------------------------------------------------------
第三方兑换卡(爱奇艺)
16:18:29.898 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放开始10001,req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3,"uId":"10001"}
模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio
16:18:29.899 [main] INFO org.itstack.factorymethod.design.PrizeController - 奖品发放完成10001
16:18:29.899 [main] INFO org.itstack.factorymethod.test.ApiTest - 请求参数:{"uId":"10001","awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3}
16:18:29.899 [main] INFO org.itstack.factorymethod.test.ApiTest - 测试结果:{"code":"0000","info":"发放成功"}
Process finished with exit code 0
- 运行结果正常,满足当前所有业务产品需求,但是难以维护。
2.2 工厂方法模式代码优化
2.2.1 UML类图
2.2.2 工程结构
FACTORY-METHOD-MODE-02
├─src
│ ├─main
│ │ ├─java
│ │ │ └─org
│ │ │ └─itstack
│ │ │ └─factorymethod
│ │ │ └─design
│ │ │ │ StoreFactory.java
│ │ │ │
│ │ │ └─store
│ │ │ │ ICommodity.java
│ │ │ │
│ │ │ └─impl
│ │ │ CardCommodityService.java
│ │ │ CouponCommodityService.java
│ │ │ GoodsCommodityService.java
│ │ │
│ │ └─resources
│ └─test
│ └─java
│ └─org
│ └─itstack
│ └─factorymethod
│ └─test
│ ApiTest.java
2.2.3 代码实现
2.2.3.1 定义发奖接口
public interface ICommodity {
/**
* 发奖
*
* @param uId 用户Id
* @param commodityId 奖品Id
* @param bizId 业务Id
* @param extMap 扩展字段
* @throws Exception e
*/
void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}
- 所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统⼀性。
- 接口的入参包括; 用户ID 、 奖品ID 、 业务ID 以及 扩展字段用于处理发放实物商品时的收货地址。
2.2.3.2 实现奖品发放接口
优惠券
/**
* 优惠券奖品服务
*/
public class CouponCommodityService implements ICommodity {
private final Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
private final CouponService couponService = new CouponService();
@Override
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 final Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);
/**
* 模拟注入商品服务
*/
private final GoodsService goodsService = new GoodsService();
@Override
public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
DeliverReq req = new DeliverReq();
req.setUserPhone(queryUserPhoneNumber(uId));
req.setUserName(queryUserName(uId));
req.setSku(commodityId);
req.setOrderId(bizId);
req.setConsigneeUserName(extMap.get("consigneeUserName"));
req.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
req.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
Boolean isSuccess = goodsService.deliverGoods(req);
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 final Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
/**
* 模拟注入
*/
private final IQiYiCardService iQiYiCardService = new IQiYiCardService();
@Override
public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
String mobileNumber = queryUserMobile(uId);
iQiYiCardService.grantToken(mobileNumber,commodityId);
logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
logger.info("测试结果[爱奇艺兑换卡]:success");
}
private String queryUserMobile(String uId) {
return "15200101232";
}
}
- 从上面可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
- 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。
- 在统一了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。
2.2.3.3 创建商店工厂
/**
* 商店工厂类
*/
public class StoreFactory {
private static final Map<Integer, ICommodity> commodityServiceMap = new HashMap<>();
static {
commodityServiceMap.put(1, new CouponCommodityService());
commodityServiceMap.put(2, new GoodsCommodityService());
commodityServiceMap.put(3, new CardCommodityService());
}
/**
* 工厂模式根据商品类型获取对应service(map配置结构)
*
* @param commodityType 商品类型
* @return ICommodity
*/
public ICommodity getCommodityServiceByMap(Integer commodityType) {
return Optional.ofNullable(commodityType)
.map(commodityServiceMap::get)
.orElseThrow(() -> new RuntimeException("不存在的商品服务类型"));
// if (null == commodityType) return null;
// ICommodity iCommodity = commodityServiceMap.get(commodityType);
// if (null == iCommodity) {
// throw new RuntimeException("不存在的商品服务类型");
// } else {
// return iCommodity;
// }
}
/**
* 工厂模式根据商品类型获取对应service(switch判断)
*
* @param commodityType 商品类型
* @return ICommodity
*/
public ICommodity getCommodityService(Integer commodityType) {
if (null == commodityType) return null;
switch (commodityType) {
case 1:
return new CouponCommodityService();
case 2:
return new GoodsCommodityService();
case 3:
return new CardCommodityService();
default:
throw new RuntimeException("不存在的商品服务类型");
}
}
}
- 定义一个商店的⼯厂类,在⾥面按照类型实现各种商品的服务。可以⾮非常干净整洁的处理我们的代码,后续新增的商品在这里扩展即可。
- 这里定义了两种不同的处理方式,getCommodityService 通过if判断获取接口,getCommodityServiceByMap通过map方式获取相对应的接口。
2.2.4 测试验证
**getCommodityService **
@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",new HashMap<String, String>() {{
put("consigneeUserName", "谢飞机");
put("consigneeUserPhone", "15200292123");
put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
}});
// 3. 第三方兑换卡(爱奇艺)
ICommodity commodityService_3 = storeFactory.getCommodityService(3);
commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
}
**getCommodityServiceByMap **
@Test
public void test_commodity2() throws Exception {
StoreFactory storeFactory = new StoreFactory();
// 1. 优惠券
ICommodity commodityService_1 = storeFactory.getCommodityServiceByMap(1);
commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
// 2. 实物商品
ICommodity commodityService_2 = storeFactory.getCommodityServiceByMap(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",new HashMap<String, String>() {{
put("consigneeUserName", "谢飞机");
put("consigneeUserPhone", "15200292123");
put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");
}});
// 3. 第三方兑换卡(爱奇艺)
ICommodity commodityService_3 = storeFactory.getCommodityServiceByMap(3);
commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
}
测试结果
模拟发放优惠券一张:10001,EGM1023938910232121323432,791098764902132
09:54:47.287 [main] INFO org.itstack.factorymethod.design.store.impl.CouponCommodityService - 请求参数[优惠券] => uId:10001 commodityId:EGM1023938910232121323432 bizId:791098764902132 extMap:null
09:54:47.328 [main] INFO org.itstack.factorymethod.design.store.impl.CouponCommodityService - 测试结果[优惠券]:{"code":"0000","info":"发放成功"}
模拟发货实物商品一个:{"consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserName":"谢飞机","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.GoodsCommodityService - 请求参数[优惠券] => uId:10001 commodityId:9820198721311 bizId:1023000020112221113 extMap:{"consigneeUserName":"谢飞机","consigneeUserAddress":"吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109","consigneeUserPhone":"15200292123"}
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.GoodsCommodityService - 测试结果[优惠券]:true
模拟发放爱奇艺会员卡一张:15200101232,AQY1xjkUodl8LO975GdfrYUio
09:54:47.338 [main] INFO org.itstack.factorymethod.design.store.impl.CardCommodityService - 请求参数[爱奇艺兑换卡] => uId:10001 commodityId:AQY1xjkUodl8LO975GdfrYUio bizId:null extMap:null
09:54:47.339 [main] INFO org.itstack.factorymethod.design.store.impl.CardCommodityService - 测试结果[爱奇艺兑换卡]:success
Process finished with exit code 0
- 运行结果正常
3.总结
工厂方法模式的主要思想是继承,修改符合开闭原则;但每个工厂只能创建一种类型的产品。
通过上面的实战我们可以总结出它的一些优点:避免创建者与具体的产品逻辑耦合、满足单一职责,每一个业务逻辑实现都在所属自己的类中完成、满足开闭原则,无需更改使用调用方就可以在程序中引入新的产品类型。但这样也会带来一些问题,如果有非常多的奖品类型,那么实现的子类会极速扩张。
更多精彩内容,请关注微信公众号:bugstack虫洞栈