设计模式之-简单工厂模式
前言
为了理解和学习简单工厂模式,我们先看一段简单计算器的代码
class Program { static void Main(string[] args) { Console.Write("请输入数字A:"); string A = Console.ReadLine(); Console.Write("请选择运算符号(+、-、*、/):"); string B = Console.ReadLine(); Console.Write("请输入数字B:"); string C = Console.ReadLine(); string D = ""; if (B == "+") D = Convert.ToString(Convert.ToDouble(A) + Convert.ToDouble(C)); if (B == "-") D = Convert.ToString(Convert.ToDouble(A) - Convert.ToDouble(C)); if (B == "*") D = Convert.ToString(Convert.ToDouble(A) * Convert.ToDouble(C)); if (B == "/") D = Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C)); Console.WriteLine("结果是:" + D); Console.ReadKey(); } }
以上代码存在几点明显问题
①A、B、C、D这样的命名非常不规范,真实项目中应该避免使用
②if判断分支,让计算机多做了三次无用功
③除数的时候如果用户输入了非正数及符号,没有相关处理。
根据上述三点问题进行优化后的代码如下:
class Program { static void Main(string[] args) { try { Console.Write("请输入数字A:"); string strNumberA = Console.ReadLine(); Console.Write("请选择运算符号(+、-、*、/):"); string strOperate = Console.ReadLine(); Console.Write("请输入数字B:"); string strNumberB = Console.ReadLine(); string strResult = ""; switch (strOperate) { case "+": strResult = Convert.ToString(Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB)); break; case "-": strResult = Convert.ToString(Convert.ToDouble(strNumberA) - Convert.ToDouble(strNumberB)); break; case "*": strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB)); break; case "/": if (strNumberB != "0") strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB)); else strResult = "除数不能为0"; break; } Console.WriteLine("结果是:" + strResult); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("您的输入有错:" + ex.Message); } } }
就上述代码而言,的确实现了简单计算器的功能,但是我们进一步思考这段代码,如果此时 需要新做一个计算器呢?估计有很多小伙伴灵光一现,复制粘贴大法。我们回想下古代活字印刷术的出现,对于印刷的巨大帮助。
⑴ 要改内容,只需要改动要改的文字,此为可维护;
⑵这些字并非用完这次就无用了,以后的印刷中可以重复使用,此乃可复用;
⑶若此版面内容要加字,只需要另刻字加入即可,这是可扩展。
⑷字的排列可能是竖排,可能是横排,此时只需要将活字移动就可做到满足排列需求,此是灵活性好。
而再活字印刷术出现之前,以上四点特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本后,此版已无任何可再利用价值。
面向对象的好处
★业务的封装(让业务逻辑与界面逻辑分开,降低耦合度)
根据上述思考,我们将运算单独封装一个运算类 Operation
/// <summary> /// 运算类 /// </summary> public class Operaion { /// <summary> /// 计算方法 /// </summary> /// <param name="numberA">运算数</param> /// <param name="numberB">运算数</param> /// <param name="operate">符号</param> /// <returns>返回值</returns> public static double GetResult(double numberA, double numberB, string operate) { double result = 0d; switch (operate) { case "+": result = numberA + numberB; break; case "-": result = numberA - numberB; break; case "*": result = numberA * numberB; break; case "/": result = numberA / numberB; break; } return result; } }
客户端调用的时候调用此方法即可:
class Program { static void Main(string[] args) { try { Console.Write("请输入数字A:"); string strNumberA = Console.ReadLine(); Console.Write("请选择运算符号(+、-、*、/):"); string strOperate = Console.ReadLine(); Console.Write("请输入数字B:"); string strNumberB = Console.ReadLine(); string strResult = ""; //调用运算类中计算方法 strResult = Convert.ToString(Operaion.GetResult(Convert.ToDouble(strNumberA), Convert.ToDouble(strNumberB), strOperate)); Console.WriteLine("结果是:" + strResult); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("您的输入有错:" + ex.Message); } } }
上述代码实现了业务逻辑与界面的分离,但是还没有真正运用到程序的精髓所在:封装、继承、多态,带着思考和疑问,我们做出进一步修改如下:
/// <summary> /// 运算类 /// </summary> public class Operaion { 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; } } /// <summary> /// 加法类,继承运算类 /// </summary> class OpertionAdd : Operaion { public override double GetResult() { double result = 0; result = NumberA + NumberB; return result; } } /// <summary> /// 减法类,继承运算类 /// </summary> class OperationSub : Operaion { public override double GetResult() { double result = 0; result = NumberA - NumberB; return result; } } /// <summary> /// 乘法类,继承运算类 /// </summary> class OperationMul : Operaion { public override double GetResult() { double result = 0; result = NumberA * NumberB; return result; } } /// <summary> ///除法类,继承运算类 /// </summary> class OperationDiv : Operaion { public override double GetResult() { double result = 0; if (NumberB == 0) throw new Exception("除数不能为0."); result = NumberA / NumberB; return result; } }
上述代码写完,此时就有一个疑问,如何让计算器知道我们希望用哪一个算法呢?
下面我们看看简单运算工厂类
public class OperationFactory { public static Operaion createOperate(string operate) { Operaion oper = null; switch (operate) { case "+": oper = new OpertionAdd(); break; case "-": oper = new OperationSub(); break; case "*": oper = new OperationMul(); break; case "/": oper = new OperationDiv(); break; } return oper; } }
有了运算工厂类,我们只需要输入运算符号,工厂就能实例化适合的对象,通过多态,返回父类的方法实现了计算器的结果,客户端代码如下:
class Program { static void Main(string[] args) { try { Console.Write("请输入数字A:"); string strNumberA = Console.ReadLine(); Console.Write("请选择运算符号(+、-、*、/):"); string strOperate = Console.ReadLine(); Console.Write("请输入数字B:"); string strNumberB = Console.ReadLine(); //调用工厂类进行计算 Operaion oper; oper = OperationFactory.createOperate(strOperate); oper.NumberA =double.Parse(strNumberA); oper.NumberB =double.Parse(strNumberB); //返回计算结果 var strResult = oper.GetResult(); Console.WriteLine("结果是:" + strResult); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("您的输入有错:" + ex.Message); } } }
这样,不管我们是控制台程序、Windows程序,Web程序或者手机程序,都可以使用这段代码来实现计算器的功能,如果有一天我们需要更改加法运算,我们改OperationAdd就可以了,如果需要增加其他复杂运算,比如平方根、立方根,我们只需要增加相应的运算子类和运算类工厂,再switch中增加分支即可。
UML类图
- 感谢你的阅读。如果你觉得这篇文章对你有帮助或者有启发,就请推荐一下吧~你的精神支持是博主强大的写作动力。欢迎转载!
- 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高(其实是个菜B),不足和错误之处在所难免,希望大家能够批评指出。
- 欢迎加入.NET 从入门到精通技术讨论群→523490820 期待你的加入
- 不舍得打乱,就永远学不会复原。被人嘲笑的梦想,才更有实现的价值。
- 我的博客:http://www.cnblogs.com/zhangxiaoyong/