工厂模式是一种常见的设计模式,通过定义一个创建对象的接口,让子类决定将哪个工厂类实例化。
未经优化的代码案例
例子用发放奖品为例,有三种不同的奖品,当不同类型的发放请求进入时会根据参数进行发放。
/**
* 模拟发奖服务
*/
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";
}
}
&esmp;很明显,代码里根据不同的参数进行if-else的判断,当面对复杂的业务场景时代码的可读性和维护性都很差。
用工厂模式进行优化
// 定义奖品发放接口
public interface ICommodity {
void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}
根据业务需要定义接口实现类:
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 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 StoreFactory {
/**
* 奖品类型方式实例化
* @param commodityType 奖品类型
* @return 实例化对象
*/
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("不存在的奖品服务类型");
}
/**
* 奖品类信息方式实例化
* @param clazz 奖品类
* @return 实例化对象
*/
public ICommodity getCommodityService(Class<? extends ICommodity> clazz) throws IllegalAccessException, InstantiationException {
if (null == clazz) return null;
return clazz.newInstance();
}
}
&emsp 工厂实现类提供了两种对象创建的方法,第一种通过奖品类型进行区分,直接根据业务需要进行编码,第二种通过类信息进行实例化,根据传入的类型进行创建。
工厂模式的优点
很明显工厂模式面向接口编程,将创建者与具体的业务逻辑进行解耦,将同类的业务逻辑抽象到接口,由不同的实现类实现不同的具体逻辑(例子中将抽奖抽象出来),满足依赖倒置原则;满足单一职责原则,每个业务类实现一个业务逻辑,业务逻辑之间互不影响,维护成本低;满足开闭原则,当需要添加新一种业务逻辑时只要增加实现类即可。
参考资料:《重学Java设计模式》