工厂方法设计模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化,延迟到子类。

我们做一个“加减乘除计算器”来理解工厂方法设计模式。

  1. 设计结构

抽象产品:ICalculator
具体产品:Add/Sub/Mul/Div
通用反射工厂:CalculatorFactory

  1. 定义运算接口(抽象产品)
1
2
3
4
5
public interface ICalculator
{
double Calculate(double a, double b);
}

  1. 具体运算类(具体产品)
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
[Operation("add")]
public class Add : ICalculator
{
public double Calculate(double a, double b)
{
return a + b;
}
}

[Operation("sub")]
public class Sub : ICalculator
{
public double Calculate(double a, double b)
{
return a - b;
}
}

[Operation("mul")]
public class Mul : ICalculator
{
public double Calculate(double a, double b)
{
return a * b;
}
}

[Operation("div")]
public class Div : ICalculator
{
public double Calculate(double a, double b)
{
if (b == 0)
throw new DivideByZeroException("除数不能为0");
return a / b;
}
}
  1. 反射工厂+特性(Attribute)

先定义一个特性(Attribute)

1
2
3
4
5
6
7
8
9
10
[AttributeUsage(AttributeTargets.Class)]
public class OperationAttribute : Attribute
{
public string Name { get; }

public OperationAttribute(string name)
{
Name = name.ToLower();
}
}

反射工厂

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
public class CalculatorFactory
{
private static readonly Dictionary<string, Type> _operations;

static CalculatorFactory()
{
_operations = new Dictionary<string, Type>();
var types = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => typeof(ICalculator).IsAssignableFrom(t)
&& !t.IsInterface
&& !t.IsAbstract);
foreach (var type in types)
{
var attribute = type.GetCustomAttribute<OperationAttribute>();
if (attribute != null)
{
_operations[attribute.Name] = type;
}
}
}

public static ICalculator Create(string operationName)
{
operationName = operationName.ToLower();
if (!_operations.TryGetValue(operationName, out var type))
throw new Exception("不支持的运算符");

return (ICalculator)Activator.CreateInstance(type);
}
}

程序启动时扫描所有带有 OperationAttribute 的运算类存入字典
运行时根据字符串动态创建对应运算对象

1
2
3
4
foreach (var type in types)
{
var attribute = type.GetCustomAttribute<OperationAttribute>();
}

对每个类型:
读取它身上有没有贴 OperationAttribute

1
2
3
4
if (attribute != null)
{
_operations[attribute.Name] = type;
}

key = 特性里定义的名字
value = 这个类的 Type

如果两个类使用同一个 Name:会被覆盖(后者覆盖前者)。

  1. 客户端调用
1
2
3
var calculator = CalculatorFactory.Create("add");
double result = calculator.Calculate(10, 20);
Console.WriteLine(result);

GetCustomAttribute 和 GetCustomAttributes 有什么区别?

GetCustomAttribute → 期望一个
GetCustomAttributes → 可能多个