最近在读《重构 改善既有代码的设计》这本书,其实这本书大部分都在讲如何写出可维护、可复用、可扩展的程序,还有很多地方将一些类似代码规范什么的。
看完之后,我觉得其中最值得学习的就是“以多态取代条件表达式”这个规则了。
一、案例
最近直播很流行,那么就拿直播来举个例子。
比如熊猫直播,直播间里的观众一般分为3中:
1.普通观众(可以观看和发弹幕)
2.房管(权限比普通观众高,可以封禁普通观众)
3.超管(权限比房管高,可以封禁房管甚至直播)
通过用户昵称前面的标记区分到底是哪一类观众,普通观众不显示任何内容,房管前面有一个房管标记,超管前面有一个超管标记:
二、条件表达式
现在,我们编写代码来实现区分这3种观众:
#include <iostream>
using namespace std;
int main()
{
int iUserType;//用户类型。0:普通;1:房管;2:超管
cin >> iUserType;
if (iUserType == 0)
{
cout << "您是普通观众" << endl;
cout << "不显示任何标记" << endl;
}
else if (iUserType == 1)
{
cout << "您是房管" << endl;
cout << "显示房管标记" << endl;
}
else if (iUserType == 2)
{
cout << "您是超管" << endl;
cout << "显示超管标记" << endl;
}
system("pause");
return 0;
}
为了便于理解,上面的程序中没有考虑对错误的处理。
也许你觉得这样做就完了,但是这段代码明显不是面向对象的,不利于代码复用,没有“将业务逻辑和界面逻辑分开”。
所以接下来便是对业务的封装——
三、封装
观众类的头文件:
class Audience
{
public:
Audience(int iUserType);
~Audience();
void showUserType();
private:
int m_iUserType;
};
观众类的实现:
#include "Audience.h"
#include <iostream>
using namespace std;
Audience::Audience(int iUserType)
{
m_iUserType = iUserType;
}
Audience::~Audience()
{
}
void Audience::showUserType()
{
if (m_iUserType == 0)
{
cout << "您是普通观众" << endl;
cout << "不显示任何标记" << endl;
}
else if (m_iUserType == 1)
{
cout << "您是房管" << endl;
cout << "显示房管标记" << endl;
}
else if (m_iUserType == 2)
{
cout << "您是超管" << endl;
cout << "显示超管标记" << endl;
}
}
main函数:
int main()
{
int iUserType;//用户类型。0:普通;1:房管;2:超管
cin >> iUserType;
Audience audience(iUserType);
audience.showUserType();
system("pause");
return 0;
}
至此,业务和界面逻辑得以分离,代码的复用性也提高了。
那么,这样的程序是否已经足够了?
我们想想,现在观众分为3种类型,如果今后要增加或修改观众类型呢?比如添加一个系统管理员类型,他拥有封禁任何其他观众的权限。那么我们就需要增加一个条件判断,看起来没什么,实际上当程序很庞大时,这种修改很容易对原来良好的程序带来错误。而且增加一个条件判断,就要将之前的代码都重新编译一遍。
所以现在的程序时“紧耦合”的,可维护性不好。
一个更好的做法是将不同类型的用户分离。
四、类的分离
使用3个类,每个类对应一个类型的观众,在类内部进行一些其它操作。
1.编写3个类,每个类都有各自的shouUserType方法。
class CommonAudience; //普通观众
class HouseManager; //房管
class SuperManager; //超管
2.普通观众类的实现,另外2各类写法类似
#include "CommonAudience.h"
CommonAudience::CommonAudience()
{
}
void CommonAudience::showUserType()
{
cout << "您是普通观众" << endl;
cout << "不显示任何标记" << endl;
}
OK。到了这一步已经完成用户对象的分离。
下一步便是使用继承和多态,来获得究竟是哪一类观众。
在C++中,多态的作用是根据对象的不同类型而采取不同的行为。
回顾一下多态的用法,声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,根据指向的子类的不同而实现不同的方法。
五、多态
对代码进行重构:
1.创建一个用户基类
class User
{
virtual void showUserType();
};
声明用于显示用户类型的虚函数,注意是虚函数,否则无法使用多态。
2.创建继承它的三个子类
class CommonAudience : User
{
void showUserType(){...}
};
class HouseManager : User
{
void showUserType(){...}
};
class SuperManager : User
{
void showUserType(){...}
};
那么,如何知道是哪一类用户呢?这需要用到设计模式中的简单工厂模式:
#include "UserFactory.h"
UserFactory::UserFactory()
{
}
User * UserFactory::createUser(int iUserType)
{
User *user = nullptr;
switch (iUserType)
{
case 0:
user = new CommonAudience();
break;
case 1:
user = new HouseManager();
break;
case 2:
user = new SuperManager();
break;
default:
break;
}
}
这样只要知道用户类型,工厂就实例化出合适的对象,通过多态返回父类的方式,实现相应的输出。
int main()
{
int iUserType;//用户类型。0:普通;1:房管;2:超管
cin >> iUserType;
User *user = UserFactory::createUser(iUserType);
user->showUserType();
system("pause");
return 0;
}
“以多态取代条件表达式”只需在工厂创建实例时需要一次条件判断,后面就不再需要判断了,提高了效率。而且代码的结构得以改善,如果需要增加其它用户类型,只需再写一个集成自User基类的子类,便于扩展和维护。
六、总结
我们的流程是:
面向过程 ——> 面向对象 : 提高代码的可复用性;
if语句 ——> 多态 : 提高代码的可维护和可扩展性(同时提高了程序的效率,因为工厂类创建对象后,调用该对象的方法就无需条件判断了)
源码已放在我的github上:点我