简单工厂模式(2)
一、举例
用控制台程序做一个非常简单的计算机,实现加减乘除运算即可,那么,大家自然而然的会写出如下代码
1 static void Main(string[] args) 2 { 3 Console.WriteLine("请输入数字A:"); 4 string strNumA = Console.ReadLine(); 5 Console.WriteLine("请选择运算符号(+、-、*、、)"); 6 string strOperate = Console.ReadLine(); 7 Console.WriteLine("请输入数字C:"); 8 string strNumB = Console.ReadLine(); 9 string strResult = ""; 10 if (strOperate == "+") 11 { 12 strResult = Convert.ToString(Convert.ToDouble(strNumA) + Convert.ToDouble(strNumB)); 13 } 14 if (strOperate == "-") 15 { 16 strResult = Convert.ToString(Convert.ToDouble(strNumA) - Convert.ToDouble(strNumB)); 17 } 18 if (strOperate == "*") 19 { 20 strResult = Convert.ToString(Convert.ToDouble(strNumA) * Convert.ToDouble(strNumB)); 21 } 22 if (strOperate == "/") 23 { 24 strResult = Convert.ToString(Convert.ToDouble(strNumA) / Convert.ToDouble(strNumB)); 25 } 26 Console.WriteLine($"结果是:{strResult}"); 27 Console.ReadKey(); 28 }
二、演绎
1、第一步演绎
①由于在判断运算符时,用的是if语句,这意味着每个条件都需要做判断,相当于计算机做了三次无用功。
②没有输入校验,例如,除数不能为零的问题等等
就上述两个问题,做如下修改:(输入校验此处先只做除数为零的校验)
1 static void Main(string[] args) 2 { 3 Console.WriteLine("请输入数字A:"); 4 string strNumA = Console.ReadLine(); 5 Console.WriteLine("请选择运算符号(+、-、*、、)"); 6 string strOperate = Console.ReadLine(); 7 Console.WriteLine("请输入数字C:"); 8 string strNumB = Console.ReadLine(); 9 string strResult = ""; 10 switch (strOperate) 11 { 12 case "+": 13 strResult = Convert.ToString(Convert.ToDouble(strNumA) + Convert.ToDouble(strNumB)); 14 break; 15 case "-": 16 strResult = Convert.ToString(Convert.ToDouble(strNumA) - Convert.ToDouble(strNumB)); 17 break; 18 case "*": 19 strResult = Convert.ToString(Convert.ToDouble(strNumA) * Convert.ToDouble(strNumB)); 20 break; 21 case "/": 22 if (strNumB != "0") 23 { 24 strResult = Convert.ToString(Convert.ToDouble(strNumA) / Convert.ToDouble(strNumB)); 25 } 26 else 27 { 28 strResult = "除数不能为0"; 29 } 30 break; 31 } 32 Console.WriteLine($"结果是:{strResult}"); 33 Console.ReadKey(); 34 }
2、第二步演绎
①上述写的简单的计算器仅仅体现了面向过程的编程,没有体现面向对象的编程
②上述代码使用控制台应用实现的,假如有一天我需要用Winform、Web 等实现一模一样的需求,那么,上述代码则需要重新编写,好可怕。
就上述问题,做如下修改:
将业务逻辑与界面分离
Operation运算类
1 public class Operation 2 { 3 public static double GetResult(double numA, double numB, string strOperate) 4 { 5 double result = 0d; 6 switch (strOperate) 7 { 8 case "+": 9 result = numA + numB; 10 break; 11 case "-": 12 result = numA - numB; 13 break; 14 case "*": 15 result = numA * numB; 16 break; 17 case "/": 18 result = numA / numB; 19 break; 20 } 21 return result; 22 } 23 }
客户端
1 static void Main(string[] args) 2 { 3 try 4 { 5 Console.WriteLine("请输入数字A:"); 6 string strNumA = Console.ReadLine(); 7 Console.WriteLine("请选择运算符号(+、-、*、、)"); 8 string strOperate = Console.ReadLine(); 9 Console.WriteLine("请输入数字C:"); 10 string strNumB = Console.ReadLine(); 11 string strResult = ""; 12 strResult = Convert.ToString(Operation.GetResult(Convert.ToDouble(strNumA), Convert.ToDouble(strNumB), strOperate)); 13 Console.WriteLine($"结果是:{strResult}"); 14 Console.ReadKey(); 15 } 16 catch (Exception ex) 17 { 18 Console.WriteLine($"您的输入有错:{ex.Message}"); 19 } 20 }
这样的话,无论是控制台,还是Winform以及其他应用,只需要调用 Operation 这个类即可了。实现了业务逻辑与界面的分离。这里,也用到了面向对象编程的三大特性之一的封装。
3、第三步演绎
①如果我想增加一种计算方式,例如开根号(sqrt)运算怎么办,只能是修改Operation类,在switch中增加一种情况。
②针对①问题做修改的话,那么另一个问题就来了,我修改Oeration类的时候,之前写好的加减乘除功能全在其中,如果我不小心,或者有意搞破坏,将本来运行的好好的加减乘除功能改掉,怎么办?(此例中仅仅是计算器,如果换做是成熟的产品,或者涉及到资金运算等等的代码,运行的好好的,不可能会让程序员在其中胡作非为的)
针对上述问题,作如下修改:
我可以将加减乘除各种运算分别封装到不同的类里面,这样,就能解决②的问题,每增加一种运算,我就增加一个类,这样①问题也解决了。
首先,将不同的运算抽象出一个共同的类,即父类
1 public class Operation 2 { 3 private double _numberA = 0; 4 private double _numberB = 0; 5 public double NumberA 6 { 7 get 8 { 9 return _numberA; 10 } 11 12 set 13 { 14 _numberA = value; 15 } 16 } 17 public double NumberB 18 { 19 get 20 { 21 return _numberB; 22 } 23 24 set 25 { 26 _numberB = value; 27 } 28 } 29 public virtual double GetResult() 30 { 31 double result = 0; 32 return result; 33 } 34 }
其次,让不同的运算继承这个父类,然后根据不同的运算,重写父类中的GetResult方法
1 //加法类 2 public class OperationAdd:Operation 3 { 4 public override double GetResult() 5 { 6 double result = 0; 7 result = NumberA + NumberB; 8 return result; 9 } 10 } 11 //减法类 12 public class OperationSub : Operation 13 { 14 public override double GetResult() 15 { 16 double result = 0; 17 result = NumberA = NumberB; 18 return result; 19 } 20 } 21 //乘法类 22 public class OperationMul : Operation 23 { 24 public override double GetResult() 25 { 26 double result = 0; 27 result = NumberA * NumberB; 28 return result; 29 } 30 } 31 //除法类 32 public class OperationDiv : Operation 33 { 34 public override double GetResult() 35 { 36 double result = 0; 37 if (NumberB==0) 38 { 39 throw new Exception("除数不能为0"); 40 } 41 result = NumberA / NumberB; 42 return result; 43 } 44 }
这样,上述的两个问题就迎刃而解了。
4、第四步演绎
到了简单工厂最核心的地方了。
这么多的类,客户端该如何用这些类呢,换句话说,客户端如何实例化这些类呢?那么,就需要一个核心的东西——工厂,这个工厂负责给客户端提供他想要的类的实例。我想要实例化什么类,告诉工厂就可以了。
代码如下:
1 public class OperationFactory 2 { 3 public static Operation createOperate(string operate) 4 { 5 Operation oper = null; 6 switch (operate) 7 { 8 case "+": 9 oper = new OperationAdd(); 10 break; 11 case "-": 12 oper = new OperationSub(); 13 break; 14 case "*": 15 oper = new OperationMul(); 16 break; 17 case "/": 18 oper = new OperationDiv(); 19 break; 20 } 21 return oper; 22 } 23 }
注意一点,这个工厂中的方法,返回的是加减乘除这些类的父类。
好了,制造类实例的工厂有了,那么,客户端只需要告诉工厂,给我造一个什么类的实例,然后用这个实例就可以了。
客户端代码如下
1 static void Main(string[] args) 2 { 3 Operation oper; 4 //告诉工厂,给我一个加法类的实例 5 oper = OperationFactory.createOperate("+"); 6 oper.NumberA = 1; 7 oper.NumberB = 2; 8 double result = oper.GetResult(); 9 Console.WriteLine(result); 10 Console.ReadKey(); 11 }
经过上述一系列的演绎,也就形成了简单工厂模式。
三、总结
简单工厂模式,即 通过一个‘工厂类’,用于子类返回父类对象。
工厂模式结束,下一次,将讲解策略模式