抽象工厂模式(Abstract Factory )
概述
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?这就是我们要说的抽象工厂模式。
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
例子
1.某游戏的场景。
2.某款汽车的外观风格。
3.某软件的外观皮肤。
UML类图

代码:对应UML类图
首先,创建抽象工厂类,抽象产品A类,抽象产品B类。面向接口(抽象)编程
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
4
/// <summary>
5
/// 抽象工厂
6
/// </summary>
7
public abstract class AbstractFactory
8
{
9
/// <summary>
10
/// 创建产品A
11
/// </summary>
12
/// <returns></returns>
13
public abstract AbstractProductA CreateProductA();
14
15
/// <summary>
16
/// 创建产品B
17
/// </summary>
18
/// <returns></returns>
19
public abstract AbstractProductB CreateProductB();
20
}
21
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 抽象产品A
5
/// </summary>
6
public abstract class AbstractProductA
7
{
8
}
9
}

2

3

4

5

6

7

8

9

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 抽象产品B
5
/// </summary>
6
public abstract class AbstractProductB
7
{
8
/// <summary>
9
/// 产品交互
10
/// </summary>
11
/// <param name="abstractProductA"></param>
12
public abstract void Interact(AbstractProductA abstractProductA);
13
}
14
}

2

3

4

5

6

7

8

9

10

11

12

13

14

接着,实现一套具体的工厂类,具体的产品A类,具体的产品B类,分别继承上述三个抽象类。
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体工厂1
5
/// </summary>
6
public class ConcreteFactory1:AbstractFactory
7
{
8
/// <summary>
9
/// 覆写抽象方法,创建具体产品A
10
/// </summary>
11
/// <returns></returns>
12
public override AbstractProductA CreateProductA()
13
{
14
return new ProductA1();
15
}
16
17
/// <summary>
18
/// 覆写抽象方法,创建具体产品B
19
/// </summary>
20
/// <returns></returns>
21
public override AbstractProductB CreateProductB()
22
{
23
return new ProductB1();
24
}
25
}
26
}

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

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体产品A1
5
/// </summary>
6
public class ProductA1:AbstractProductA
7
{
8
}
9
}

2

3

4

5

6

7

8

9

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体产品B1
5
/// </summary>
6
public class ProductB1:AbstractProductB
7
{
8
/// <summary>
9
/// 产品交互
10
/// </summary>
11
/// <param name="abstractProductA"></param>
12
public override void Interact(AbstractProductA abstractProductA)
13
{
14
Console.WriteLine(this.GetType().Name + " interact with :" + abstractProductA.GetType().Name);
15
}
16
}
17
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

最后,客户程序可以通过创建具体的工厂来自动生产相应的产品。但是,这样的话客户端就对具体的工厂产生了依赖,当新增加一套产品时,需要修改客户端的产生具体工厂的代码。
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 客户端类
5
/// </summary>
6
class Client
7
{
8
private AbstractProductA abstractProductA;
9
private AbstractProductB bbstractProductB;
10
11
/// <summary>
12
/// 构造函数
13
/// </summary>
14
/// <param name="factory"></param>
15
public Client(AbstractFactory factory)
16
{
17
this.abstractProductA = factory.CreateProductA();
18
this.bbstractProductB = factory.CreateProductB();
19
}
20
21
/// <summary>
22
/// 运行
23
/// </summary>
24
private void Run()
25
{
26
this.bbstractProductB.Interact(this.abstractProductA);
27
}
28
29
/// <summary>
30
/// 主程序
31
/// </summary>
32
public static void Main()
33
{
34
// 方式1
35
// 直接依赖具体工厂1
36
AbstractFactory factory = new ConcreteFactory1();
37
38
new Client(factory).Run();
39
Console.Read();
40
}
41
}
42
}

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

现在新增一套产品(具体工厂2,具体产品A2,具体产品B2)
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体工厂2
5
/// </summary>
6
public class ConcreteFactory2:AbstractFactory
7
{
8
public override AbstractProductA CreateProductA()
9
{
10
return new ProductA2();
11
}
12
13
public override AbstractProductB CreateProductB()
14
{
15
return new ProductB2();
16
}
17
}
18
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体产品A2
5
/// </summary>
6
class ProductA2:AbstractProductA
7
{
8
}
9
}
10

2

3

4

5

6

7

8

9

10

1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 具体产品B2
5
/// </summary>
6
class ProductB2:AbstractProductB
7
{
8
public override void Interact(AbstractProductA abstractProductA)
9
{
10
Console.WriteLine(this.GetType().Name + " is interact with :" + abstractProductA.GetType().Name);
11
}
12
}
13
}

2

3

4

5

6

7

8

9

10

11

12

13

这时客户端的程序必须修改为
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 客户端类
5
/// </summary>
6
class Client
7
{
8
private AbstractProductA abstractProductA;
9
private AbstractProductB bbstractProductB;
10
11
/// <summary>
12
/// 构造函数
13
/// </summary>
14
/// <param name="factory"></param>
15
public Client(AbstractFactory factory)
16
{
17
this.abstractProductA = factory.CreateProductA();
18
this.bbstractProductB = factory.CreateProductB();
19
}
20
21
/// <summary>
22
/// 运行
23
/// </summary>
24
private void Run()
25
{
26
this.bbstractProductB.Interact(this.abstractProductA);
27
}
28
29
/// <summary>
30
/// 主程序
31
/// </summary>
32
public static void Main()
33
{
34
// 方式1
35
//// 直接依赖具体工厂1
36
//AbstractFactory factory = new ConcreteFactory1();
37
38
//new Client(factory).Run();
39
//Console.Read();
40
41
// 当新增产品(更换系列产品)时,必须将代码修改为如下,又对具体的工厂2产生了直接依赖
42
AbstractFactory factory = new ConcreteFactory2();
43
44
new Client(factory).Run();
45
Console.Read();
46
}
47
}
48
}

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

下面将通过反射机制将这种对变化点的依赖转移到配置文件,这样,可以对变化做出最快反应。
1
namespace DesignPatterns.AbstractFactory.UMLCode
2
{
3
/// <summary>
4
/// 客户端类
5
/// </summary>
6
class Client
7
{
8
private AbstractProductA abstractProductA;
9
private AbstractProductB bbstractProductB;
10
11
/// <summary>
12
/// 构造函数
13
/// </summary>
14
/// <param name="factory"></param>
15
public Client(AbstractFactory factory)
16
{
17
this.abstractProductA = factory.CreateProductA();
18
this.bbstractProductB = factory.CreateProductB();
19
}
20
21
/// <summary>
22
/// 运行
23
/// </summary>
24
private void Run()
25
{
26
this.bbstractProductB.Interact(this.abstractProductA);
27
}
28
29
/// <summary>
30
/// 主程序
31
/// </summary>
32
public static void Main()
33
{
34
// 方式1
35
//// 直接依赖具体工厂1
36
//AbstractFactory factory = new ConcreteFactory1();
37
38
//new Client(factory).Run();
39
//Console.Read();
40
41
//// 当新增产品(更换系列产品)时,必须将代码修改为如下,又对具体的工厂2产生了直接依赖
42
//AbstractFactory factory = new ConcreteFactory2();
43
44
//new Client(factory).Run();
45
//Console.Read();
46
47
// 方式2:通过反射机制,将具体工厂的创建工作(变化点),推迟到配置文件,
48
// 即将变换点封装在配置文件,这样对于客户系列对象的变化,将可以做出最快的反应。
49
// 当需要更换系列产品时,直接修改配置文件中相应的factoryClassName和factoryAssemblyName的值。
50
51
// 反射实例话方式: Assembly.Load("AssemblyName").CreateInstance("ClassName")
52
string factoryClassName = "DesignPatterns.AbstractFactory.UMLCode.ConcreteFactory1";
53
string factoryAssemblyName = "DesignPatterns";
54
Assembly assembly = Assembly.Load(factoryAssemblyName);
55
AbstractFactory factory = (AbstractFactory)assembly.CreateInstance(factoryClassName);
56
57
new Client(factory).Run();
58
Console.Read();
59
}
60
}
61
}

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

代码:完整例子
实现数据库访问层的切换,以ORACLE和SQLSERVER数据库为例.
2.在项目AbstractDAL下,新建抽象工厂类和抽象订单类:
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
using System.Reflection;
6
using System.Configuration;
7
8
namespace AbstractDAL
9
{
10
/// <summary>
11
/// 抽象工厂
12
/// </summary>
13
public abstract class Factory
14
{
15
public abstract Order CreateOrder();
16
}
17
}
18

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace AbstractDAL
6
{
7
/// <summary>
8
/// 抽象的订单
9
/// </summary>
10
public abstract class Order
11
{
12
public abstract string Name
13
{
14
get;
15
}
16
}
17
}
18

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

2.在项目SqlDAL下,新建针对Sql的具体工厂类和和具体订单类:
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace SqlDAL
6
{
7
/// <summary>
8
/// Sql 数据库工厂
9
/// </summary>
10
public class Factory:AbstractDAL.Factory
11
{
12
public override AbstractDAL.Order CreateOrder()
13
{
14
return new Order();
15
}
16
17
}
18
}
19

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace SqlDAL
6
{
7
/// <summary>
8
/// Sql Order
9
/// </summary>
10
public class Order:AbstractDAL.Order
11
{
12
public override string Name
13
{
14
get
15
{
16
// 做一些数据库增删改操作
17
return "SqlOrderName";
18
}
19
}
20
}
21
}
22

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

3.在项目OracleDAL下,新建针对Oracle的具体工厂类和和具体订单类:
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace OracleDAL
6
{
7
/// <summary>
8
/// Oracle 数据库工厂
9
/// </summary>
10
public class Factory:AbstractDAL.Factory
11
{
12
public override AbstractDAL.Order CreateOrder()
13
{
14
return new Order();
15
}
16
17
}
18
}
19

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace OracleDAL
6
{
7
/// <summary>
8
/// Oracle Order
9
/// </summary>
10
public class Order:AbstractDAL.Order
11
{
12
public override string Name
13
{
14
get
15
{
16
// 做一些数据库增删改操作
17
return "OracleOrderName";
18
}
19
}
20
}
21
}
22

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

4.Web.config相应配置
1
2
<appSettings>
3
<add key="factoryClassName" value="SqlDAL.Factory"/>
4
<add key="factoryAssemblyName" value="SqlDAL"/>
5
</appSettings>
6

2

3

4

5

6

5.在WebSite下新建类:DALFactory.cs,封装从Web.config中读取具体的数据库工厂配置。
1
using System;
2
using System.Data;
3
using System.Configuration;
4
using System.Web;
5
using System.Web.Security;
6
using System.Web.UI;
7
using System.Web.UI.WebControls;
8
using System.Web.UI.WebControls.WebParts;
9
using System.Web.UI.HtmlControls;
10
11
using AbstractDAL;
12
using System.Reflection;
13
14
namespace WebSite
15
{
16
/// <summary>
17
/// 数据访问层工厂,单件模式
18
/// </summary>
19
public class DALFactory
20
{
21
private static Factory instance;
22
public static Factory Instance
23
{
24
get
25
{
26
if (instance == null)
27
{
28
string factoryAssemblyName = ConfigurationManager.AppSettings["factoryAssemblyName"];
29
string factoryClassName = ConfigurationManager.AppSettings["factoryClassName"];
30
Assembly assembly = Assembly.Load(factoryAssemblyName);
31
instance = (Factory)assembly.CreateInstance(factoryClassName);
32
}
33
return instance;
34
}
35
}
36
37
}
38
}
39

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

6.客户端Aspx.cs页面
1
using System;
2
using System.Data;
3
using System.Configuration;
4
using System.Collections;
5
using System.Web;
6
using System.Web.Security;
7
using System.Web.UI;
8
using System.Web.UI.WebControls;
9
using System.Web.UI.WebControls.WebParts;
10
using System.Web.UI.HtmlControls;
11
12
using AbstractDAL;
13
using System.Reflection;
14
15
namespace WebSite
16
{
17
public partial class AbstractFactory : System.Web.UI.Page
18
{
19
/// <summary>
20
/// 加载
21
/// </summary>
22
/// <param name="sender"></param>
23
/// <param name="e"></param>
24
protected void Page_Load(object sender, EventArgs e)
25
{
26
// 获取数据库工厂,并通过其产生订单类
27
Factory factory = DALFactory.Instance;
28
Order order = factory.CreateOrder();
29
30
// 调用Order类的提供的接口
31
this.Response.Write(order.Name);
32
}
33
}
34
}
35

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

总结
抽象工厂解决的是系列易变对象的创建问题。
源代码:[下载]
参考
1. 【dofactory】ttp://www.dofactory.com/Patterns/Patterns.aspx#list
2. 【Terrylee】http://www.cnblogs.com/Terrylee/archive/2006/07/17/334911.html
3. 【卢振宇老师】http://zhenyulu.cnblogs.com/category/6930.html