对于计算器的实现,面向过程的方案就不说了,在面试过程中如果遇到,大概率是与设计模式结合,那么首先给出简单工厂的实现:
#include <iostream>
using namespace std;
class Operator
{
private:
double numa, numb;
public:
void setA(double x) { numa = x; }
void setB(double x) { numb = x; }
double getA() { return numa; }
double getB() { return numb; }
virtual double doOperator() { return 0; }
};
class AddOperator : public Operator
{
double doOperator()
{
return getA() + getB();
}
};
class SubOperator : public Operator
{
double doOperator()
{
return getA() - getB();
}
};
class MulOperator : public Operator
{
double doOperator()
{
return getA() * getB();
}
};
class DivOperator : public Operator
{
double doOperator()
{
return getA() / getB();
}
};
class OperatorFactory
{
public:
static Operator* operate(char c)
{
switch (c)
{
case '+':
return new AddOperator();
break;
case '-':
return new SubOperator();
break;
case '*':
return new MulOperator();
break;
case '/':
return new DivOperator();
break;
}
}
};
int main()
{
for (char c; cin >> c;)
{
Operator* of = OperatorFactory::operate(c);
double numa, numb;
cin >> numa >> numb;
of->setA(numa), of->setB(numb);
if (numb == 0 && c == '/') cout << "wrong operation\n";
else cout << of->doOperator() << endl;
}
return 0;
}
用main函数来代表客户端,输入需要进行的运算符以及两个运算数,针对特定运算符创建相应的运算类,最后得出结果(注意除法除数为0的边界条件),当然这里还有输入是否异常,简单起见没做判断
于是可以看出,若此时想要新增运算,则需要修改原先OperatorFactory这个类中的switch函数,也即破坏了设计模式的开闭原则,即对于扩展是开放的,但尽可能避免修改(因此简单工厂并不是23种设计模式之一)
于是引入了工厂方法:
#include <iostream>
using namespace std;
class Operator
{
private:
double numa, numb;
public:
void setA(double x) { numa = x; }
void setB(double x) { numb = x; }
double getA() { return numa; }
double getB() { return numb; }
virtual double doOperator() { return 0; }
};
class AddOperator : public Operator
{
double doOperator()
{
return getA() + getB();
}
};
class SubOperator : public Operator
{
double doOperator()
{
return getA() - getB();
}
};
class MulOperator : public Operator
{
double doOperator()
{
return getA() * getB();
}
};
class DivOperator : public Operator
{
double doOperator()
{
return getA() / getB();
}
};
class OperatorFactory
{
virtual Operator* createOperator() = 0;
};
class AddOperatorFactory: public OperatorFactory
{
public:
static Operator* createOperator() { return new AddOperator; }
};
class SubOperatorFactory : public OperatorFactory
{
public:
static Operator* createOperator() { return new SubOperator; }
};
class MulOperatorFactory : public OperatorFactory
{
public:
static Operator* createOperator() { return new MulOperator; }
};
class DivOperatorFactory : public OperatorFactory
{
public:
static Operator* createOperator() { return new DivOperator; }
};
int main()
{
for (char c; cin >> c;)
{
Operator* of = NULL;
switch (c)
{
case '+':
of = AddOperatorFactory::createOperator();
break;
case '-':
of = SubOperatorFactory::createOperator();
break;
case '*':
of = MulOperatorFactory::createOperator();
break;
case '/':
of = DivOperatorFactory::createOperator();
break;
}
double numa, numb;
cin >> numa >> numb;
if (c == '/' && numb == 0) cout << "wrong operation\n";
else
{
of->setA(numa), of->setB(numb);
cout << of->doOperator() << endl;
}
}
return 0;
}
这里如果要引入一个新的运算符,那么只需新增一个新的运算类和运算工厂,无需修改原有函数
当然这里也有个新的问题,即需要对客户端的switch部分代码进行修改
其实这里又有另一个技巧,“反射”机制,在C#中可通过using System.Reflection;
直接实现,C++则需要通过map来手动实现,其实质为一个字符串到对象实例的映射,可参考C++: 反射的简单实现
如此,对于输入的运算符,只要根据该映射关系即可找到对应的实例对象,并调用相关方法,若新增运算,也只需添加一对新映射即可。
最后的抽象工厂,借用抽象工厂模式(详解版)中的一张图
工厂方法是针对一个产品族的管理,不同产品族之间相互独立
而抽象工厂则是跨产品,可实现不同产品族同一个产品等级的管理
例如针对计算器,若此时有另一个星球的人说他们自己也有个计算器,有他们自己的加减乘除法则,这时候则需要通过抽象工厂来统一管理,也即,当产品族数量为1时,抽象工厂便退化为工厂方法。