开始学习《大话设计模式》(作者:程杰 清华大学出版社),用博客记录学习过程,以激励自己坚持学习,下面正式开始学习…
第一章 代码无错即是优?——简单工厂模式
1.1 面试受挫
该小节作者抛出了一个问题:
- 请用C++、JAVA、C#或者VB.NET任意一种面向对象编程语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
下面自己用C++先实现一下
/*
先理一下思路:
1、输入:两个数和运算符号
2、输出:计算结果
3、面向对象:实现一个类
MyCalculator
{
成员变量:
参数1
参数2
运算符
成员方法:
set参数1
set参数2
set运算符
get结果
};
*/
#include <iostream>
class MyCalculator //MyCalculator这个类写的好像有点奇怪吧
{
public:
MyCalculator()
{
this->num1 = 0;
this->num2 = 0;
}
void setNum1(float num) //这也没啥用吧,感觉不如调计算直接传参进去
{
this->num1 = num;
}
void setNum2(float num)
{
this->num2 = num;
}
bool getResult(char ope,float &result)
{
bool flag=false;
switch (ope)
{
case '+':
result = num1 + num2;
flag = true;
break;
case '-':
result = num1 - num2;
flag = true;
break;
case '*':
result = num1 * num2;
flag = true;
break;
case '/':
result = num1 / num2;
flag = true;
break;
default:
flag = false;
result = 0;
}
return flag;
}
private:
float num1;
float num2;
};
void main()
{
MyCalculator myCal;
float num;
char ope;
while (1)
{
std::cout << "输入第1个参数:";
std::cin >> num; //输入合法性,输入参数不是数字就不行了
myCal.setNum1(num);
std::cout << "输入第2个参数:";
std::cin >> num;
myCal.setNum2(num);
std::cout << "输入运算符:";
std::cin >> ope;
if (myCal.getResult(ope, num))
{
std::cout << "result:" << num << std::endl;
}
else
{
std::cout << "error" << std::endl;
}
}
system("pause");
}
程序写出来了,稍微测了一下勉强也能用…
1.2 代码规范性和健壮性
先来思考一下,一个代码先抛开功能不说,最基本的要满足一下两点吧,个人觉得这也是公司面试代码题最基本的考察点
- 规范性
包括命名规范、设计规范等等 - 健壮性
写出来的代码最起码不能随便崩吧
回过头自己审查一下代码,那么上面写的代码也就存在一些问题了
- 类设计的好像不太合理,
- 健壮性有问题,参数合法性没有做检查
先改一版呗
#include <iostream>
class MyCalculator
{
public:
bool getResult(const float numA,const float numB,const char ope,float &result)
{
bool flag=false;
switch (ope)
{
case '+':
result = numA + numB;
flag = true;
break;
case '-':
result = numA - numB;
flag = true;
break;
case '*':
result = numA * numB;
flag = true;
break;
case '/':
result = numA / numB;
flag = true;
break;
default:
flag = false;
result = 0;
}
return flag;
}
};
void main()
{
MyCalculator myCal;
float num1,num2,result;
char ope;
while (1)
{
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cin.sync();//清空缓存
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第1个参数:";
}
std::cin >> num1;
} while (std::cin.fail());
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cin.sync();//清空缓存
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第2个参数:";
}
std::cin >> num2;
} while (std::cin.fail());
std::cout << "输入运算符(+ - * /):";
std::cin >> ope;
std::cin.sync();//清空缓存
if (myCal.getResult(num1, num2, ope, result))
{
std::cout << "计算结果:" << result << std::endl;
}
else
{
std::cout << "计算结果:" << "error" << std::endl;
}
}
system("pause");
}
现在程序算是完成了一个简单的计算器了,但是这还没完…
1.3 面向对象编程
回过头看题目:
- 请用C++、JAVA、C#或者VB.NET任意一种面向对象编程语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
题目中强调了面向对象,这个也是该题目最重要的考察点——面向对象编程
面向对象的三大特性:
- 封装
- 继承
- 多态
目的,
为啥要用面向对象?提供程序的内聚,降低程序的耦合度,使程序更容易进行维护、扩展、复用
- 高内聚、低耦合,易维护、易扩展、易复用
1.3.1 封装
将业务逻辑和界面逻辑分开
通过类MyCalculator封装了业务逻辑
1.3.2 松耦合&紧耦合
考虑:如果现在的计算器,需求增加对开根计算的支持
目前的代码则需要在MyCalculator的getResult中进行修改,在其中修改就有可能会破坏了原来已实现的加减乘除的算法,后期不断的增加运算能力的支持,会导致getResult需要不断被修改。目前实现的代码仍然是紧耦合的…
问题发现了,那我们的目的也就明确了:
希望在增加新的运算能力时,是单独的增加代码,不去破坏已实现的代码结构。那么在面向对象中可以通过继承的方法来实现不同的运算能力。
MyCalculator类名称不合适,改成了Operation
#include <iostream>
class Operation //运算类基类,具体运算能力都通过继承该类进行实现
{
public:
void setNumberA(double const num)
{
this->numberA = num;
}
void setNumberB(double const num)
{
this->numberB = num;
}
virtual double GetResult() = 0;
protected:
double numberA;
double numberB;
};
class OperationAdd : public Operation //加子类
{
public:
virtual double GetResult()
{
return numberA + numberB;
}
};
class OperationSub : public Operation //减子类
{
public:
virtual double GetResult()
{
return numberA - numberB;
}
};
class OperationMul : public Operation //乘子类
{
virtual double GetResult()
{
return numberA * numberB;
}
};
class OperationDiv : public Operation //除子类
{
virtual double GetResult()
{
return numberA / numberB;
}
};
void main()
{
Operation *operation = NULL;
float num1,num2,result;
char ope;
while (1)
{
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第1个参数:";
}
std::cin >> num1;
std::cin.sync();//清空缓存
} while (std::cin.fail());
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第2个参数:";
}
std::cin >> num2;
std::cin.sync();//清空缓存
} while (std::cin.fail());
std::cout << "输入运算符(+ - * /):";
std::cin >> ope;
std::cin.sync();//清空缓存
switch (ope)
{
case '+':
operation = new OperationAdd();
break;
case '-':
operation = new OperationSub();
break;
case '*':
operation = new OperationMul();
break;
case '/':
operation = new OperationSub();
break;
default:
result = 0;
}
if (operation != NULL)
{
operation->setNumberA(num1);
operation->setNumberB(num2);
result = operation->GetResult();
std::cout << "计算结果:" << result << std::endl;
delete operation;
operation = NULL;
}
else
{
std::cout << "计算结果:" << "error" << std::endl;
}
}
system("pause");
}
通过继承的方式,实现了代码的松耦合,增加新的运算能力不会影响已有的运算子类;
同时通过父类指针指向子类,借助多态的特性,来获取不同的运行结果。
通过判断不同的运算符,来创建不同的实例化对象,这段代码和界面代码放一起好像不太合适吧!
1.4 简单工厂模式
用一个单独的类来实现创建实例的过程,这就是工厂。
#include <iostream>
class Operation //运算类基类,具体运算能力都通过继承该类进行实现
{
public:
void setNumberA(double const num)
{
this->numberA = num;
}
void setNumberB(double const num)
{
this->numberB = num;
}
virtual double GetResult() = 0;
protected:
double numberA;
double numberB;
};
class OperationAdd : public Operation
{
public:
virtual double GetResult()
{
return numberA + numberB;
}
};
class OperationSub : public Operation
{
public:
virtual double GetResult()
{
return numberA - numberB;
}
};
class OperationMul : public Operation
{
virtual double GetResult()
{
return numberA * numberB;
}
};
class OperationDiv : public Operation
{
virtual double GetResult()
{
return numberA / numberB;
}
};
//工厂类
class OperatinFactory
{
public:
static Operation * CreateOperation(char ope)
{
Operation *operation = NULL;
switch (ope)
{
case '+':
operation = new OperationAdd();
break;
case '-':
operation = new OperationSub();
break;
case '*':
operation = new OperationMul();
break;
case '/':
operation = new OperationSub();
break;
default:
operation = NULL;
}
return operation;
}
};
void main()
{
Operation *operation = NULL;
float num1,num2,result;
char ope;
while (1)
{
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第1个参数:";
}
std::cin >> num1;
std::cin.sync();//清空缓存
} while (std::cin.fail());
do
{
if (std::cin.fail())
{
std::cin.clear();//清除错误标志
std::cout << "输入错误,请重新输入:";
}
else
{
std::cout << "输入第2个参数:";
}
std::cin >> num2;
std::cin.sync();//清空缓存
} while (std::cin.fail());
std::cout << "输入运算符(+ - * /):";
std::cin >> ope;
std::cin.sync();//清空缓存
operation = OperatinFactory::CreateOperation(ope);
if (operation != NULL)
{
operation->setNumberA(num1);
operation->setNumberB(num2);
result = operation->GetResult();
std::cout << "计算结果:" << result << std::endl;
delete operation;
operation = NULL;
}
else
{
std::cout << "计算结果:" << "error" << std::endl;
}
}
system("pause");
}
1.5 总结
程序的UML图如下:
实现了一个结构相对合理的程序,具备了面向对象编程的特点,算是实现了高内聚、低耦合,易维护、易扩展、易复用的目的。
以简单工厂类为接口,工厂类根据不同输入参数创建具体的对象,实现界面和业务的解耦(界面需求改变和业务需求改变不互相影响)
对业务进行封装,利用继承使程序容易扩展,利用多态,用工厂类来实现对象实例的创建维护。