工厂方法设计模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化,延迟到子类。
我们做一个“加减乘除计算器”来理解工厂方法设计模式。
- 设计结构
抽象产品:ICalculator
具体产品:Add/Sub/Mul/Div
通用反射工厂:CalculatorFactory
- 定义运算接口(抽象产品)
1 2 3 4 5
| public interface ICalculator { double Calculate(double a, double b); }
|
- 具体运算类(具体产品)
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; } }
|
- 反射工厂+特性(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 2 3
| var calculator = CalculatorFactory.Create("add"); double result = calculator.Calculate(10, 20); Console.WriteLine(result);
|
GetCustomAttribute 和 GetCustomAttributes 有什么区别?
GetCustomAttribute → 期望一个
GetCustomAttributes → 可能多个