用面向对象编程实现一个计算器程序,要求输入两个数和运算符号,得到结果。
“所有编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实是用计算机的方式去思考,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护,不容易扩展,更不容易复用。从而达不到高质量代码的要求。”
例子:【活体印刷】
三国时期,由于还未发明活字印刷,所有要改字的时候,就必须要整个刻板全部重新刻!实在是很吐血很蛋痛!而到了后来,活体印刷的出现,此景此情此处只需更改四字即可,其余工作都未白做,岂不妙哉!
“第一,要改,只需更改要改之字,此为可维护;第二,这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;第三,此诗若要加字,只需刻字加入即可,这是可扩展;第四,字的排列其实可能是竖排也可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
“可维护、可复用、可扩展、灵活性好这四点,不仅仅是面向对象编程的一个起始点更是我们对待每一个程序应用一个较好的编程思想!”
————————————————
基于以上的情景,要求你再写一个Windows的计算器,你现在的代码能不能复用呢?
“有人说直接把代码复制过去不就可以了吗?改动不多不算麻烦。这其实是非常不好的编码习惯,因为当你的代码中重复的代码多到一定程度,维护的时候可能就是一场灾难。越大的系统,这种方式带来的问题越严重,编程有一原则,就是用尽可能的办法去避免重复。回过头看看我们写的代码,有哪些是和控制台(界面逻辑)无关的,而哪些又与计算器(业务逻辑)有关呢?”
“准确地说,就是让业务逻辑与界面逻辑分开,让它们之间的耦合度下降。只有分离开,才可以达到容易维护或扩展。”
业务与界面分离
/**
*运算类
*@author sunwind
*/
class Operation {
GetResult(numberA, numberB, operate)
{
result := 0d
switch (operate)
{
case "+":
result := numberA + numberB
case "-":
result := numberA - numberB
case "*":
result := numberA * numberB
case "/":
{
if (numberB=0)
throw { what: "除零错误!", file: A_LineFile, line: A_LineNumber }
else
result := numberA / numberB
}
}
return result
}
}
/**
* 业务与界面分离
* @author sunwind
*
*/
class Test {
main() {
try {
InputBox, strNumberA , 输入, 请输入数字A:
InputBox, strOperator , 输入, 请选择运算符号(+、-、*、/):
InputBox, strNumberB , 输入, 请输入数字B:
strResult := Operation.GetResult(strNumberA,strNumberB, strOperator)
MsgBox % "结果是:" strResult
} catch e {
MsgBox % "您的输入有错:" e.what "`n" e.file "`n" e.line
}
}
}
Test.main()
如此的代码编程,不单是Windows程序,Web版程序需要运算也可以用它,PDA、手机等需要移动系统的软件需要运算也可以用它。但仅此而已,还谈不上完全面向对象,此处仅仅只用了面向对象三大特性中的其中一个(封装),还有两个没用(继承和多态)呢!
紧耦合 VS 松耦合
问:“基于以上的代码,是否已做到很灵活的可修改和扩展呢?”
条件:“现在如果我希望增加一个开根(sqrt)运算,你会如何改?”
菜鸟回答:“那只需要改Operation类就行了,在switch中加一个分支就行了。”
分析:“问题是你要加一个平方根运算,却需要让加减乘除的运算都得来参与编译,如果你一不小心把加法运算改成了减法,这岂不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员(时薪)的算法,但按照上述的程序写法,公司就必须要把包含原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘TMD!公司给我的工资这么低,这下有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句。
if (技术人员是我)
{
salary *= 1.1;
}
如此就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有运行良好的功能代码产生了变化,这个风险太大了!”
简单工厂模式
工厂模式用于处理 如何获取实例对象问题,建造者模式用于处理如何建造实例对象 问题。
工厂方法的实现并不能减少工作量,但是它能够在必须处理新情况时,避免使已经很复杂的代码更加复杂。
通常设计应该是从工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余地。
#SingleInstance, Force
/**
* 运算类
* @author sunwind
*
*/
class Operation {
_numberA
{
get{
return this._numberA
}
}
_numberB
{
get{
return this._numberB
}
}
}
/**
* 加减乘除类 -- 加法类,继承运算类
* @author sunwind
*
*/
class OperationAdd extends Operation {
result[]
{
get{
return base._numberA + base._numberB
}
}
}
/**
* 加减乘除类 -- 减法类,继承运算类
* @author sunwind
*
*/
class OperationSub extends Operation {
result[]
{
get{
return base._numberA - base._numberB
}
}
}
/**
* 加减乘除类 -- 乘法类,继承运算类
* @author sunwind
*
*/
class OperationMul extends Operation {
result[]
{
get{
return base._numberA * base._numberB
}
}
}
/**
* 加减乘除类 -- 除法类,继承运算类
* @author sunwind
*
*/
class OperationDiv extends Operation {
result[]
{
get{
return base._numberA / base._numberB
}
}
}
;增加一个新运算,需要增加一个Operation的子类 比如,增加“指数运算” 就幢一个 OperationExp 类 继承 Operation
;并需要在简单运算工厂类中增加相应运算符即可。
class OperationExp extends Operation{
result[]
{
get{
return base._numberA ** base._numberB
}
}
}
/**
* 简单运算工厂类
* @author sunwind
*
*/
class OperationFacotry {
createOperate(operate){
oper :=""
switch (operate)
{
case "+":
oper := new OperationAdd()
case "-":
oper := new OperationSub()
case "*":
oper := new OperationMul()
case "/":
oper := new OperationDiv()
case "^":
oper := new OperationExp()
}
return oper
}
}
/**
* 客户端代码
* @author sunwind
*
*/
class Test{
main() {
oper:= ""
oper := OperationFacotry.createOperate("^")
oper._numberA:=2
oper._numberB:=3
result := oper.result
MsgBox % "结果是:" result
}
}
Test.main()
编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更容易维护,容易扩展和复用,只有这样才可以真正得到提高。写出优雅的代码真的是一种很爽的事情。UML类图也不是一学就会的,需要有一个慢慢熟练的过程。所谓学无止境,其实这才是理解面向对象的开始呢。
——程杰《大话设计模式》
标签:oper,numberB,运算,numberA,AHK,面向对象,result,._,AutoHotkey From: https://blog.51cto.com/u_15408625/6228965