这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多ifelse的方式。当然这可能也有一些缺点,比如需要实现的类非常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使用中,逐步降低。
实例:模拟积分兑换中的发放多种类型商品
| 序号 | 类型 | 接口 |
|---|---|---|
| 1 | 优惠券 | CouponResult sendCoupon(String uId, String couponNumber, String uuid) |
| 2 | 实物商品 | Boolean deliverGoods(DeliverReq req) |
| 3 | 第三方爱奇艺兑换卡 | void grantToken(String bindMobileNumber, String cardId) |
/**
* @Author blackcat
* @version: 1.0
* @description:模拟优惠券服务
*/
public class CouponService {
public CouponResult sendCoupon(String uId, String couponNumber, String uuid) {
System.out.println("模拟发放优惠券一张:" + uId + "," + couponNumber + "," + uuid);
return new CouponResult("0000", "发放成功");
}
}
/**
* @Author blackcat
* @version: 1.0
* @description:优惠券返回类
*/
@Data
@AllArgsConstructor
public class CouponResult {
private String code; // 编码
private String info; // 描述
}
/**
* @Author blackcat
* @version: 1.0
* @description:模拟实物商品服务
*/
public class GoodsService {
public Boolean deliverGoods(DeliverReq req) {
System.out.println("模拟发货实物商品一个:" +req.toString());
return true;
}
}
/**
* @Author blackcat
* @version: 1.0
* @description:事物参数
*/
@Data
public class DeliverReq {
private String userName; // 用户姓名
private String userPhone; // 用户手机
private String sku; // 商品SKU
private String orderId; // 订单ID
private String consigneeUserName; // 收货人姓名
private String consigneeUserPhone; // 收货人手机
private String consigneeUserAddress; // 收获人地址
}
/**
* @Author blackcat
* @version: 1.0
* @description:模拟爱奇艺会员卡服务
*/
public class IQiYiCardService {
public void grantToken(String bindMobileNumber, String cardId) {
System.out.println("模拟发放爱奇艺会员卡一张:" + bindMobileNumber + "," + cardId);
}
}
ifelse 实现需求
/**
* @Author blackcat
* @version: 1.0
* @description:模拟发奖服务
*/
@Slf4j
public class PrizeController {
public AwardRes awardToUser(AwardReq req) {
AwardRes awardRes = null;
try {
log.info("奖品发放开始{}。req:{}", req.getUId(),req.toString());
// 按照不同类型方法商品[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", "发放成功");
}
log.info("奖品发放完成{}。", req.getUId());
} catch (Exception e) {
log.error("奖品发放失败{}。req:{}", req.getUId(), req.toString(), e);
awardRes = new AwardRes("0001", e.getMessage());
}
return awardRes;
}
private String queryUserName(String uId) {
return "房东的猫";
}
private String queryUserPhoneNumber(String uId) {
return "123456789100";
}
}
1、如上就是使用 ifelse 非常直接的实现出来业务需求的一坨代码,如果仅从业务角度看,研发如期甚至提前实现了功能。 2、那这样的代码目前来看并不会有什么问题,但如果在经过几次的迭代和拓展,接手这段代码的研发将十分痛苦。重构成本高需要理清之前每一个接口的使用,测试回归验证时间长,需要全部验证一次。这也就是很多人并不愿意接手别人的代码,如果接手了又被压榨开发时间。那么可想而知这样的 ifelse 还会继续增加。
工厂模式优化代码
定义发奖接口
/**
* @Author blackcat
* @version: 1.0
* @description:定义发奖接口 保证入参和出参一致
*/
public interface ICommodity {
void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}
实现奖品发放接口
/**
* @Author blackcat
* @version: 1.0
* @description:优惠券
*/
@Slf4j
public class CouponCommodityService implements ICommodity {
private 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);
log.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, extMap.isEmpty() ? "" : extMap.toString());
log.info("测试结果[优惠券]:{}", couponResult.toString());
if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());
}
}
/**
* @Author blackcat
* @version: 1.0
* @description:实物服务
*/
@Slf4j
public class GoodsCommodityService implements ICommodity {
private GoodsService goodsService = new GoodsService();
@Override
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);
log.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, extMap.isEmpty() ? "" : extMap.toString());
log.info("测试结果[优惠券]:{}", isSuccess);
if (!isSuccess) throw new RuntimeException("实物商品发放失败");
}
private String queryUserName(String uId) {
return "房东的猫";
}
private String queryUserPhoneNumber(String uId) {
return "123456789100";
}
}
/**
* @Author blackcat
* @version: 1.0
* @description:第三方爱奇艺兑换卡
*/
@Slf4j
public class CardCommodityService implements ICommodity {
private IQiYiCardService iQiYiCardService = new IQiYiCardService();
@Override
public 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, extMap.isEmpty() ? "" : extMap.toString());
log.info("测试结果[爱奇艺兑换卡]:success");
}
private String queryUserMobile(String uId) {
return "123456789100";
}
}
/**
* @Author blackcat
* @version: 1.0
* @description:
*/
public class StoreFactory {
public ICommodity getCommodityService(Integer commodityType) {
if (1 == commodityType) return new CouponCommodityService();
if (2 == commodityType) return new GoodsCommodityService();
if (3 == commodityType) return new CardCommodityService();
throw new RuntimeException("不存在的商品服务类型");
}
}
**避免创建者与具体的产品逻辑耦合、满足单一职责,每一个业务逻辑实现都在所属自己的类中完成、满足开闭原则,无需更改使用调用方就可以在程序中引入新的产品类型。**但这样也会带来一些问题,比如有非常多的奖品类型,那么实现的子类会极速扩张。