最简单的计算器控制台程序很简单,通过打印文本跟用户交互,达到计算出结果的目的,代码如下:
int main()
{
double A, D, C;
string B;
cout<< "请输入数字A: ";
cin >> A;
cout << "请选择运算符号(+、-、*、/): ";
cin >> B;
cout << "请输入数字B: ";
cin >> C;
if (B == "+")
D = A + C;
if (B == "-")
D = A - C;
if (B == "*")
D = A * C;
if (B == "/")
D = A / C;
cout << "结果是: " << D << endl;
return 0;
}
以上的代码非常的简单,所以需要改进的地方也有很多。
int main()
{
double numberA, numberB;
double result;
string strOperate;
map operatorParam = {
{"+", 1},
{"-", 2},
{"*", 3},
{"/", 4},
};
try
{
cout << "请输入数字A: ";
cin >> numberA;
while (cin.fail())
{
cin.clear();
cin.ignore();
cout << "请输入数字A:";
cin.ignore();
cin >> numberA;
}
cout << "请选择运算符号(+、-、*、/): ";
cin >> strOperate;
cout << "请输入数字B: ";
cin >> numberB;
while (cin.fail())
{
cin.clear();
cin.ignore();
cout << "请输入数字B:";
cin.ignore();
cin >> numberA;
}
int caseValue = operatorParam[strOperate];
switch (caseValue)
{
case 1:
result = numberA + numberB;
break;
case 2:
result = numberA - numberB;
break;
case 3:
result = numberA * numberB;
break;
case 4:
if (numberB != 0.0f)
result = numberA / numberB;
else
throw "除数不能为 0 ";
break;
default:
throw "请选择运算符号(+、-、*、/) ";
break;
}
cout << "结果是: " << result << endl;
}
catch (const char *e)
{
cout << e;
}
return 0;
}
现在这份代码虽然比前面更加复杂一点,但是涵盖的东西也不少,麻雀虽小,五脏俱全。可是缺点也很明显,这个程序要求用户输入两个数和运算符号进行运算,得到结果,这本身没有错,但是这样的思维使得我们只能够满足当前的需求,程序不容易维护、不容易拓展、更不容易服用,从而达不到高质量的代码要求。
C++作为一种面向对象的语言,可以面向对象编程。以印刷术为例,如果一句一句的雕刻印刷,那么,一句话就要雕刻一版,如果发生两句话中只有一个字不一样,那么就要重新再雕刻一个。但是如果使用活字印刷,则只需要再雕刻一个字即可。
所以我们需要考虑的就是通过封装、继承、多态把程序的耦合度降低,使得程序更加的灵活、容易修改、并且易于复用。
如果在写一个Windows的计算器,这个代码就不能复用了,但是可以重新写一个,使用ctrl+C 和 ctrl+v,这其实是一个不好的编码习惯(宏观),因为当重复代码多到一定的程度,维护就是一场灾难,也就是现在说的屎山代码。如果将业务逻辑和界面逻辑分开,让它们之间的耦合度降低,这样就容易维护和扩展了。下面尝试一下:
首先封装运算类
#Operation.h
#pragma once
#include
#include
using namespace std;
class Operation {
public:
static map operatorParam;
public:
static double GetResult(double numberA, double numberB, string strOperator);
};
#Operation.cpp
#include
#include 客户端代码:
#include "Operation.h"
int main()
{
double numberA, numberB;
double result;
string strOperate;
try
{
cout << "请输入数字A: ";
cin >> numberA;
while (cin.fail())
{
cin.clear();
throw "输入的不是数字,请重新开始";
}
cout << "请选择运算符号(+、-、*、/): ";
cin >> strOperate;
cout << "请输入数字B: ";
cin >> numberB;
while (cin.fail())
{
cin.clear();
throw "输入的不是数字,请重新开始";
}
result = Operation::GetResult(numberA, numberB, strOperate);
cout << "结果是: " << result << endl;
}
catch (const char* e)
{
cout << e;
}
return 0;
}
这样,如果我要开发需要运算的类,那我就可以复用这个Operation的运算类了。但是这样也仅仅使用了面向对象三大特性中的一个封装,其他的两个对于这个小小的计算器来讲,如何用到继承,至于多态,一直也不了解它到底有什么好处,只是运行时的自动匹配吗?
上文中的Operation类,如果需要加一个开根(sqrt)运算,是不是只需要更改Operation类,在switch中加一个分支就行了。浅层次来讲,这样没错,但是,在编译的时候却要整个Operation进行重新编译,万一把加法运算不小心改成了减法,这就不太好了。
举个例子,如果公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员(时薪)的算法,按照刚刚程序的写法,公司就必须要把包含原三种算法的运算类给你,让你修改,你如果心中小算盘一打,“TMD的,公司给我的工资这么低,我真是郁闷,这下机会来了”,于是你就
if(员工是 小王)
{
salary = salary * 1.1;
}
那这样就意味着我月薪每月都会增加10%,很快就会走上人生巅峰,迎娶白富美了。本来只是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。
所以,我们重写了Operation运算类,如下:
Operation.h
class Operation {
public:
static map operatorParam;
void setNumberA(double val);
void setNumberB(double val);
double getNumberA();
double getNumberB();
virtual double GetResult();
private:
double numberA, numberB;
};
Operation.cpp
map Operation::operatorParam = {
{"+", 1},
{"-", 2},
{"*", 3},
{"/", 4},
};
void Operation::setNumberA(double val)
{
numberA = val;
}
void Operation::setNumberB(double val)
{
numberB = val;
}
double Operation::getNumberA()
{
return numberA;
}
double Operation::getNumberB()
{
return numberB;
}
double Operation::GetResult()
{
double result = 0;
return result;
return 0.0;
}
#OperationAdd.h
#include "Operation.h"
class OperationAdd :public Operation{
public:
double GetResult();
};
#OperationAdd.cpp
#include "OperationAdd.h"
double OperationAdd::GetResult()
{
double result = 0;
result = getNumberA() + getNumberB();
return result;
}
搭配简单的工厂模式
//工厂类
//OperationFactory.h
#include
#include "Operation.h"
class OperationFactory
{
public:
static Operation *createOperate(string operate);
};
//OperationFactory.cpp
#include "OperationFactory.h"
#include "OperationAdd.h"
Operation* OperationFactory::createOperate(string operate)
{
Operation *oper;
switch (operate[0])
{
case '+':
oper = new OperationAdd();
break;
default:
oper = new Operation();
break;
}
return oper;
}
//调用 main 部分
Operation *oper;
oper = OperationFactory::createOperate(strOperate);
oper->setNumberA(numberA);
oper->setNumberB(numberB);
result = oper->GetResult();
这样,如果需要改加法运算,则只需要修改OperationAdd类即可,如果需要添加其他的运算,比如平方根等等,只需要增加对应的子类和再switch中增加分支即可,如果需要修改界面,则只需要改界面。
编程是一门技术,更加是一门艺术