经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式-工厂方法模式 实战演习 代码实现 - Code-CHAN
来源:cnblogs  作者:Code-CHAN  时间:2021/3/1 9:15:40  对本文有异议

设计模式-工厂方法模式

一、工厂方法模式介绍

工厂模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型

工厂模式是 Java 开发中最常见的一种模式,其主要意图是定义一个创建对象的接口,让其子类决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

简单说就是为了提供代码的可扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的调用。同时,可以去掉众多 ifelse

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

二、模拟多种奖品发放

营销场景中经常会有抽奖活动,我们在这里模拟兑换多种类型的商品。

假如我们有如下三种类型的商品接口:

序号 类型 接口
1 代金劵 VoucherResult sendVoucher(String userId, String voucherId, int voucherNumber)
2 VIP体验卡 void openVIP(String userId, Date date, String time)
3 实物奖品 Boolean deliverGoods(DeliverRequest req)
  • 三个接口返回类型不同,有对象类型、布尔类型、void
  • 入参不同,代金券需要代金券 id 和代金券数量、VIP体验卡需要体验时间、实物奖品需要奖品对象(包含发货地址等)
  • 可能随着业务的拓展,会增加其他商品类型。

三、非工厂模式实现

1. ifelse实现需求

  1. public class PrizeController {
  2. private static final String CODE_SUCCESS = "11111111";
  3. private static final String CODE_FAIL = "00000000";
  4. public AwardResult awardToUser(AwardRequest req) {
  5. AwardResult res = null;
  6. // 按照不同类型分法商品[1代金劵、2VIP体验卡、3实物奖品]
  7. if (req.getAwardType() == 1) {
  8. VoucherService voucherService = new VoucherService();
  9. VoucherResult voucherResult = voucherService.sendVoucher(req.getUserId(), req.getAwardId(), req.getAwardNumber());
  10. if (CODE_SUCCESS.equals(voucherResult.getCode())) {
  11. res = new AwardResult(CODE_SUCCESS, "成功");
  12. } else {
  13. res = new AwardResult(CODE_FAIL, voucherResult.getInfo());
  14. }
  15. } else if (req.getAwardType() == 2) {
  16. VIPService vipService = new VIPService();
  17. Date date = new Date(System.currentTimeMillis());
  18. vipService.openVip(req.getUserId(), date, req.getTime());
  19. res = new AwardResult(CODE_SUCCESS, "成功");
  20. } else if (req.getAwardType() == 3) {
  21. GoodsService goodsService = new GoodsService();
  22. DeliverRequest deliverRequest = new DeliverRequest();
  23. deliverRequest.setUserId(req.getUserId());
  24. deliverRequest.setUserName(queryUserNameById(req.getUserId()));
  25. deliverRequest.setUserPhone(queryUserPhoneById(req.getUserId()));
  26. deliverRequest.setUserAddress(queryUserAddressById(req.getUserId()));
  27. Boolean isSuccess = goodsService.deliverGoods(deliverRequest);
  28. if (isSuccess) {
  29. res = new AwardResult(CODE_SUCCESS, "成功");
  30. } else {
  31. res = new AwardResult(CODE_FAIL, "失败");
  32. }
  33. }
  34. return res;
  35. }
  36. public String queryUserNameById(String userId) {
  37. return "Tom";
  38. }
  39. public String queryUserPhoneById(String userId) {
  40. return "123456789";
  41. }
  42. public String queryUserAddressById(String userId) {
  43. return "BeiJin";
  44. }
  45. }
  • 如上为使用ifelse 实现业务需求。如果仅从业务角度看,已经实现了基本功能。
  • 但是经过几次迭代后,接手这段代码的研发将十分痛苦。重构成本高,且需要理清之前的每一个接口的使用,测试回归验证时间长,需要全部验证一次。

2. 测试验证

  1. @Test
  2. public void testAwardToUser() {
  3. PrizeController prizeController = new PrizeController();
  4. AwardResult awardResult = null;
  5. System.out.println("******模拟多种奖品发放测试******");
  6. AwardRequest awardReq = new AwardRequest();
  7. awardReq.setAwardId("0001");
  8. awardReq.setAwardNumber(1);
  9. awardReq.setUserId("10001");
  10. System.out.println("\n------ 代金券的发放 ------");
  11. awardReq.setAwardType(1);
  12. awardResult = prizeController.awardToUser(awardReq);
  13. System.out.println("代金券发放结果:" + awardResult.getRes());
  14. System.out.println("\n------ VIP体验卡的发放 -------");
  15. awardReq.setAwardType(2);
  16. awardResult = prizeController.awardToUser(awardReq);
  17. System.out.println("VIP体验卡发放结果:" + awardResult.getRes());
  18. System.out.println("\n------ 实物奖品的发放 -------");
  19. awardReq.setAwardType(3);
  20. awardResult = prizeController.awardToUser(awardReq);
  21. System.out.println("实物奖品发放结果:" + awardResult.getRes());
  22. }

结果:

截屏2021-02-24 14.13.22
  • 运行结果正常,满足业务需求。写的还很快,但是实在难以维护。

四、工厂模式优化代码

1. 代码实现

1.1 定义发奖接口

  1. public interface IAward {
  2. void sendAward(String userId, String awardId, Map<String, Object> map);
  3. }

1.2 实现发奖接口

代金券

  1. public class VoucherAwardService implements IAward{
  2. private VoucherService voucherService = new VoucherService();
  3. @Override
  4. public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception{
  5. VoucherResult voucherResult = voucherService.sendVoucher(userId, awardId, (int) map.get("voucherNumber"));
  6. if (PrizeController.CODE_FAIL.equals(voucherResult.getCode())) {
  7. throw new RuntimeException("失败");
  8. }
  9. }
  10. }

VIP体验卡

  1. public class VIPAwardService implements IAward{
  2. private VIPService vipService = new VIPService();
  3. @Override
  4. public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception {
  5. Date date = new Date(System.currentTimeMillis());
  6. vipService.openVip(userId, (Date) map.get("data"), (String) map.get("time"));
  7. }
  8. }

实物奖品

  1. public class GoodsAwardService implements IAward{
  2. private GoodsService goodsService = new GoodsService();
  3. @Override
  4. public void sendAward(String userId, String awardId, Map<String, Object> map) throws Exception {
  5. DeliverRequest deliverRequest = new DeliverRequest();
  6. deliverRequest.setUserId(userId);
  7. deliverRequest.setUserName(queryUserNameById(userId));
  8. deliverRequest.setUserPhone(queryUserPhoneById(userId));
  9. deliverRequest.setUserAddress(queryUserAddressById(userId));
  10. Boolean isSuccess = goodsService.deliverGoods(deliverRequest);
  11. if (!isSuccess) {
  12. throw new RuntimeException("失败");
  13. }
  14. }
  15. public String queryUserNameById(String userId) {
  16. return "Tom";
  17. }
  18. public String queryUserPhoneById(String userId) {
  19. return "123456789";
  20. }
  21. public String queryUserAddressById(String userId) {
  22. return "BeiJin";
  23. }
  24. }
  • 可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试
  • 后续再新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展
  • 统一入参和出参后,调用方不在需要关心内部的实现逻辑,按照统一方式处理即可

1.3 创建奖品工厂

  1. public class AwardFactory {
  2. public IAward getAwardService(Integer awardType) throws Exception{
  3. if (awardType == null) return null;
  4. if (1 == awardType) return new VoucherAwardService();
  5. if (2 == awardType) return new VIPAwardService();
  6. if (3 == awardType) return new GoodsAwardService();
  7. throw new RuntimeException("不存在此奖品类型");
  8. }
  9. }
image-20210224153448134

2.测试验证

  1. @Test
  2. public void testIAward() throws Exception{
  3. AwardFactory awardFactory = new AwardFactory();
  4. System.out.println("******模拟多种奖品发放测试******");
  5. System.out.println("\n------ 代金券的发放 ------");
  6. IAward awardService1 = awardFactory.getAwardService(1);
  7. Map<String, Object> map1 = new HashMap<>();
  8. map1.put("voucherNumber", 1);
  9. awardService1.sendAward("10001", "123456712", map1);
  10. System.out.println("\n------ VIP体验卡的发放 -------");
  11. IAward awardService2 = awardFactory.getAwardService(2);
  12. Map<String, Object> map2 = new HashMap<>();
  13. map2.put("data", new Date(System.currentTimeMillis()));
  14. map2.put("time", "23:59:59");
  15. awardService2.sendAward("10002", "123123414", map2);
  16. System.out.println("\n------ 实物奖品的发放 -------");
  17. IAward awardService3 = awardFactory.getAwardService(3);
  18. awardService3.sendAward("10003", "12312451", null);
  19. }

五、总结

工厂模式可以避免创建者与具体的产品逻辑耦合、满足单一职责,每一个业务逻辑实现都在所属自己的类中完成满足开闭原则,无需修改调用方就可以在程序中引入新的产品类型

但这样也会带来一些问题,比如有非常多的奖品类型,那么实现的子类会急速扩张。

原文链接:http://www.cnblogs.com/Code-CHAN/p/14441804.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号