在研究完简单工厂模式,从它的优缺点中,我们不难看出:如果我们要新增加一个新的运算方法,我们需要做两步:
1、编写新的运算方法;
2、去修改OperationFactory类,增加case分支判断;
这样一个弊端:同时开放了扩展,而且也开放了修改,违背了开放-封闭原则。
从简单工厂模式UML图中我们发现“简单工厂类”是依赖于“运算类”。
为了解决这个问题,我们分别为每个运算方法类建立工厂,每个工厂继承于同一接口,这样“简单工厂类”对于“运算类”的依赖,就降低了。
这就是工厂方法模式。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
在客户端调用方面:工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在。也就是说工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行。如果你需要新添功能,本来需要改工厂类,现在需要修改客户端。
计算器的工厂方法模式UML图:
接下来,我们完成代码实现:
1、定义抽象工厂接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SimpleFactoryPattern
{
internal interface IFactory
{
Operation CreateOperation();
}
}
2、定义具运算体工厂类,并实现IFactory接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SimpleFactoryPattern
{
class SpecificFactory
{
}
public class AddFactory : IFactory
{
#region IFactory 成员
public Operation CreateOperation()
{
return new OperationAdd();
}
#endregion
}
public class SubFactory : IFactory
{
#region IFactory 成员
public Operation CreateOperation()
{
return new OperationSub();
}
#endregion
}
public class MulFactory : IFactory
{
#region IFactory 成员
public Operation CreateOperation()
{
return new OperationMul();
}
#endregion
}
public class DivFactory : IFactory
{
#region IFactory 成员
public Operation CreateOperation()
{
return new OperationDiv();
}
#endregion
}
}
3、客户端调用代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SimpleFactoryPattern
{
class Program
{
static void Main(string[] args)
{
//Operation oper;
//oper = OperationFactory.CreateOperate("+");
//oper.NumberA = 1;
//oper.NumberB = 2;
//double result = oper.GetResult();
//Console.WriteLine("输出结果:"+result);
//Console.ReadLine();
//对比上述简单工厂客户端调用代码
IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
oper.NumberA = 1;
oper.NumberB = 2;
double result = oper.GetResult();
Console.WriteLine("结果:"+result);
Console.ReadLine();
}
}
}
4、补充一下其他实现代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SimpleFactoryPattern
{
public class Operation
{
private double _numberA = 0;
private double _numberB = 0;
public double NumberA
{
get { return _numberA; }
set { _numberA = value; }
}
public double NumberB
{
get { return _numberB; }
set { _numberB = value; }
}
public virtual double GetResult()
{
double result = 0;
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SimpleFactoryPattern
{
class OperationBLL
{
}
/// <summary>
/// 加法
/// </summary>
internal class OperationAdd:Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA + NumberB;
return result;
}
}
/// <summary>
/// 减法
/// </summary>
internal class OperationSub:Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA - NumberB;
return result;
}
}
/// <summary>
/// 乘法
/// </summary>
internal class OperationMul:Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA * NumberB;
return result;
}
}
/// <summary>
/// 除法
/// </summary>
internal class OperationDiv:Operation
{
public override double GetResult()
{
double result = 0;
if (NumberB==0)
{
throw new Exception("除数不能为0.");
}
result = NumberA / NumberB;
return result;
}
}
工厂方法类是对简单工厂类加强。
这样整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全符合了“开放-封闭”原则。
但是随之而来有一个问题:由于每增加一个产品。就需要一个产品工厂的类,增加了额外的开发量。
但是这样的问题可以利用“反射”解决分支判断的问题。
补充:
优点:
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象。而且如何创建一个具体产品的细节完全封装在具体工厂内部,符合高内聚,低耦合。
- 在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,很好的利用了封装和委托。
缺点:
- 在添加新产品时,需要编写新的具体产品类(其实这不算一个缺点,因为这是不可避免的),要增加与之对应的具体工厂类。
应用情景:
- 类不知道自己要创建哪一个对象时
- 类用它的子类来指定创建哪个对象
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候