《软件设计精要与模式》之Factory Method模式
声明:我发布本章节主要是和大家分享这本书里的知识点,如果涉及到了侵权请作者或是相关人士留言,我会删除。
Factory Method模式是应用最为广泛的设计模式,毕竟他负责了一系列对象的创建,而对象的创建正是面向对象编程中最为繁琐的行为。《设计模式》一书写到,“Factory Method模式使一个类的实例化延迟到子类。”准确的说,Factory Method模式是将创建对象实例的责任,转移到了工厂类中,并利用抽象原理,将实例化行为延迟到具体工厂类。
"嫁祸江东"之计
在面向对象的设计中,对象的管理是其核心所在,其中,对象的创建是对象管理的第一步。对象的创建非常简单,在C#中,只需使用new操作符调用对象的构造方法即可,因此,管理对象最重要的是掌握创建对象的时机。
我们首先从对象的特征来看。代表抽象意思的类型,如接口和抽象类,是不能创建对象实例的,这意味着我们要创建的对象都是与对象类型有关。也就是说,对象的创建工作必然涉及到设计中的实现细节,从而导致创建者与具体的被创建这之间耦合度增强。举例来说,如果在一个项目中需要创建一些图形对象,例如Circle,Square图形,它们的结构如下图所示。

这个类结构非常符合面向对象思想,他通过IShape接口将Square和Circle对象抽象出来。根据多态的原理,我们可以在程序中利用IShape接口代替具体的Square和Circle类,从而将具体的对象类型绑定留到运行时,但由于接口对象是不能创建的,一旦项目需求创建IShape类型的对象,就必然要针对具体的类对象进行创建操作。例如:
IShape shape=new Square();
如果是开发一个图形工具,诸如Square和Circle之类对象的创建工作必然非常频繁。可以设想,在这个项目的各个摸块中,如果大量充斥着如上代码行,那么导致的结果是各个模块无法与Square具体对象解耦。当我们需要改变创建的对象为Circle时,就必须修改所以调用new Square()操作的模块。这无疑加大了修改的工作量,同时也导致了项目的不可扩展性,以及模块的不可重用性;而对于图形对象的抽象IShape接口来说,也成为了不必要且失败的设计。
我们的目的是要将这种变化给项目带来的灾难枪杀在摇篮之中,此时就需要利用“封装变化”的原理。我们可以引入Factory Method模式对对象的创建行为进行封装。IShape类型的对象是工厂要生产的产品。在Factory Method模式中,工厂对象的结构应与产品的结构平行,并与之一一对应。既然具体的产品对象有两种,相对应的具体工厂对象也应该是两个,既SquareFactory和CircleFactory;同时,还应该为这两个工厂抽象出一个共同的工厂接口IShapeFactory。

实现代码如下:
Factory Method模式实现了对象的创建封装,将创建具体产品对象的代码全部转移到了各自的工厂对象中,并在CreateShape方法中实现,整个结构如下图:

CreateShape方法的返回对象是IShape接口类型,这就有效地避免了工厂对象与具体产品对象的依赖,使得创建者与被创建者实现了解耦.
我想,真正的智者是不会被表象所迷惑的.事实上,Factory Method模式的引入,是一种"嫁祸江东"的伎俩,他依旧无法摆脱的职责转交给工厂对象,然而在工厂类的结构中,依旧存在具体的工厂类对象.我门解出了模块与具体图象的依赖,却又增加了对具体工厂对象的依赖,这会给我们的设计带来何种益处?
让我们从对象的创建频率来分析.对于一个图形分析工具而言,IShape对象的创建无疑是频繁的,最大的可能行就在这个项目的各个模块中都可能存在创建IShape对象的需要.工厂对象则不尽然,我门完全可以集中一个模块中,初始化该工厂对象,并在需要IShape对象的时候,直接调用工厂实例CreateShape方法就可以达到目的.
因此,引入工厂对象并不是简单的为产品建立相应的工厂,而是要注意划分各个模块的职责,将工厂对象的创建放到合适的地方。最佳方案莫过于将创建工厂对象的职责集中起来,放到一个模块中;而不是在需要创建产品时,才创建工厂对象。错误的例子是在创建产品时将工厂对象的创建与产品对象的创建放在一起,然后分布在各个模块中。这对于引入Factory Method模式来说,无非是画蛇添足。
示例代码下载:FactoryMetod.rar
Factory Method模式是应用最为广泛的设计模式,毕竟他负责了一系列对象的创建,而对象的创建正是面向对象编程中最为繁琐的行为。《设计模式》一书写到,“Factory Method模式使一个类的实例化延迟到子类。”准确的说,Factory Method模式是将创建对象实例的责任,转移到了工厂类中,并利用抽象原理,将实例化行为延迟到具体工厂类。
"嫁祸江东"之计
在面向对象的设计中,对象的管理是其核心所在,其中,对象的创建是对象管理的第一步。对象的创建非常简单,在C#中,只需使用new操作符调用对象的构造方法即可,因此,管理对象最重要的是掌握创建对象的时机。
我们首先从对象的特征来看。代表抽象意思的类型,如接口和抽象类,是不能创建对象实例的,这意味着我们要创建的对象都是与对象类型有关。也就是说,对象的创建工作必然涉及到设计中的实现细节,从而导致创建者与具体的被创建这之间耦合度增强。举例来说,如果在一个项目中需要创建一些图形对象,例如Circle,Square图形,它们的结构如下图所示。

这个类结构非常符合面向对象思想,他通过IShape接口将Square和Circle对象抽象出来。根据多态的原理,我们可以在程序中利用IShape接口代替具体的Square和Circle类,从而将具体的对象类型绑定留到运行时,但由于接口对象是不能创建的,一旦项目需求创建IShape类型的对象,就必然要针对具体的类对象进行创建操作。例如:

如果是开发一个图形工具,诸如Square和Circle之类对象的创建工作必然非常频繁。可以设想,在这个项目的各个摸块中,如果大量充斥着如上代码行,那么导致的结果是各个模块无法与Square具体对象解耦。当我们需要改变创建的对象为Circle时,就必须修改所以调用new Square()操作的模块。这无疑加大了修改的工作量,同时也导致了项目的不可扩展性,以及模块的不可重用性;而对于图形对象的抽象IShape接口来说,也成为了不必要且失败的设计。
我们的目的是要将这种变化给项目带来的灾难枪杀在摇篮之中,此时就需要利用“封装变化”的原理。我们可以引入Factory Method模式对对象的创建行为进行封装。IShape类型的对象是工厂要生产的产品。在Factory Method模式中,工厂对象的结构应与产品的结构平行,并与之一一对应。既然具体的产品对象有两种,相对应的具体工厂对象也应该是两个,既SquareFactory和CircleFactory;同时,还应该为这两个工厂抽象出一个共同的工厂接口IShapeFactory。

实现代码如下:
1
namespace DonOfDesign.FactoryPattern.GraphicLib
2
{
3
public interface IShapeFactory
4
{
5
IShape CreateShape();
6
}
7
}
8
----------------------------------------------
9
namespace DonOfDesign.FactoryPattern.GraphicLib
10
{
11
public class SquareFactory : IShapeFactory
12
{
13
public IShape CreateShape()
14
{
15
return new Square();
16
}
17
}
18
}
19
----------------------------------------------
20
namespace DonOfDesign.FactoryPattern.GraphicLib
21
{
22
public class CircleFactory : IShapeFactory
23
{
24
public IShape CreateShape()
25
{
26
return new Circle();
27
}
28
}
29
}
30
31

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

Factory Method模式实现了对象的创建封装,将创建具体产品对象的代码全部转移到了各自的工厂对象中,并在CreateShape方法中实现,整个结构如下图:

CreateShape方法的返回对象是IShape接口类型,这就有效地避免了工厂对象与具体产品对象的依赖,使得创建者与被创建者实现了解耦.
我想,真正的智者是不会被表象所迷惑的.事实上,Factory Method模式的引入,是一种"嫁祸江东"的伎俩,他依旧无法摆脱的职责转交给工厂对象,然而在工厂类的结构中,依旧存在具体的工厂类对象.我门解出了模块与具体图象的依赖,却又增加了对具体工厂对象的依赖,这会给我们的设计带来何种益处?
让我们从对象的创建频率来分析.对于一个图形分析工具而言,IShape对象的创建无疑是频繁的,最大的可能行就在这个项目的各个模块中都可能存在创建IShape对象的需要.工厂对象则不尽然,我门完全可以集中一个模块中,初始化该工厂对象,并在需要IShape对象的时候,直接调用工厂实例CreateShape方法就可以达到目的.
因此,引入工厂对象并不是简单的为产品建立相应的工厂,而是要注意划分各个模块的职责,将工厂对象的创建放到合适的地方。最佳方案莫过于将创建工厂对象的职责集中起来,放到一个模块中;而不是在需要创建产品时,才创建工厂对象。错误的例子是在创建产品时将工厂对象的创建与产品对象的创建放在一起,然后分布在各个模块中。这对于引入Factory Method模式来说,无非是画蛇添足。
示例代码下载:FactoryMetod.rar
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述