问题引入
实现一个简单的计算器:输入数字和加减乘除操作符,输出结果
按照单纯的面向过程方法,可能有以下实现:
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Num1: ");
double num1 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Num2: ");
double num2 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Oper: ");
string strOper = Console.ReadLine();
double res = 0d;
switch(strOper)
{
///
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
以上代码暂时可以满足需求,但是如果现在需要增加一个case: 求模运算,那此时便需要修改switch
中的代码了,这不符合对修改封闭,对扩展开放的原则。此时可以考虑简单工厂模式:
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Num1: ");
double num1 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Num2: ");
double num2 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Oper: ");
string strOper = Console.ReadLine();
double res = 0d;
Operation oper = OperationFactory.createOperate(strOper);
oper.Num1 = num1;
oper.Num2 = num2;
Console.WriteLine(Convert.ToString(oper.GetResult()));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
abstract class Operation
{
private double _num1;
private double _num2;
public double Num1
{
get { return _num1; }
set { _num1 = value; }
}
public double Num2
{
get { return _num2; }
set { _num2 = value; }
}
public abstract double GetResult();
}
class OperationAdd : Operation
{
public override double GetResult()
{
return Num1 + Num2;
}
}
class OperationSub : Operation
{
public override double GetResult()
{
return Num1 - Num2;
}
}
class OperationMul : Operation
{
public override double GetResult()
{
return Num1 * Num2;
}
}
class OperationDiv : Operation
{
public override double GetResult()
{
if (Num2 == 0)
throw new Exception("Divisor can not be zero!!!");
return Num1 / Num2;
}
}
class OperationFactory
{
public static Operation createOperate(string type)
{
Operation oper = null;
switch (type)
{
case "+":
oper = new OperationAdd();
break;
case "-":
oper = new OperationSub();
break;
case "*":
oper = new OperationMul();
break;
case "/":
oper = new OperationDiv();
break;
}
return oper;
}
}
在上述模式中,我们首先抽象出一个Operation
抽象类,统一了GetResult
操作,然后不同的运算操作都继承自这个抽象类,并根据不同的运算自己实现GetResult
。接着使用一个OperationFactory
实现了switch
的功能,这样客户端只需要把运算的类型传进去即可。
当我们需要增加一种运算的时候,需要做两步:
- 定义一个新的类继承自
Operation
,实现GetResult
接口 - 在
OperationFactory
的switch
语句中增加一个对应的分支
显然,使用了简单工厂模式,我们还是避免不了修改switch
里面的代码,不过相比之前代码,有以下优点:
- 封装了
switch
,客户端不需要自己判断 - 将不同的算法分离,新增加一个算法只需要实现父类所要求的接口,原来的算法不会被暴露出来,减少被误改的风险
缺点也是有的:
- 若是要频繁增加算法,需要不断修改简单工厂里的判断分支
- 客户端使用时需要知道两个类:
Operation
和OperationFactory