设计模式之工厂模式(Factory Pattern)

工厂模式是最常用的设计模式之一,它属于创建型设计模式;工厂模式 提供了一种创建对象的最好方式 (如果你有更好的方式就当我没说);它的主要作用是让对象的创建与对象的使用分离开来,即使用单独的类来 封装创建实例的逻辑。
工厂模式又可以细分为:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

1、简单工厂模式(Simple Factory Pattern)

又称为静态工厂方法(Static Factory Method)模式;在简单工厂模式中,通常包含一个静态方法,可以根据参数的不同返回不同类的实例。
简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

  • 简单工厂模式包含以下几种角色:
    • 工厂:负责创建所有具体产品类的实例,工厂类中的静态方法要能够被其它类直接在外界调用,以获得产品对象。
    • 抽象产品类:是所有具体产品的父类,负责定义其它具体产品的所共有的公共接口(子类共有的行为)
    • 具体产品类:继承自抽象产品类,是工厂角色的创建目标。

如果上面说的不好理解的话,那就来个栗子消化一下吧。

在一个游戏中,有三种小怪: MobAMobBMobc ;
在游戏中,他们都可以攻击主角。这时候,我们就可以使用简单工厂模式来控制它们的 出场(实例生成)。


首先,我们应该创建一个抽象怪物类 Mob ,让上面那三个小怪类继承自Mob。然后用Mob当做工厂中静态方法的返回对象。
不太懂得话,就看看代码吧(c#):

1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
namespace FactoryPattern
{
//怪物工厂
class MobFactory
{
//获取对象实例的静态方法
public static Mob GetMob(string mobName)
{
switch (mobName) {
case "MobA": return new MobA();
case "MobB": return new MobB();
case "MobC": return new MobC();
default: return null;
}
}
}

////使用接口实现抽象类会更好
//interface I_Mob
//{
// //定义攻击接口(规定每个继承自此抽象类的怪物都能进行攻击)
// void Attack();
//}

//抽象怪物类
abstract class Mob
{
//定义攻击接口(规定每个继承自此抽象类的怪物都能进行攻击)
public abstract void Attack();
}
//小怪A
class MobA : Mob
{
//重写Acctak方法
public override void Attack()
{
Console.WriteLine("MobA 进行攻击");
}
}
//小怪B
class MobB : Mob
{
//重写Acctak方法
public override void Attack()
{
Console.WriteLine("MobB 进行攻击");
}
}
//小怪C
class MobC : Mob
{
//重写Acctak方法
public override void Attack()
{
Console.WriteLine("MobC 进行攻击");
}
}

class Test
{
static void Main(string[] args)
{
Mob mob = MobFactory.GetMob("MobA");
mob.Attack(); //MobA进行攻击
mob = MobFactory.GetMob("MobB");
mob.Attack(); //MobB进行攻击

Console.ReadKey();
}
}
}

简单工厂模式分析

  • 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
  • 对于简单工厂模式,由于其方法是静态的,因此使用起来比较方便。
  • 简单工厂模式虽然满足单一职则原则,不过工厂类的职责相对过重,而且增加新的产品时需要修改工厂类的判断逻辑,不符合开闭原则。

模式优缺点及使用场景

  • 优点

    • 简单工厂模式实现了对责任的分割,它提供了专门的工厂类用于创建对象,降低系统的耦合度。
    • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。
  • 缺点

    • 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
    • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
    • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
    • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
  • 使用场景

    当需要创建的对象较少且以后基本上不要增加新的对象时,用这个工厂方式还是不错的。

2、工厂方法模式(Factory Pattern)

又被称多态性工厂模式。在这个模式中,不再使用一个类创建所有的产品实例,而是实现一个抽象的工厂父类角色,然后为每个产品都实现一个具体的工厂子类,抽象工厂父类角色,仅负责给出具体工厂子类必须实现的接口,而具体的创建对象的逻辑在子类中实现。

  • 工厂方法模式包含的角色:
    • 抽象工厂类:所有具体工厂的父类,规定工厂子类应实现哪些接口。
    • 具体工厂类:继承自抽象工厂类,用于创建一个特定的产品。
    • 抽象产品类:所有具体产品的父类,负责定义其它具体产品的所共有的公共接口
    • 具体产品类:继承自抽象产品类,是具体工厂类角色的创建目标。

根据上述例子实现的具体代码为:

1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
namespace FactoryPattern
{
//怪物接口
interface Mob
{
void Attack();
}
//小怪A
class MobA : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobA 进行攻击");
}
}
//小怪B
class MobB : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobB 进行攻击");
}
}
//小怪C
class MobC : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobC 进行攻击");
}
}

//抽象工厂接口
interface Factory
{
Mob GetMob();
}
//创建怪物A的工厂
class MobA_Factory : Factory
{
public Mob GetMob()
{
return new MobA();
}
}
//创建怪物B的工厂
class MobB_Factory : Factory
{
public Mob GetMob()
{
return new MobB();
}
}
//创建怪物C的工厂
class MobC_Factory : Factory
{
public Mob GetMob()
{
return new MobC();
}
}

class Test
{
static void Main(string[] args)
{
//获得小怪A
Factory factory = new MobA_Factory();
Mob mob = factory.GetMob();
mob.Attack();
//获得小怪B
factory = new MobB_Factory();
mob = factory.GetMob();
mob.Attack();
Console.ReadKey();
}
}
}

工厂方法模式分析

在这个模式中,将每个具体产品的创建细节分配给每个具体的工厂来实现,这样做不仅使工厂方法具有简单工厂方法的优点,还解决了简单工厂模式职责过重的问题。不过由于每个产品类都要对应一个工厂类,因此使用此模式时,会在一定程度上增加系统的复杂性。

优点

工厂方法模式除了具有简单工厂模式的优点外,还具有以下优点:

  • 进一步的细化了工厂类的职责,真正的实现了在创建实例这块的高内聚、低耦合。
  • 当一个工厂出现问题时不会影响到其他工厂,提高了程序的健壮性。
  • 提高了程序的灵活性,当新添加一个产品时,只需添加一个产品类和相应产品的工厂类即可。

缺点

  • 要为每个产品类创建具体工厂,增加了代码量。(个人感觉有其优点相比,这点缺点可以忽略了)

应用场景

产品等级结构与产品族

  • 产品等级结构:即产品的继承结构,如在上面所用到的例子中,抽象怪物类Mob与小怪MobA、小怪MobB、小怪MobC之间便构成了一个产品等级结构,抽象怪物类是父类,而不同种类的小怪是其子类。
  • 产品族:在抽象工厂模式中会提到,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。

3、抽象工厂模式

抽象工厂模式是当有多个抽象产品时所使用的一种工厂模式。它和工厂方法模式很像,可以把工厂方法模式当成是产品族为1时的抽象工厂模式。

  • 抽象工厂模式包含的角色:
    • 抽象工厂类:所有具体工厂的父类,实现对具体工厂的规范。
    • 具体工厂类:继承自抽象工厂类,用于创建某个产品族中的的产具体产品。
    • 抽象产品类:所有具体产品的父类,负责定义其它具体产品的所共有的公共接口
    • 具体产品类:继承自抽象产品类,是具体工厂类角色的创建目标之一。

修改一下上面那个例子,除了怪物类外,现在还要新添加一个武器类,武器类中包括武器WpA、WpB、WpC;怪物MobA使用WpA,怪物MobB使用WpB,怪物MobC使用WpC;
使用抽象工厂模式实现怪物和武器生成的代码如下:

1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//怪物接口
interface Mob
{
void Attack();
}
//小怪A
class MobA : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobA 进行攻击");
}
}
//小怪B
class MobB : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobB 进行攻击");
}
}
//小怪C
class MobC : Mob
{
//重写Acctak方法
public void Attack()
{
Console.WriteLine("MobC 进行攻击");
}
}

//武器接口
interface Wp
{
//攻击加成
void AtkAdd();
}
//武器WpA
class WpA : Wp
{
public void AtkAdd()
{
Console.WriteLine("MobA专属武器,MobA提高了攻击力");
}
}
//武器WpA
class WpB : Wp
{
public void AtkAdd()
{
Console.WriteLine("MobB专属武器,MobB提高了攻击力");
}
}
//武器WpC
class WpC : Wp
{
public void AtkAdd()
{
Console.WriteLine("MobC专属武器,MobC提高了攻击力");
}
}

//工厂接口
interface Factory
{
//获取怪物实例的方法
Mob GetMob();
//获取武器实例的方法
Wp GetWp();
}

//A类怪物武器产品族工厂
class A_Factory : Factory
{
//获取怪物实例的方法
public Mob GetMob() {
return new MobA();
}
//获取武器实例的方法
public Wp GetWp()
{
return new WpA();
}
}

//A类怪物武器产品族工厂
class B_Factory : Factory
{
//获取怪物实例的方法
public Mob GetMob()
{
return new MobB();
}
//获取武器实例的方法
public Wp GetWp()
{
return new WpB();
}
}

//A类怪物武器产品族工厂
class C_Factory : Factory
{
//获取怪物实例的方法
public Mob GetMob()
{
return new MobC();
}
//获取武器实例的方法
public Wp GetWp()
{
return new WpC();
}
}


class Test
{
static void Main(string[] args)
{
//A类产品族工厂
Factory factory = new A_Factory();
Mob moba = factory.GetMob();
Wp wpa = factory.GetWp();
wpa.AtkAdd();
moba.Attack();

//B类产品族工厂
factory = new C_Factory();
Mob mobb = factory.GetMob();
Wp wpb = factory.GetWp();
wpb.AtkAdd();
mobb.Attack();

Console.ReadKey();
}
}

抽象工厂模式分析

抽象工厂模式符合单一职责原则、开闭原则、里氏替换原则、依赖倒置原则。它为具有多个产品结构的程序提供了一种获得产品实例的方法。

抽象工厂的优缺点

  • 优点:
  • 工厂方法模式除了具有简单工厂模式的优点外,还具有以下优点:

    • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
    • 增加新的产品族时很方便,只需添加生产相应产品族实例的工厂即可,无须修改已有系统。
  • 缺点:

    • 当增加新的产品等级结构时,需要对抽象工厂和具体工厂类进行修改,不符合开闭原则。

 评论