简单工厂模式(Simple Factory Pattern)
介绍:简单工厂模式不能说是一个设计模式,说它是一种编程习惯可能更恰当些。因为它至少不是Gof23种设计模式之一。但它在实际的编程中经常被用到,而且思想也非常简单,可以说是工厂方法模式的一个引导,所以我想有必要把它作为第一个讲一下。
引入:
我们在编程的时候,每当"new"一个对象之后,这个对象就依赖于这个类了。如果在后期的维护过程中由于某些原因需要修改一下这个类,则唯一的做法就是打开源代码,进行修改,修改所有与这个对象有关的操作。这对我们是非常不利的。
问题出来了:对象不能应对“具体实例化类型”的变化
解决思路:套用一下李建忠李老师的话,封装变化点,哪里变化,封装哪里。在这个例子中,要实例化的类变了,就将实例化这个操作封装起来,我们可以把"new"这个操作移交一个具体的类,由它去负责根据我们的条件创建具体类的实例,也就是下面要说的“简单工厂模式”。
定义:
专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类或接口。简单工厂模式又称为静态工厂方法(Static Factory Method)模式,属于类的创建型模式,通常根据一个条件(参数)来返回不同的类的实例。
意图:
提供一个类,由它负责根据一定的条件创建某一具体类的实例
参与者:
·
是简单工厂模式的核心,它负责实现创建所有具体产品类的实例。工厂类可以被外界直接调用,创建所需的产品对象。
·
是所有具体产品角色的父类,它负责描述所有实例所共有的公共接口。
·
继承自抽象产品角色,一般为多个,是简单工厂模式的创建目标。工厂类返回的都是该角色的某一具体产品。
UML图:
现实生活中例子:
每次参加不同的聚会或者与不同的人见面,可能穿的衣服是不一样的,比如,你今天上午要与你的一个新客户见面,你可能会对你的老婆说:老婆,给拿件商务装(参数),我要去见我的一个客户,你老婆(工厂类)接到你的请求(商务装参数)后,从衣柜中取出一件商务装(具体产品),交给你。整个过程就完成了。
分析:
你可能根据不同的条件,要的衣服是不一样的,但要的衣服都是已经在你的衣柜中存在的。并且,每件上衣它们都属于同一种抽象,即它们可以从一个抽象类或接口中继承,这此衣服各自都有一定特征,这些都是条件。然后你要的时候,就可以向你老婆说一种特征,她就会根据这个特征为你服务了。这就是典型的简单工厂模式 的应用。
1
2
3
4
5
6
7
非常简单,是吧?这里我只是举一个仅仅能说明问题的例子,在具体的项目中,可能是很复杂的哦。。
具体产品类代码
1 namespace
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 }
1 namespace
10
11
12
13
14
15
16
17
18
19
20
21 }
再看一下客户在调用的时候的代码
10
11
12
13
14
15
16
17
18
19
20
21
22
23
到这里,代码就完成了。
在客户端的代码中有我们就可以根据具体的参数,返回我们希望返回的对象,将"new"操作推迟到工厂类中实现。
这里,参数我直接写上了,我们其实可以将这个参数写到一个xml文件中,如app.config文件中,动态的读出来,需要穿另外一种衣服了,只需要打开 app.config文件,修改里面的值就行了,不需要项目重新编译。这样这个小程序就能够适应一定的变化了(在上传上去的代码中我会修改一下)。其实它也是设计模式正要解决的问题,在不修改代码的情况下,使项目能够适应一定的客户需求变化。注意,是一定的,并非全部。
·
·
·
缺点:
·
·
应用情景
·
·
介绍:
在简单工厂模式中,我们提到,工厂方法模式是简单工厂模式的一个延伸,它属于Gof23中设计模式的创建型设计模式。它解决的仍然是软件设计中与创建对象有关的问题。它可以更好的处理客户的需求变化。
工厂方法模式(Factory Method Pattern)
引入
我们继续来说"new"的问题,我们在简单工厂模式中,将实例化对象的工作推迟到了专门负责创建对象的工厂类中,这样,在我们事先预知的情况下,可以根据我们的需要动态创建产品类。但是,我们的预知是有限的, 客户的变化可能是无限的。所以,就出现了问题,一旦客户的变化超越了我们的预知,我们就必须修改我们的源代码了。这是设计模式所不允许的,怎么办呢?工厂方法模式正是解决此类问题的。
问题:具体工厂类的创建工作不能满足我们的要求了,创建的工作变化了
解决思路:哪里变化,封装哪里。把具体工厂封装起来。
定义
工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂模式(Polymorphic Factory),在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化(创建)哪一个类。
意图
定义一个用户创建对象的接口,让子类决定实例化哪一个类,工厂方法模式使一个类的实例化延迟到其子类。
参与者
·
定义产品的接口
·
实现接口Product的具体产品类
·
声明工厂方法(FactoryMethod),返回一个产品
·
实现FactoryMethod工厂方法,由客户调用,返回一个产品的实例
工厂方法模式UML图
现实生活中的例子
为了方便大家理解,我仍然举穿衣服方面的一个例子。这个例子与简单工厂模式中的那个例子有些不同。
据说清朝有个皇帝穿衣非常的奢侈,每种衣服(具体产品类)由一宫女(具体工厂类)专门负责,这样一来,每增加一种衣服(具体产品类),就要多出一个宫女(具体工厂类),但是他们各负其责,互不影响。皇帝之所以这样做,是因为针对穿衣服这件事来说,可扩展性是非常强的( )。
分析
实现的功能:可以根据皇帝的要求,动态的创建(由宫女去拿)已存在的具体产品(衣服),如果皇帝的要求太苛刻,这种衣服还没有,只需要 增加一个宫女,一个衣服就能够满足他的要求了。每个宫女只负责一种衣服(高内聚),要增加一种衣服,对于以前的所有宫女与衣服来说,都不会受到影响(设计模式中所期望的)。说到这里,是不是明白了工厂方法模式所能解决的问题及其应用了?呵呵。。你一定在想,比简单工厂模式灵活性高吧。。
抽象工厂角色代码
1 namespace
10 }
抽象产品角色代码
1 namespace
10 }
具体工厂角色代码
1 namespace
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 }
具体产品角色代码
1 namespace
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 }
25
客户端代码
1 namespace
10
11
12
13
14
15
16
17
18
19
20 }
客户端代码需要注意的两个地方:
1,把具体工厂类类名称写在了应用程序配置文件中,方便修改
2,用到了反射,利用.NET提供的反射可以根据类名来创建它的实例,非常方便
由反射想到的:
下面这一段内容不是计划要写的。
如果在具体工厂中,每次new的对象都是一个,而且这些类是继承自抽象产品接口的,那么我们用简单工厂模式也可以实现动态的增加具体产品类。这样来做,在简单工厂模式中最核心的部分----工厂类不要根据传来的条件去动态创建产品类,利用反射机制去创建。把要实例化的类名放在应用程序配置文件中,呵呵。。这样利用.NET特有的反射就可以用简单工厂模式解决更多的问题了,工厂方法模式的一部分问题也是可以通过 “这样的简单工厂模式”解决的,在需要增加具体产品类时,不用增加具体工厂,是不是简单一些呀。下去试一下。。。
优点:
·
·
缺点:
·
应用情景:
·
·
·
工厂方法模式在ASP.NET HTTP通道中的应用,TerryLee在他的那篇文件中写的非常好,推荐去看一下。
抽象工厂模式(Abstract Factory Pattern)
引入:
在前面介绍的两个创建型模式里面,我们解决的都是有关"new"的问题,用它们来避免显式指定类创建对象。我写的也非常简单易懂,相信看过的朋友们都应该对简单工厂模式、工厂方法模式的意图、所能解决的问题及适用情景有一定的了解了。但是若要达到灵活运用,什么时候用,怎样用合适还不是看一篇文章就能解决的问题。呵呵..这需要你对OO的理解程度,你的项目开发经验等等许多方面的积累。一起努力喔。。
好了,咱们言归正传,通过对这两个模式的了解,我们掌握一种思想,就是在创建一个对象时,需要把容易发生变化的地方给封装起来,来控制变化(哪里变化,封装哪里),以适应客户的变动,项目的扩展。但是,我们在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作,同时由于需求的变化,这“一系列相互 依赖的对象”也要改变,如何应对这种变化呢?如何像简单工厂模式、工厂方法模式一样绕过常规的"new",然后提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?可能有人会说,你也可以将这些对象一个一个通过工厂方法模式来解决呀?但是,我们试想,既然是一系列相互依赖的对象,它们是有联系的,每个对象都这样解决,你又如何来保证他们的联系呢?举一个例子:Windows桌 面主题,当你更换一个桌面主题的时候,系统的开始按钮、任务栏、菜单栏、工具栏等等都变了,而且是一起变的,他们的色调都还很一致,难道类似这样的问题,怎么来解决呢?它的天敌就是抽象工厂模式。
意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
参考者:
也就是该模式中的各个类或对象之间的关系:
·
声明生成一系列抽象产品的方法
·
执行生成一系列抽象产品的方法,生成一系列具体的产品
·
为这一系列的某一种产品声明接口
·
定义具体工厂生成的具体产品的对象,实现产品接口
·
我们的应用程序客户端(不要理解成人),使用抽象产品和抽象工厂生成对象。
抽象工厂模式在生活中的实例
咱们继续拿怎么穿衣服来说明这个抽象工厂模式。
就拿你来说吧。工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。咱们假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在你的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用OO的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
分析:
要好好去读上面那个实例,虽然有点绕嘴,其实只要用心去读,分清了抽象工厂模式的各个角色,对理解设计模式是非常重要的。理解头绪,然后接合简单工厂模式、工厂方法模式对工厂家族的了解,再加上抽象工厂模式的意图,头脑中差不多有一个雏型了吧。好了,咱们一起来分析一下。。
先把各个角色揪出来。
抽象工厂:虚拟的衣柜,它只是个概念而已。在项目中可能是一个接口或抽象类,定义规则,取出上衣,裤子。
具体工厂:具体的存在的衣柜,它用于存放某一种成套的衣服,换句话说,这种成套的衣服都是从这个衣柜中取出的。在项目中继承于抽象工厂,实现抽象工厂中的方法,取出具体产品,某一件上衣,某一条裤子。
抽象产品:虚拟的衣服,也只是个概念。在项目中可能是多个接口或抽象类,定义规则,有什么特性,起什么作用。
具体产品:具体的实际存在的产品,它指的就是用于组装成某一套衣服的某一件上衣或裤子。它继承自某一个抽象产品。实
它们之间怎么联系呢?客户在用的时候,依赖的又是什么呢?
客户在要的时候,首先要说出你要的什么系列的衣服,然后根据它的要求生成一个具体工厂的实例,剩下的工作就都是这个倒霉的具体工厂了,它会根据自己的实现生成一个上衣,生成一个裤子,然后把它交给客户。客户在这一过程中并不知道具体工厂都做了什么。也就是说,客户只依赖于抽象工厂和抽象产品了。在初始化的 时候会用到一次具体工厂类名,我们根据.NET特有的反射机制又可以把这个在客户端唯一的具体的非抽象类放到一个应用程序配置文件中,防止它变化。
具体代码如下:
抽象工厂角色:
1 namespace
10
11 }
12
抽象产品角色:
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 }
39
具体工厂角色:
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 }
具体产品角色:(注意:我并没有把所有的具体产品类都列出来,由于简单,可以推想出剩余的产品类,详见附件)
1 namespace
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 }
客户端代码:
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 }
43
app.config文件
1 <configuration>
2
3
4
5
6
7
8 </configuration>
这样,代码就完成了。
小结一下:
抽象工厂模式堪称gof23种设计模式精典模式之一,它能够解决诸如:通过显示指定类创建对象,紧耦合,对对象表示或实现的依赖等等一些问题,有关设计模式的设计原则,所能解决的问题,详见OO与设计模式的原则、目标 。
抽象工厂模式适用于对“一系列相互依赖的对象”的创建工作,这些对象是相互依赖的,是有联系的。如果仅为一个对象的创建则用简单工厂模式或工厂方法模式完全可以实现,没有必要用抽象工厂模式。
由于抽象工厂模式的客户端只依赖于抽象工厂,抽象产品,在初始化过程中仅用到一次具体工厂我们又把它放在了app.config中了,完全依赖接口,这样不仅在系统的扩展性方面好,而且可以提高团队开发效率。两个团队只要彼此了解定义的接口,抽象类,可以并行开发。举个例子,就拿博客园来说吧,我们在用自 己的博客空间时,可以随时的换皮肤,这个换皮肤是不是典型的抽象工厂模式吗?如果是,它的各个角色又是什么呢?我认为是的。换一下皮肤,你博客页面上的各个样式都变了,而且这里各个样式都同属于你选定的这一个皮肤。而每个样式都又是独立的,它们组合起来就成了一款皮肤。我们来揪出来各个角色。
抽象工厂:皮肤
抽象产品:样式
具体工厂:某一款皮肤,皮肤名即为具体工厂的类名
具体产品:某一个样式。
虽然不存在这样的接口与类,但是它确实是抽象工厂模式的一个应用。抽象工厂制定都有哪些样式名,而具体工厂来实现这些样式名中的样式,而具体工厂中用到的各个样式都是一个具体产品。这也是我的理解,如兄弟们有不同的见解,欢迎发表意见,共同探讨。
确定过各个角色之后,就可以说一下为什么提高效率了。不论dudu在设计博客园时用什么工具或语言,它与泸江博客只要约定好所有用到的样式名就可以了。而泸江博客就可以根据要求单独去做每一款皮肤了。
优点:
·
·
·
缺点:
·
应用情景:
·
·
·
·
应用场景举例:
·
·
·