最近在读《重构 改善既有代码的设计》这本书,其实这本书大部分都在讲如何写出可维护、可复用、可扩展的程序,还有很多地方将一些类似代码规范什么的。
看完之后,我觉得其中最值得学习的就是“以多态取代条件表达式”这个规则了。
一、案例
最近直播很流行,那么就拿直播来举个例子。
比如熊猫直播,直播间里的观众一般分为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上:点我