

师爷,你给翻译翻译什么叫工厂模式? - Love&Share
source link: https://www.cnblogs.com/YXBLOGXYY/p/16583956.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

师爷,你给翻译翻译什么叫工厂模式?
写在前面:
初为职场新手,难免会把代码写的一把梭。遇到业务的时候直接if-else干。但是本着不想被开除的原则,还是学习一下设计模式,今天来看看设计模式之工厂模式。

用需求引出代码:
最近正在开发抽奖系统,系统当中有个业务是发奖业务,奖品有优惠券,实物商品,第三方优惠券。当用户抽中对应的奖品之后,我们需要有对应的方法来发奖。针对这样的业务我们先来看一下代码一把梭的情况。
工程结构:

public class PrizeController {
private Logger logger = LoggerFactory.getLogger(PrizeController.class);
/**
* 给用户发奖
* @param req
* @return
*/
public AwardRes awardToUser(AwardReq req) {
// 把req转为json格式好处理
String reqJson = JSON.toJSONString(req);
// 初始化返回结果为空
AwardRes awardRes = null;
try {
logger.info("奖品发放开始{}。req:{}", req.getuId(), reqJson);
// 按照不同类型方法商品[1优惠券、2实物商品、3第三方兑换卡(爱奇艺)]
if (req.getAwardType() == 1) {
// 这是1-00当中的类
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"));
// 调用方法来发实物商品,如果发成功返回true
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";
}
}
如上的代码有什么问题?
- 业务不变还好(基本不可能),业务如果变更,那就得去修改这个类中的代码,违背开闭原则(对修改关闭,对扩展开放),违背最小知识原则(一个类只干一件事)。
- 被组长知道了,绩效3.0跑不了,假如离职下一个同事接手了估计直骂娘。


为什么叫工厂模式呢?
- 工厂是生产东西的,买家可以直接去工厂拿东西 ,并且工厂能生产的东西是多样性的。比如食品工厂,可以生产旺旺雪饼,雪糕,华夫饼。那我们需要去进货的时候,只需要带着需求清单去找工厂就行。
调用关系图:

具体代码实现:
发送商品接口:
public interface ICommodity { void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception; }
- 所有的奖品⽆论是实物、虚拟还是第三⽅,都需要通过我们的程序实现此接⼝进⾏处理,以保证最终⼊参出参的统⼀性。
- 接⼝的⼊参包括:⽤户ID 、 奖品ID 、 业务ID 以及 扩展字段⽤于处理发放实物商品时的收获地址。
public class CouponCommodityService implements ICommodity {
private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
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);
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();
@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);
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(); @Override 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 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 配置结构,会让代码更加⼲净
- 从上到下的优化来看,⼯⼚⽅法模式并不复杂,使用工厂模式之后代码结构更加清晰了,但是还是要根据业务和开发工期来动态决策具体该怎么写。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK