【设计模式精讲】创建型模式之工厂方法模式(简单工厂、工厂方法)
- 手机
- 2025-08-25 07:12:01

文章目录 第四章 创建型模式4.2 工厂方法模式4.2.1 需求: 模拟发放奖品业务4.2.2 原始开发方式4.2.3 简单工厂模式4.2.3.1 简单工厂模式介绍4.2.3.2 简单工厂原理4.2.3.3 简单工厂模式重构代码4.2.3.4 简单工厂模式总结 4.2.4 工厂方法模式4.2.4.1 工厂方法模式介绍4.2.4.2 工厂方法模式原理4.2.4.3 工厂方法模式重构代码4.2.4.4 工厂方法模式总结
个人主页:道友老李 欢迎加入社区:道友老李的学习社区
第四章 创建型模式 4.2 工厂方法模式工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。《设计模式》一书中,工厂模式被分为了三种:简单工厂、工厂方法和抽象工厂。(不过,在书中作者将简单工厂模式看作是工厂方法模式的一种特例。)
接下来我会介绍三种工厂模式的原理及使用
简单工厂模式(不属于GOF的23种经典设计模式)工厂方法模式抽象工厂模式 4.2.1 需求: 模拟发放奖品业务需求: 为了让我们的案例更加贴近实际开发, 这里我们来模拟一下互联网电商中促销拉新下的业务场景, 新用户注册立即参与抽奖活动 ,奖品的种类有: 打折券, 免费优酷会员,小礼品.
4.2.2 原始开发方式不考虑设计原则,不使用设计模式的方式进行开发
在不考虑任何代码的可扩展性的前提下,只为了尽快满足需求.我们可以这样去设计这个业务的代码结构:
1) 实体类
名称描述AwardInfo获奖信息对应实体类DiscountInfo打折券信息对应实体类YouKuMember优酷会员对应实体类SmallGiftInfo小礼品信息对应实体类DiscountResult打折券操作响应结果封装类 public class AwardInfo { private String uid; //用户唯一ID private Integer awardType; //奖品类型: 1 打折券 ,2 优酷会员,3 小礼品 private String awardNumber; //奖品编号 Map<String, String> extMap; //额外信息 } public class DiscountInfo { //属性信息省略...... } public class YouKuMember { //属性信息省略...... } public class SmallGiftInfo { private String userName; // 用户姓名 private String userPhone; // 用户手机 private String orderId; // 订单ID private String relAddress; // 收货地址 } public class DiscountResult { private String status; // 状态码 private String message; // 信息 }2) 服务层
名称功能描述DiscountServiceDiscountResult sendDiscount(String uid,String number)模拟打折券服务YouKuMemberServicevoid openMember(String bindMobile , String number)模拟赠送优酷会员服务SmallGiftServiceBoolean giveSmallGift(SmallGiftInfo smallGiftInfo)模拟礼品服务 public class DiscountService { public DiscountResult sendDiscount(String uid, String number){ System.out.println("向用户发放打折券一张: " + uid + " , " + number); return new DiscountResult("200","发放打折券成功"); } } public class YouKuMemberService { public void openMember(String bindMobile , String number){ System.out.println("发放优酷会员: " + bindMobile + " , " + number); } } public class SmallGiftService { public Boolean giveSmallGift(SmallGiftInfo smallGiftInfo){ System.out.println("小礼品已发货,获奖用户注意查收! " + JSON.toJSON(smallGiftInfo)); return true; } }3) 控制层
名称功能描述DeliverControllerResponseResult awardToUser(AwardInfo awardInfo)按照类型的不同发放商品奖品类型: 1 打折券 ,2 优酷会员,3 小礼品 public class DeliverController { /** * 按照类型的不同发放商品 * 奖品类型: 1 打折券 ,2 优酷会员,3 小礼品 */ public void awardToUser(AwardInfo awardInfo){ if(awardInfo.getAwardType() == 1){ //打折券 DiscountService discountService = new DiscountService(); DiscountResult result = discountService.sendDiscount(awardInfo.getUid(), awardInfo.getAwardNumber()); System.out.println("打折券发放成功!"+ JSON.toJSON(result)); }else if(awardInfo.getAwardType() == 2){ //优酷会员 //获取用户手机号 String bindMobile = awardInfo.getExtMap().get("phone"); //调用service YouKuMemberService youKuMemberService = new YouKuMemberService(); youKuMemberService.openMember(bindMobile,awardInfo.getAwardNumber()); System.out.println("优酷会员发放成功!"); }else if(awardInfo.getAwardType() == 3){ /* 小礼品 封装收货用户信息 */ SmallGiftInfo smallGiftInfo = new SmallGiftInfo(); smallGiftInfo.setUserName(awardInfo.getExtMap().get("username")); smallGiftInfo.setOrderId(UUID.randomUUID().toString()); smallGiftInfo.setRelAddress(awardInfo.getExtMap().get("adderss")); SmallGiftService smallGiftService = new SmallGiftService(); Boolean isSuccess = smallGiftService.giveSmallGift(smallGiftInfo); System.out.println("小礼品发放成功!" + isSuccess); } } }4) 测试
通过单元测试,来对上面的接口进行测试,验证代码质量.
public class TestApi01 { //测试发放奖品接口 @Test public void test01(){ DeliverController deliverController = new DeliverController(); //1. 发放打折券优惠 AwardInfo info1 = new AwardInfo(); info1.setUid("1001"); info1.setAwardType(1); info1.setAwardNumber("DEL12345"); deliverController.awardToUser(info1); //2. 发放优酷会员 AwardInfo info2 = new AwardInfo(); info2.setUid("1002"); info2.setAwardType(2); info2.setAwardNumber("DW12345"); Map<String,String> map = new HashMap<>(); map.put("phone","13512341234"); info2.setExtMap(map); deliverController.awardToUser(info2); //2. 发放小礼品 AwardInfo info3 = new AwardInfo(); info3.setUid("1003"); info3.setAwardType(3); info3.setAwardNumber("SM12345"); Map<String,String> map2 = new HashMap<>(); map2.put("username","大远"); map2.put("phone","13512341234"); map2.put("address","北京天安门"); info3.setExtMap(map2); deliverController.awardToUser(info3); } }对于上面的实现方式,如果我们有想要添加的新的奖品时,势必要改动DeliverController的代码,违反开闭原则.而且如果有的抽奖接口出现问题,那么对其进行重构的成本会非常高.
除此之外代码中有一组if分支判断逻辑,现在看起来还可以,但是如果经历几次迭代和拓展,后续ifelse肯定还会增加.到时候接手这段代码的研发将会十分痛苦.
4.2.3 简单工厂模式 4.2.3.1 简单工厂模式介绍简单工厂不是一种设计模式,反而比较像是一种编程习惯。简单工厂模式又叫做静态工厂方法模式(static Factory Method pattern),它是通过使用静态方法接收不同的参数来返回不同的实例对象.
实现方式:
定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
适用场景: (1)需要创建的对象较少。 (2)客户端不关心对象的创建过程。
4.2.3.2 简单工厂原理简单工厂包含如下角色:
抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。具体产品 :实现或者继承抽象产品的子类具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。 4.2.3.3 简单工厂模式重构代码1) service
/** * 免费商品发放接口 **/ public interface IFreeGoods { ResponseResult sendFreeGoods(AwardInfo awardInfo); } /** * 模拟打折券服务 **/ public class DiscountFreeGoods implements IFreeGoods { @Override public ResponseResult sendFreeGoods(AwardInfo awardInfo) { System.out.println("向用户发放一张打折券: " + awardInfo.getUid() + " , " + awardInfo.getAwardNumber()); return new ResponseResult("200","打折券发放成功!"); } } /** * 小礼品发放服务 **/ public class SmallGiftFreeGoods implements IFreeGoods { @Override public ResponseResult sendFreeGoods(AwardInfo awardInfo) { SmallGiftInfo smallGiftInfo = new SmallGiftInfo(); smallGiftInfo.setUserPhone(awardInfo.getExtMap().get("phone")); smallGiftInfo.setUserName(awardInfo.getExtMap().get("username")); smallGiftInfo.setAddress(awardInfo.getExtMap().get("address")); smallGiftInfo.setOrderId(UUID.randomUUID().toString()); System.out.println("小礼品发放成,请注意查收: " + JSON.toJSON(smallGiftInfo)); return new ResponseResult("200","小礼品发送成功",smallGiftInfo); } } /** * 优酷 会员服务 **/ public class YouKuMemberFreeGoods implements IFreeGoods { @Override public ResponseResult sendFreeGoods(AwardInfo awardInfo) { String phone = awardInfo.getExtMap().get("phone"); System.out.println("发放优酷会员成功,绑定手机号: " + phone); return new ResponseResult("200","优酷会员发放成功!"); } }2) factory
/** * 具体工厂: 生成免费商品 **/ public class FreeGoodsFactory { public static IFreeGoods getInstance(Integer awardType){ IFreeGoods iFreeGoods = null; if(awardType == 1){ //打折券 iFreeGoods = new DiscountFreeGoods(); }else if(awardType == 2){ //优酷会员 iFreeGoods = new YouKuMemberFreeGoods(); }else if(awardType == 3){ //小礼品 iFreeGoods = new SmallGiftFreeGoods(); } return iFreeGoods; } }3)controller
public class DeliverController { //发放奖品 public ResponseResult awardToUser(AwardInfo awardInfo){ try { IFreeGoods freeGoods = FreeGoodsFactory.getInstance(awardInfo.getAwardTypes()); ResponseResult responseResult = freeGoods.sendFreeGoods(awardInfo); return responseResult; } catch (Exception e) { e.printStackTrace(); return new ResponseResult("201","奖品发放失败!"); } } }4) 测试
通过单元测试,来对上面的接口进行测试,验证代码质量.
public class TestApi02 { DeliverController deliverController = new DeliverController(); @Test public void test01(){ //1. 发放打折券优惠 AwardInfo info1 = new AwardInfo(); info1.setUid("1001"); info1.setAwardTypes(1); info1.setAwardNumber("DEL12345"); ResponseResult result = deliverController.awardToUser(info1); System.out.println(result); } @Test public void test02(){ //2. 发放优酷会员 AwardInfo info2 = new AwardInfo(); info2.setUid("1002"); info2.setAwardTypes(2); info2.setAwardNumber("DW12345"); Map<String,String> map = new HashMap<>(); map.put("phone","13512341234"); info2.setExtMap(map); ResponseResult result1 = deliverController.awardToUser(info2); System.out.println(result1); } @Test public void test03(){ //3. 发放小礼品 AwardInfo info3 = new AwardInfo(); info3.setUid("1003"); info3.setAwardTypes(3); info3.setAwardNumber("SM12345"); Map<String,String> map2 = new HashMap<>(); map2.put("username","大远"); map2.put("phone","13512341234"); map2.put("address","北京天安门"); info3.setExtMap(map2); ResponseResult result2 = deliverController.awardToUser(info3); System.out.println(result2); } } 4.2.3.4 简单工厂模式总结优点:
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:
增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。
4.2.4 工厂方法模式 4.2.4.1 工厂方法模式介绍工厂方法模式 Factory Method pattern,属于创建型模式.
概念: 定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
4.2.4.2 工厂方法模式原理工厂方法模式的目的很简单,就是封装对象创建的过程,提升创建对象方法的可复用性。
工厂方法模式的主要角色:
抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。抽象产品:定义了产品的规范,描述了产品的主要特性和功能。具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。我们直接来看看工厂方法模式的 UML 图:
4.2.4.3 工厂方法模式重构代码为了提高代码扩展性,我们需要将简单工厂中的if分支逻辑去掉,通过增加抽象工厂(生产工厂的工厂)的方式,让具体工厂去进行实现,由具体工厂来决定实例化哪一个具体的产品对象.
抽象工厂
public interface FreeGoodsFactory { IFreeGoods getInstance(); }具体工厂
public class DiscountFreeGoodsFactory implements FreeGoodsFactory { @Override public IFreeGoods getInstance() { return new DiscountFreeGoods(); } } public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory { @Override public IFreeGoods getInstance() { return new SmallGiftFreeGoods(); } }Controller
public class DeliverController { /** * 按照类型的不同发放商品 */ public ResponseResult awardToUser(AwardInfo awardInfo){ FreeGoodsFactory freeGoodsFactory = null; if(awardInfo.getAwardType() == 1){ freeGoodsFactory = new DiscountFreeGoodsFactory(); }else if(awardInfo.getAwardType() == 2){ freeGoodsFactory = new SmallGiftFreeGoodsFactory(); } IFreeGoods freeGoods = freeGoodsFactory.getInstance(); System.out.println("=====工厂方法模式========"); ResponseResult result = freeGoods.sendFreeGoods(awardInfo); return result; } }从上面的代码实现来看,工厂类对象的创建逻辑又耦合进了 awardToUser() 方法中,跟我们最初的代码版本非常相似,引入工厂方法非但没有解决问题,反倒让设计变得更加复杂了。
那怎么 来解决这个问题呢?
我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。
/** * 用简单方法模式实现: 工厂的工厂,作用是不需要每次创建新的工厂对象 **/ public class FreeGoodsFactoryMap { private static final Map<Integer,FreeGoodsFactory> cachedFactories = new HashMap<>(); static{ cachedFactories.put(1, new DiscountFreeGoodsFactory()); cachedFactories.put(2, new SmallGiftFreeGoodsFactory()); } public static FreeGoodsFactory getParserFactory(Integer type){ if(type == 1){ FreeGoodsFactory freeGoodsFactory = cachedFactories.get(1); return freeGoodsFactory; }else if(type ==2){ FreeGoodsFactory freeGoodsFactory = cachedFactories.get(2); return freeGoodsFactory; } return null; } }Controller
/** * 发放奖品接口 **/ public class DeliverController { /** * 按照类型的不同发放商品 */ public ResponseResult awardToUser(AwardInfo awardInfo){ //根据类型获取工厂 FreeGoodsFactory goodsFactory = FreeGoodsFactoryMap.getParserFactory(awardInfo.getAwardType()); //从工厂中获取对应实例 IFreeGoods freeGoods = goodsFactory.getInstance(); System.out.println("=====工厂方法模式========"); ResponseResult result = freeGoods.sendFreeGoods(awardInfo); return result; } }现在我们的代码已经基本上符合了开闭原则,当有新增的产品时,我们需要做的事情包括:
创建新的产品类,并且让该产品实现抽象产品接口创建产品类对应的具体工厂,并让具体工厂实现抽象工厂将新的具体工厂对象,添加到FreeGoodsFactoryMap的cachedFactories中即可,需要改动的代码改动的非常少. 4.2.4.4 工厂方法模式总结工厂方法模优缺点
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;缺点:
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。什么时候使用工厂方法模式
需要使用很多重复代码创建对象时,比如,DAO 层的数据对象、API 层的 VO 对象等。创建对象要访问外部信息或资源时,比如,读取数据库字段,获取访问授权 token 信息,配置文件等。创建需要统一管理生命周期的对象时,比如,会话信息、用户网页浏览轨迹对象等。创建池化对象时,比如,连接池对象、线程池对象、日志对象等。这些对象的特性是:有限、可重用,使用工厂方法模式可以有效节约资源。希望隐藏对象的真实类型时,比如,不希望使用者知道对象的真实构造函数参数等。【设计模式精讲】创建型模式之工厂方法模式(简单工厂、工厂方法)由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【设计模式精讲】创建型模式之工厂方法模式(简单工厂、工厂方法)”
上一篇
文字识别软件cnocr学习笔记