简单工厂模式
手上有一个《大话设计模式》,之前已经看了一些了,但是没有认真做一做记录。这里是第一个最简单的设计模式:简单工厂模式
工厂当然就是生产东西的,在程序设计中(面向对象的程序设计)这个工厂则是负责生产对象的。
先看一个写一个控制台计算器的例子:要求实现一个控制台计算器,输入两个数和运算符号,然后得出结果。
版本1:
class Program { static void Main(string[] args) { try { Console.WriteLine("输入A:"); int numA = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("选择运算符(+ - * /):"); string strOperate = Console.ReadLine(); Console.WriteLine("输入B:"); int numB = Convert.ToInt32(Console.ReadLine()); string strResult = ""; switch (strOperate) { case "+": strResult = (numA + numB).ToString(); break; case "-": strResult = (numA - numB).ToString(); break; case "*": strResult = (numA * numB).ToString(); break; case "/": if (numB!=0) { strResult = (numA / numB).ToString(); } else { strResult = "除数不能为0!"; } break; default: strResult = "非法操作符"; break; } Console.WriteLine("运算结果是:"+strResult); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("输入有错:"+ex.Message); } } }
就运行来说完全没有问题,但是就这样真的没毛病吗?这是用计算机的思维在写程序,完全没有面向对象的思想,比如要把这个东西改成一个windows的计算器,或者web上,代码就不能用了。
封装:
将业务逻辑封装起来,和界面逻辑分开,把运算的部分封装成一个类。这样就让这个运算类在其它地方复用了。
版本2:
/// <summary> /// 运算类 /// </summary> public class Operation { /// <summary> /// 获取计算结果 /// </summary> /// <param name="numA">数字A</param> /// <param name="numB">数字B</param> /// <param name="strOperate">运算符</param> /// <returns></returns> public static double GetResult(double numA,double numB,string strOperate) { double result = 0d; switch (strOperate) { case "+": result = numA + numB; break; case "-": result = numA - numB; break; case "*": result = numA * numB; break; case "/": if (numB != 0) { result = numA / numB; } else { throw new Exception("除数不能为0"); } break; default: //strResult = "非法操作符"; throw new Exception("不支持的操作符"); } return result; } } class Program { static void Main(string[] args) { try { Console.WriteLine("输入A:"); double numA = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("选择运算符(+ - * /):"); string strOperate = Console.ReadLine(); Console.WriteLine("输入B:"); double numB = Convert.ToDouble(Console.ReadLine()); double result = Operation.GetResult(numA,numB,strOperate); Console.WriteLine("运算结果是:"+ result); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("输入有错:"+ex.Message); } } }
面向对象的三大思想之一封装就在这里体现了。还有两大思想呢?
再回头来看看这个计算器,逻辑和界面已经分离了,代码可以在其它地方复用了,还有什么问题呢?如果要增加一个运算呢,比如增加一个开根号运算sqrt需要怎么修改呢?
最容易想到的是就是增加switch的分支吧,再增加一个分支支持开根号就行了这又存在一个问题了:明明只要增加一个开根号运算,但是其它运算规则都来参加编译。书上举了一个特别有意思的例子:如果一个公司系统让做薪资管理系统的维护,原来是 技术人员(月薪),销售(底薪+提成),经理(年薪+股份)三种计算薪水规则,现在要增加兼职人员(时薪)的算法,然后做维护,按照之前的写法,把运算的类拿出来维护,做修改,维护人员除了完成兼职算法以外再悄悄的在技术人员(月薪)地方加一行代码
if(我) { salary = salary*1.1; }
工资就比原来多了10%(违法要坐牢),所以这样是有风险的!所以这时候应该把各种运算进行分离,在分离的过程中就运用到了继承和多态。
版本3:
/// <summary> /// 运算基类 /// </summary> public class Operation { public double numA { get; set; } public double numB { get; set; } public virtual double GetResult() { double result = 0; return result; } } /// <summary> /// 加法类 /// </summary> class OperateAdd : Operation { public override double GetResult() { double result = 0; result = numA + numB; return result; } } /// <summary> /// 减法类 /// </summary> class OperateSub : Operation { public override double GetResult() { double result = 0; result = numA - numB; return result; } } /// <summary> /// 乘法类 /// </summary> class OperateMul : Operation { public override double GetResult() { double result = 0; result = numA * numB; return result; } } /// <summary> /// 除法类 /// </summary> class OperateDiv : Operation { public override double GetResult() { double result = 0; result = numA / numB; return result; } }
现在是各种类已经有了,但是要怎么样才能得到不同的运算类呢?这里就需要一个工厂了,根据输入运算符得到对应的运算类。
/// <summary> /// 运算类工厂 /// </summary> public class OperationFactory { public static Operation CreateOperation(string operate) { Operation oper = null; switch (operate) { case "+": oper = new OperateAdd(); break; case "-": oper = new OperateSub(); break; case "*": oper = new OperateMul(); break; case "/": oper = new OperateDiv(); break; default: //strResult = "非法操作符"; throw new Exception("不支持的操作符"); } return oper; } }
class Program { static void Main(string[] args) { try { Console.WriteLine("输入A:"); double numA = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("选择运算符(+ - * /):"); string strOperate = Console.ReadLine(); Console.WriteLine("输入B:"); double numB = Convert.ToDouble(Console.ReadLine()); var oper = OperationFactory.CreateOperation(strOperate); oper.numA = numA; oper.numB = numB; double result = oper.GetResult(); Console.WriteLine("运算结果是:"+ result.ToString()); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("输入有错:"+ex.Message); } } }
这就是简单工厂模式,如果要改界面,那就换界面就行了,如果要增加运算法则,那就增加类就行了,不过还要修改工厂增加switch分支,由工厂来复制创建类,代码复用性和可维护性大大地提高了。
(排版混乱,以后慢慢改进)