这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤用性。
包含工厂方法、抽象工厂、建造者、原型、单例。
工厂方法:
解释说明:
工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
这种设计模式也是 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
从以上接⼝口来看有如下信息:
- 三个接口返回类型不不同,有对象类型、布尔类型、还有⼀一个空类型。
- 入参不不同,发放优惠券需要仿重、兑换卡需要卡ID、实物商品需要发货位置(对象中含有)。
- 另外可能会随着后续的业务的发展,会新增其他种商品类型。因为你所有的开发需求都是随着业务对市场的拓展而带来的。
使用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;
}
- 所有的奖品无论是实物、虚拟还是第三方,都需要通过我们的程序实现此接口进行处理,以保证最终入参出参的统一性。
- 接口的入参包括; 用户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";
}
}
- 从上⾯面可以看到每⼀一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
- 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。
- 在统一了了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。
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 配置结构,会让代码更更加干净。
总结:
优点:避免创建者与具体得产品逻辑耦合、满足单一原则每个业务有自己得单独实现类,满足开闭无需改变调用方法就可以新增不同类型。
缺点:比如有非常多的奖品类型,那么实现的子类会极速扩张。因此也需要使用其他的模式进行优化。