小菜和大鸟的编程故事之六:简单工厂模式之体验

鸡啄米  •  扫码分享
我是创始人李岩:很抱歉!给自己产品做个广告,点击进来看看。  

& & & &次日,小菜再来找大鸟,问道:“你昨天说计算器这样的小程序还可以用到面向对象三大特性?继承和多态怎么可能用得上,我实在不可理解。”

& & & &大鸟:“小菜很有钻研精神吗?好,今天我让你功力加深一级。你先要考虑一下,你昨天写的这个代码,能否做到很灵活的可修改和扩展呢?”

& & & &小菜:“我已经把业务和界面分离了呀,这不是很灵活了吗?”

& & & &大鸟:“那我问你,现在如果我希望增加一个开根(sqrt)运算,你如何改?”

& & & &小菜:“那只需要改operation类就行了,在switch中加一个分支就行了。”

& & & &大鸟:“问题是你要加一个平方根运算,却需要把加减乘除的运算都得来参与编译,如果你一不小心,把加法运算改成了减法,这不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员的(时薪)算法,但按照你昨天的程序写法,公司就必须要把包含有的原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘tmd,公司给我的工资这么低,我真是郁闷,这会有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句:

c#代码
  1. if&(员工是小菜)&&
  2. {&&
  3. &&&&salary&=&salary&*&1.1;&&
  4. }&&

&

& & & &那就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。你明白了吗?”

& & & &小菜:“哦,你的意思是,我应该把加减乘除等运算分离,修改其中一个不影响另外的几个,增加运算算法也不影响其它代码,是这样吗?”

& & & &大鸟:“自己想去吧,如何用继承和多态,你应该有感觉了。”

& & & &小菜:“ok,我马上去写。”

c#代码
  1. & &///&<summary>&&
  2. &&&///&运算类&&
  3. &&&///&</summary>&&
  4. &&&class&operation&&
  5. &&&{&&
  6. &&&&&&&private&double&_numbera&=&0;&&
  7. &&&&&&&private&double&_numberb&=&0;&&
  8. &&&&&&&&&
  9. &&&&&&&///&<summary>&&
  10. &&&&&&&///&数字a&&
  11. &&&&&&&///&</summary>&&
  12. &&&&&&&public&double&numbera&&
  13. &&&&&&&{&&
  14. &&&&&&&&&&&get{&return&_numbera;&}&&
  15. &&&&&&&&&&&set{&_numbera&=&value;}&&
  16. &&&&&&&}&&
  17. &&
  18. &&&&&&&///&<summary>&&
  19. &&&&&&&///&数字b&&
  20. &&&&&&&///&</summary>&&
  21. &&&&&&&public&double&numberb&&
  22. &&&&&&&{&&
  23. &&&&&&&&&&&get{&return&_numberb;&}&&
  24. &&&&&&&&&&&set{&_numberb&=&value;&}&&
  25. &&&&&&&}&&
  26. &&
  27. &&&&&&&///&<summary>&&
  28. &&&&&&&///&得到运算结果&&
  29. &&&&&&&///&</summary>&&
  30. &&&&&&&///&<returns></returns>&&
  31. &&&&&&&public&virtual&double&getresult()&&
  32. &&&&&&&{&&
  33. &&&&&&&&&&&double&result&=&0;&&&
  34. &&&&&&&&&&&return&result;&&
  35. &&&&&&&}&&
  36. &&
  37. &&&&&&&&
  38. &&&}&&

&

c#代码
  1. & & ///&<summary>&&
  2. &&&&///&加法类&&
  3. &&&&///&</summary>&&
  4. &&&&class&operationadd&:&operation&&
  5. &&&&{&&
  6. &&&&&&&&public&override&double&getresult()&&
  7. &&&&&&&&{&&
  8. &&&&&&&&&&&&double&result&=&0;&&&
  9. &&&&&&&&&&&&result&=&numbera&+&numberb;&&
  10. &&&&&&&&&&&&return&result;&&
  11. &&&&&&&&}&&
  12. &&&&}&&
  13. &&
  14. &&&&///&<summary>&&
  15. &&&&///&减法类&&
  16. &&&&///&</summary>&&
  17. &&&&class&operationsub&:&operation&&
  18. &&&&{&&
  19. &&&&&&&public&override&double&getresult()&&
  20. &&&&&&&&{&&
  21. &&&&&&&&&&&&double&result&=&0;&&
  22. &&&&&&&&&&&&result&=&numbera&-&numberb;&&
  23. &&&&&&&&&&&&return&result;&&
  24. &&&&&&&&}&&
  25. &&&&}&&
  26. &&
  27. &&&&///&<summary>&&
  28. &&&&///&乘法类&&
  29. &&&&///&</summary>&&
  30. &&&&class&operationmul&:&operation&&
  31. &&&&{&&
  32. &&&&&&&&public&override&double&getresult()&&
  33. &&&&&&&&{&&
  34. &&&&&&&&&&&&double&result&=&0;&&
  35. &&&&&&&&&&&&result&=&numbera&*&numberb;&&
  36. &&&&&&&&&&&&return&result;&&
  37. &&&&&&&&}&&
  38. &&&&}&&
  39. &&
  40. &&&&///&<summary>&&
  41. &&&&///&除法类&&
  42. &&&&///&</summary>&&
  43. &&&&class&operationdiv&:&operation&&
  44. &&&&{&&
  45. &&&&&&&&public&override&double&getresult()&&
  46. &&&&&&&&{&&
  47. &&&&&&&&&&&&double&result&=&0;&&
  48. &&&&&&&&&&&&if&(numberb==0)&&
  49. &&&&&&&&&&&&&&&&throw&new&exception("除数不能为0。");&&
  50. &&&&&&&&&&&&result&=&numbera&/&numberb;&&
  51. &&&&&&&&&&&&return&result;&&
  52. &&&&&&&&}&&
  53. &&&&}&&

&

& & & &小菜:“大鸟哥,我按照你说的方法写出来了一部分,首先是一个运算类,它有两个number属性,主要用于计算器的前后数,然后有一个虚方法getresult(),用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了getresult()方法,这样如果要修改任何一个算法,都不需要提供其它算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”

& & & &大鸟:“写得很不错吗,大大超出我的想象了,你现在的问题其实就是如何去实例化对象的问题,哈,今天心情不错,再教你一招‘简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象(比如增加开根运算),这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂,来,我们看看这个类如何写。”

c#代码
  1. ///&<summary>&&
  2. &///&运算类工厂&&
  3. &///&</summary>&&
  4. &class&operationfactory&&
  5. &{&&
  6. &&&&&public&static&operation&createoperate(string&operate)&&
  7. &&&&&{&&
  8. &&&&&&&&&operation&oper&=&null;&&
  9. &&&&&&&&&switch&(operate)&&
  10. &&&&&&&&&{&&
  11. &&&&&&&&&&&&&case&"+":&&
  12. &&&&&&&&&&&&&&&&&{&&
  13. &&&&&&&&&&&&&&&&&&&&&oper&=&new&operationadd();&&
  14. &&&&&&&&&&&&&&&&&&&&&break;&&
  15. &&&&&&&&&&&&&&&&&}&&
  16. &&&&&&&&&&&&&case&"-":&&
  17. &&&&&&&&&&&&&&&&&{&&
  18. &&&&&&&&&&&&&&&&&&&&&oper&=&new&operationsub();&&
  19. &&&&&&&&&&&&&&&&&&&&&break;&&
  20. &&&&&&&&&&&&&&&&&}&&
  21. &&&&&&&&&&&&&case&"*":&&
  22. &&&&&&&&&&&&&&&&&{&&
  23. &&&&&&&&&&&&&&&&&&&&&oper&=&new&operationmul();&&
  24. &&&&&&&&&&&&&&&&&&&&&break;&&
  25. &&&&&&&&&&&&&&&&&}&&
  26. &&&&&&&&&&&&&case&"/":&&
  27. &&&&&&&&&&&&&&&&&{&&
  28. &&&&&&&&&&&&&&&&&&&&&oper&=&new&operationdiv();&&
  29. &&&&&&&&&&&&&&&&&&&&&break;&&
  30. &&&&&&&&&&&&&&&&&}&&
  31. &&&&&&&&&&}&&
  32. &&
  33. &&&&&&&&&return&oper;&&
  34. &&&&&}&&
  35. &}&&

&

& & & &大鸟:“哈,看到吧,这样子,你只需要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果。”

c#代码
  1. operation&oper;&&
  2. oper&=&operationfactory.createoperate("+");&&
  3. oper.numbera&=&1;&&
  4. oper.numberb&=&2;&&
  5. double&result&=&oper.getresult();&&

&

& & & &大鸟: “哈,界面的实现就是这样的代码,不管你是控制台程序,windows程序,web程序,pda或手机程序,都可以用这段代码来实现计算器的功能,当有一天我们需要更改加法运算,我们只需要改哪里?”

& & & &小菜:“改operationadd 就可以了。”

& & & &大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”

& & & &小菜:“只要增加相应的运算子类就可以了呀。”

& & & &大鸟: “嗯?够了吗?”

& & & &小菜:“对了,还需要去修改运算类工厂,在switch中增加分支。”

& & & &大鸟: “哈,那才对,那如果要修改界面呢?”

& & & &小菜:“那就去改界面呀,关运算什么事呀。”

& & & &小菜:“ 回想那天我面试题写的代码,我终于明白我为什么写得不成功了,原来一个小小的计算器也可以写出这么精彩的代码,谢谢大鸟。”

& & & &(下为当时面试题时小菜所写代码,见《小菜和大鸟的编程故事之二》)

c#代码
  1. class&program&&
  2. {&&
  3. &&&&static&void&main(string[]&args)&&
  4. &&&&{&&
  5. &&&&&&&&console.write("请输入数字a:");&&
  6. &&&&&&&&string&a&=&console.readline();&&
  7. &&&&&&&&console.write("请选择运算符号(+、-、*、/):");&&
  8. &&&&&&&&string&b&=&console.readline();&&
  9. &&&&&&&&console.write("请输入数字b:");&&
  10. &&&&&&&&string&c&=&console.readline();&&
  11. &&&&&&&&string&d&=&"";&&
  12. &&
  13. &&&&&&&&if&(b&==&"+")&&
  14. &&&&&&&&&&&&d&=&convert.tostring(convert.todouble(a)&+&convert.todouble(c));&&
  15. &&&&&&&&if&(b&==&"-")&&
  16. &&&&&&&&&&&&d&=&convert.tostring(convert.todouble(a)&-&convert.todouble(c));&&
  17. &&&&&&&&if&(b&==&"*")&&
  18. &&&&&&&&&&&&d&=&convert.tostring(convert.todouble(a)&*&convert.todouble(c));&&
  19. &&&&&&&&if&(o&==&"/")&&
  20. &&&&&&&&&&&&d&=&convert.tostring(convert.todouble(a)&/&convert.todouble(c));&&
  21. &&
  22. &&&&&&&&console.writeline("结果是:"&+&d);&&
  23. &&&&}&&&&&&&
  24. }&&

&

&

& & & &大鸟: “吼吼,记住哦,编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简炼,更加容易维护,容易扩展和复用,只有这样才可以是真的提高。写出优雅的代码真的是一种很爽的事情。不过学无止境,其实这才是理解面向对象的开始呢。给你出个作业,做一个商场收银软件,营业员根据客户购买商品单价和数量,向客户收费。”

& & & &小菜:“就这个?没问题呀。”

&

除非特别注明,鸡啄米文章均为原创
转载请标明本文地址:http://www.jizhuomi.com/software/330.html
2013-5-28 16:19:52
作者:鸡啄米 分类:软件开发 浏览: 评论:0

随意打赏

提交建议
微信扫一扫,分享给好友吧。