找回密码
 立即注册
首页 业界区 业界 设计模式:简单工厂、工厂方法与抽象工厂 ...

设计模式:简单工厂、工厂方法与抽象工厂

挫莉虻 2025-6-20 07:58:19
导航
1为什么要使用工厂模式?
2简单工厂
3工厂方法
3抽象工厂
4后记
1 为什么要使用工厂模式?
解耦
工厂模式的核心目的是将对象的创建与使用分离,先说对象的创建,我们习惯于在构造函数中进行一系列对象的初始化甚至逻辑处理,虽然这在开发中很常见,但如果仔细推敲,会发现其实并不符合常理。如果把汽车的发动机当作一个对象,设计图纸当作构造函数的参数,我们传入参数,然后发动机自己把自己生产出来了。是的,发动机自己生产了自己,是不是感觉有些怪异?但这确实是软件中的大部分情形,其实现实生活中的场景更符合逻辑,把图纸送到发动机生产工厂,由工厂完成发动机的生产制造。在软件开发中,大多数简单对象,并不需要使用工厂,但当一个对象的构造过程相对复杂、易变时,就需要考虑使用工厂将对象的创建过程解耦了。
再说对象的使用,在简单的小型项目中,直接使用new来获取对象简便快捷,并没有问题。但当项目具有一定规模,需要统一考虑扩展性(使用一个对象替换另一个对象),需要统一管理多个对象的创建过程、生命周期、依赖关系时,工厂就大有用武之地了。
抽象
解释一下抽象的概念,在软件设计中,抽象是为解耦和扩展而生的。举个例子,我们打开电脑机箱找到主板上的南北桥芯片,可以看到它们是完全焊接在主板上的,这种不可替换的设计是高耦合不可扩展的。我们再找到内存条,发现它们可以拆卸并更换为其它品牌,这种可替换的设计即为可扩展。再稍深入思考一下,主板上的内存条为什么能安装不同的品牌?原因是有相关技术标准,比如长宽尺寸,针脚数量,通信标准等,不同的内存条厂商,只要遵循标准生产出来的内存条就能安装到同一块主板上。在软件开发中,让主板支持不同厂商内存条的能力称之为主板具备可扩展性,定义内存条接口标准称之为抽象,根据标准生产主板和内存条称之为面向抽象编程(面向接口编程)。
在软件设计中,只要是工厂模式,不论简单工厂、工厂方法、还是抽象工厂,生产构造出来的都是抽象对象,而非具体对象。这条规则非常重要,我们在小型项目中,效率优先,可以直接new出来具体的对象使用,但即然使用了工厂模式,说明项目具有了一定的复杂程度和规模,此时就应该统一考虑可扩展性了。
可测试性
做过单元测试的小伙伴应该深有体会,越是封闭的对象越难以测试。这很容易理解,要对一个对象进行测试,就需要从外部对它注入各种条件,然后观察它的反应,如果条件都注入不进去,何来的反应呢?构造函数对测试而言,天生就是一个无法测试的封闭黑盒,而工厂模式把这个黑盒从对象中解耦了出来,从而允许测试条件的注入。

2 简单工厂
1.jpeg

在一个工厂中,通过向静态方法传递不同的参数,生产并返回不同的产品,即简单工厂,又形象的称之为静态工厂方法(Static Factory Method),示例代码如下:
  1. // 抽象产品
  2. public interface ICellphone
  3. { }
  4. // 具体产品
  5. public class Xiaomi1Cellphone : ICellphone
  6. { }
  7. public class Xiaomi2Cellphone : ICellphone
  8. { }
  9. // 小米1和小米2是早期的小米手机型号
  10. public enum CellphoneBrand
  11. {
  12.     Xiaomi1,
  13.     Xiaomi2
  14. }
  15. // 简单工厂
  16. public class CellphoneFactory
  17. {
  18.     // 通过不同的参数构造并返回不同的对象
  19.     public static ICellphone Build(CellphoneBrand brand)
  20.     {
  21.         switch (brand)
  22.         {
  23.             case CellphoneBrand.Xiaomi1:
  24.                 return new Xiaomi1Cellphone();
  25.             case CellphoneBrand.Xiaomi2:
  26.                 return new Xiaomi2Cellphone();
  27.             // 缺点:当产品扩展时存在修改,不符合开闭原则
  28.             // ...
  29.             default:
  30.                 return null;
  31.         }
  32.     }
  33. }
复制代码
1994年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides四位合著了《设计模式-可复用面向对象软软件的基础》,书中总结了23种常用的设计模式,后来成为了大名鼎鼎的GoF23(四人帮:Gang of Four)。
简单工厂虽然不在GoF23当中,但实际开发中也较为常见,它的优点是非常简单,缺点是当产品扩展时,需要修改工厂中的代码,不符合“开闭原则”。

3 工厂方法
2.jpeg

在介绍工厂方法模式前,先简单介绍一下“开闭原则”,开闭原则在上世纪80年代就被总结了出来,其核心思想是对修改关闭,对扩展开放。对修改关闭指的是应避免修改已存在的代码,因为这部分代码是经过测试的稳定代码,任何修改都可能引入新的BUG;对扩展开放指的是应允许增加新功能,因为软件存在的价值就是不断满足用户新的需求。开闭原则看似简单,实则包含了众多软件设计思想与实现细节,整个“SOLID原则”几乎都是围绕它展开的,后续专门写一篇文章介绍开闭原则,此处就不作详述了。
工厂方法模式的核心思想是每增加一种产品,就对应的增加一个工厂,为了贯彻这个思路,方便的为新增产品匹配工厂,它对工厂也进行了抽象,示例代码如下:
  1. // 抽象产品
  2. public interface ICellphone
  3. { }
  4. // 具体产品
  5. public class XiaomiCellphone : ICellphone
  6. { }
  7. public class RedmiCellphone : ICellphone
  8. { }
  9. // 抽象工厂
  10. public interface ICellphoneFactory
  11. {
  12.     ICellphone Build();
  13. }
  14. // 具体工厂
  15. public class XiaomiCellphoneFactory : ICellphoneFactory
  16. {
  17.     public ICellphone Build()
  18.     {
  19.         return new XiaomiCellphone();
  20.     }
  21. }
  22. public class RedmiCellphoneFactory : ICellphoneFactory
  23. {
  24.     public ICellphone Build()
  25.     {
  26.         return new RedmiCellphone();
  27.     }
  28. }
复制代码
工厂方法符合开闭原则,但它也存在缺陷:一个工厂只能生产一种产品,工厂数量会随着产品数量的增加而不断增加。

4 抽象工厂
3.jpeg

在具有一定规模的实际项目中,对象通常不会孤立的出现,而是以系列的方式出现,比如小米和红米,两个品牌下不会只有一款产品,而会有一系列产品:手机、电视、电脑、智能音箱、扫地机器人等等。抽象工厂模式专为生产一系列商品而设计,它解决了工厂方法中工厂数量随着产品数量不断增加的弊端,也更贴合实际的项目场景,示例代码如下:
  1. // 抽象产品
  2. public interface ICellphone
  3. { }
  4. public interface ITelevision
  5. { }
  6. // 具体产品
  7. public class XiaomiCellphone : ICellphone
  8. { }
  9. public class RedmiCellphone : ICellphone
  10. { }
  11. public class XiaomiTelevision : ITelevision
  12. { }
  13. public class RedmiTelevision : ITelevision
  14. { }
  15. // 抽象工厂
  16. public interface IFactory
  17. {
  18.     ICellphone BuildCellphone();
  19.     ITelevision BuildTelevision();
  20. }
  21. // 具体工厂,生产一个系列的产品
  22. public class XiaomiFactory : IFactory
  23. {
  24.     public ICellphone BuildCellphone()
  25.     {
  26.         return new XiaomiCellphone();
  27.     }
  28.     public ITelevision BuildTelevision()
  29.     {
  30.         return new XiaomiTelevision();
  31.     }
  32. }
  33. public class RedmiFactory
  34. {
  35.     public ICellphone BuildCellphone()
  36.     {
  37.         return new RedmiCellphone();
  38.     }
  39.     public ITelevision BuildTelevision()
  40.     {
  41.         return new RedmiTelevision();
  42.     }
  43. }
复制代码
5 后记
很早之前就想将GoF23中的设计模式以这种图、文、代码的方式整理成文,一直拖延直到最近注册了心仪的域名,博客园的这个20年前的账号也重获新生,终于开始将软件开发这些年的一些经验整理成文。后续会陆续补充完整GoF23,敬请关注。

本文同步发步于个人站点:wubayue.com


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册