每日一句英语学习,每天进步一点点:
- "Without purpose, the days would have ended, as such days always end, in disintegration."
- 「少了目标,一天还是会结束,它总是以支离破碎的形式结束。」
羊哥之前写一篇有趣的文章《答应我,别再if/else走天下了可以吗 | CodeSheep 》,在文中使用 Java 语言实现了枚举类、工厂模式和策略模式的三种方式,来消除连环的 if / else
。内容层层递进,由浅入深的方式我非常喜欢。
看到有留言中有小伙伴想看 C++ 版本的,特此写下了此文(已经过羊哥的同意)。不过由于 C++ 没有枚举类,所以本文不涉及此方式,但本文会带大家一步一步的优化工厂模式和策略模式。
正文 糟糕 if / else 连环if / else
可以说是我们学习编程时,第一个学习的分支语句,简单易理解,生活中也处处有的 if / else
例子:
老婆给当程序员的老公打电话:“下班顺路买一斤包子带回来,如果看到卖西瓜的,买一个。” 当晚,程序员老公手捧一个包子进了家门。。。 老婆怒道:“你怎么就买了一个包子?!” 老公答曰:“因为看到了卖西瓜的。”
老婆的思维:
买一斤包子;
if( 看到卖西瓜的 )
买一只( 西瓜 );
而程序员老公的程序:
if( ! 看见卖西瓜的 )
买一斤包子;
else
买一只( 包子 );
非常生生动动的生活例子!如果身为程序员的你,犯了同样的思维错误,别继续问你媳妇为什么,问就是跪键盘:
进入本文正题。考虑以下栗子:一般来说我们正常的后台管理系统都有所谓的角色的概念,不同管理员权限不一样,能够行使的操作也不一样。
- 系统管理员(
ROLE_ROOT_ADMIN
):有A
操作权限 - 订单管理员(
ROLE_ORDER_ADMIN
):有B
操作权限 - 普通用户(
ROLE_NORMAL
):有C
操作权限
假设一个用户进来,我们需要根据不同用户的角色来判断其有哪些行为。使用过多 if / else
连环写法的我们,肯定下意识就觉得,这不简单嘛,我上演一套连环的写法:
class JudgeRole
{
public:
std::string Judge( std::string roleName )
{
std::string result = "";
if( roleName == "ROLE_ROOT_ADMIN" ) // 系统管理员
{
result = roleName + "has A permission";
}
else if( roleName == "ROLE_ORDER_ADMIN" ) // 订单管理员
{
result = roleName + "has B permission";
}
else if( roleName == "ROLE_NORMAL" ) // 普通用户
{
result = roleName + "has C permission";
}
return result;
}
};
当系统里有几十个角色,那岂不是几十个 if / else
嵌套,这个视觉效果绝对酸爽……这种实现方式非常的不优雅。
别人看了这种代码肯定大声喊:“我X,哪个水货写的!”
这时你听到,千万不要说:“那我改成 switch / case
”。千万别说,千万别说哦,否则可能拎包回家了…
因为 switch / case
和 if / else
毛区别都没,都是写费劲、难阅读、不易扩展的代码。
接下来简单讲几种改进方式,别再 if / else 走天下了。
工厂模式 —— 它不香吗?不同的角色做不同的事情,很明显就提供了使用工厂模式的契机,我们只需要将不同情况单独定义好,并聚合到工厂里面即可。
首先,定义一个公用接口 RoleOperation
,类里有一个纯虚函数 Op
,供派生类(子类)具体实现:
// 基类
class RoleOperation
{
public:
virtual std::string Op() = 0; // 纯虚函数
virtual ~RoleOperation() {} // 虚析构函数
};
接下来针对不同的角色类,继承基类,并实现 Op 函数:
// 系统管理员(有 A 操作权限)
class RootAdminRole : public RoleOperation {
public:
RootAdminRole(const std::string &roleName)
: m_RoleName(roleName) {}
std::string Op() {
return m_RoleName + " has A permission";
}
private:
std::string m_RoleName;
};
// 订单管理员(有 B 操作权限)
class OrderAdminRole : public RoleOperation {
public:
OrderAdminRole(const std::string &roleName)
: m_RoleName(roleName) {}
std::string Op() {
return m_RoleName + " has B permission";
}
private:
std::string m_RoleName;
};
// 普通用户(有 C 操作权限)
class NormalRole : public RoleOperation {
public:
NormalRole(const std::string &roleName)
: m_RoleName(roleName) {}
std::string Op() {
return m_RoleName + " has C permission";
}
private:
std::string m_RoleName;
};
接下来在写一个工厂类 RoleFactory
,提供两个接口:
- 用以注册角色指针对象到工厂的
RegisterRole
成员函数 - 用以获取对应角色指针对象的
GetRole
成员函数
// 角色工厂
class RoleFactory {
public:
// 获取工厂单例,工厂的实例是唯一的
static RoleFactory& Instance() {
static RoleFactory instance; // C++11 以上线程安全
return instance;
}
// 把指针对象注册到工厂
void RegisterRole(const std::string& name, RoleOperation* registrar) {
m_RoleRegistry[name] = registrar;
}
// 根据名字name,获取对应的角色指针对象
RoleOperation* GetRole(const std::string& name) {
std::map::iterator it;
// 从map找到已经注册过的角色,并返回角色指针对象
it = m_RoleRegistry.find(name);
if (it != m_RoleRegistry.end()) {
return it->second;
}
return nullptr; // 未注册该角色,则返回空指针
}
private:
// 禁止外部构造和虚构
RoleFactory() {}
~RoleFactory() {}
// 禁止外部拷贝和赋值操作
RoleFactory(const RoleFactory &);
const RoleFactory &operator=(const RoleFactory &);
// 保存注册过的角色,key:角色名称 , value:角色指针对象
std::map m_RoleRegistry;
};
把所有的角色注册(聚合)到工厂里,并封装成角色初始化函数InitializeRole
:
void InitializeRole() // 初始化角色到工厂
{
static bool bInitialized = false;
if (bInitialized == false) {
// 注册系统管理员
RoleFactory::Instance().RegisterRole("ROLE_ROOT_ADMIN", new RootAdminRole("ROLE_ROOT_ADMIN"));
// 注册订单管理员
RoleFactory::Instance().RegisterRole("ROLE_ORDER_ADMIN", new OrderAdminRole("ROLE_ORDER_ADMIN"));
// 注册普通用户
RoleFactory::Instance().RegisterRole("ROLE_NORMAL", new NormalRole("ROLE_NORMAL"));
bInitialized = true;
}
}
接下来借助上面这个工厂,业务代码调用只需要一行代码,if / else
被消除的明明白白:
class JudgeRole {
public:
std::string Judge(const std::string &roleName) {
return RoleFactory::Instance().GetRole(roleName)->Op();
}
};
需要注意:在使用 Judge
时,要先调用初始化所有角色 InitializeRole
函数(可以放在 main
函数开头等):
int main() {
InitializeRole(); // 优先初始化所有角色到工厂
JudgeRole judgeRole;
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?