简单工厂模式描述的是,通过类的继承关系,父类(工厂类)与子类(产品类),调用父类中的方法,实际干活儿的是子类中的方法;封装需求的不确定性,做出通用的编程,下面以常用的计算器为例:
最容易想到的写法是:
1 Console.WriteLine("请输入操作数1:"); 2 double a = double.Parse(Console.ReadLine()); 3 Console.WriteLine("请输入操作符:"); 4 string opt = Console.ReadLine(); 5 Console.WriteLine("请输入操作数2:"); 6 double b = double.Parse(Console.ReadLine()); 7 8 double result = 0; 9 10 switch (opt) 11 { 12 case "+": 13 result = a + b; 14 break; 15 case "-": 16 result = a - b; 17 break; 18 case "*": 19 result = a * b; 20 break; 21 case "/": 22 if (b == 0) 23 { 24 throw new Exception("被除数不能为0"); 25 } 26 result = a / b; 27 break; 28 } 29 Console.WriteLine("计算结果是:"+result);
这么写,对于控制台来说基本够用了,但是它有很多的弊病:
1.计算结果是直接输出到控制台,如果要做一个WinForm版呢?(目前只有重新写一遍,不能够重用)
2.这里的case只考虑了基本的四则运算,业务变更后,如果有求平方、求立方、开方等运算呢?(那么只能去改写好的方法,一个项目中只有一处还好说,如果有多处要修改,那就麻烦了,可扩展性太差)
3.这段代码也没有体现面向对象的3大特性:封装、继承、多态。
基于以上的种种弊端,需要修改代码:
首先定义一个父类Operat,在类中不考虑未来是否有四则运算及怎样运算
Operat类
1 /// <summary> 2 /// 父类计算方法 3 /// </summary> 4 public class Operat 5 { 6 public double NumberA { get; set; } 7 public double NumberB { get; set; } 8 /// <summary> 9 /// 构造函数 10 /// </summary> 11 /// <param name="a"></param> 12 /// <param name="b"></param> 13 public Operat(double a,double b) 14 { 15 this.NumberA = a; 16 this.NumberB = b; 17 } 18 19 public virtual double Oper() 20 { 21 double result = 0; 22 return result; 23 } 24 }
只定义了2个操作数和一个计算方法(虚方法,因为这里不知道未来有几个运算)
再定义一个加法类(OperatAdd)来继承它,并实现父类中的计算方法:
OperatAdd类(加法)
1 class OperatAdd : Operat 2 { 3 //构造函数 4 public OperatAdd(double a,double b):base(a,b) 5 { 6 7 } 8 /// <summary> 9 /// 子类重写父类的Oper方法(实现) 10 /// </summary> 11 /// <returns></returns> 12 public override double Oper() 13 { 14 double result = 0; 15 result = NumberA + NumberB; 16 return result; 17 } 18 }
依次定义后面的3个类(减、乘、除)
OperatSub类(减法)
1 class OperatSub : Operat 2 { 3 public OperatSub(double a,double b):base(a,b) 4 { 5 6 } 7 public override double Oper() 8 { 9 double result = 0; 10 result= NumberA - NumberB; 11 return result; 12 } 13 }
OperatMult类(乘法)
1 class OperatMult:Operat 2 { 3 public OperatMult(double a,double b):base(a,b) 4 { 5 6 } 7 public override double Oper() 8 { 9 double result = 0; 10 result= NumberA * NumberB; 11 return result; 12 } 13 }
OperatVision类(除法)
1 class OperatVision:Operat 2 { 3 public OperatVision(double a,double b):base(a,b) 4 { 5 6 } 7 public override double Oper() 8 { 9 double result = 0; 10 if (NumberB==0) 11 { 12 throw new Exception("被除数不能为0"); 13 } 14 result = NumberA / NumberB; 15 return result; 16 } 17 }
这时候,应该考虑的问题是,在业务中,怎样调用这4个子类中的运算方法(简单工厂)
定义一个工厂类,由工厂类根据具体业务去调用具体的子类(产品类)
1 /// <summary> 2 /// 工厂类 3 /// </summary> 4 public class OperatFactory 5 { 6 public Operat JiSuan(double a, string opt, double b) 7 { 8 Operat opt1 = null; 9 //封装了异同业务需求的差异 10 switch (opt) 11 { 12 case "+": 13 opt1 = new OperatAdd(a, b); //产品1(加法) 14 break; 15 case "-": 16 opt1 = new OperatSub(a, b); //产品2(减法) 17 break; 18 case "*": 19 opt1 = new OperatMult(a, b); //产品3(乘法) 20 break; 21 case "/": 22 opt1 = new OperatVision(a, b); //产品4(除法) 23 break; 24 } 25 return opt1; //返回父类对象 26 } 27 }
给opt赋不同的运算,工厂类就会去调用相应的子类,执行计算方法,new出相应的产品类,因为子类中都只是 return result;没有考虑这个结果具体显示在那个地方(控制台还是winform中的label),就变得相当灵活了,并返回父类对象。
控制台去使用时,调用工厂类中JiSuan()方法返回父类对象,即可:
1 Console.WriteLine("请输入操作数1:"); 2 double a = double.Parse(Console.ReadLine()); 3 Console.WriteLine("请输入操作符:"); 4 string opt = Console.ReadLine(); 5 Console.WriteLine("请输入操作数2:"); 6 double b = double.Parse(Console.ReadLine()); 7 8 OperatFactory factory = new OperatFactory(); 9 Operat opt1 = factory.JiSuan(a, opt, b); 10 Console.WriteLine("计算结果是:{0}", opt1.Oper()); 11 Console.ReadKey();
而winform的代码也很类似:
1 lbResult.Text = ""; 2 3 lbResult.ForeColor = Color.Red; 4 lbResult.Font = new Font("宋体", 12); 5 double a = double.Parse(txtNumber1.Text.Trim()); 6 string opt = cmbOperat.SelectedItem.ToString(); 7 double b = double.Parse(txtNumber2.Text.Trim()); 8 9 OperatFactory factory = new OperatFactory(); 10 Operat oper = factory.JiSuan(a, opt, b); 11 lbResult.Text = oper.Oper().ToString();
可以看出上面2段代码的第二段几乎是一样的,代码就足够通用了。