对外接口类 与 内部功能实现类 解耦
有这样的场景:
A类封装B,B类封装C.
A类是对外接口,C是具体实现功能的类。
class C{
public:
func1(){
cout << "run func1" << endl;
}
};
class B{
private:
C c;
public:
func1(){
c.func1();
}
};
class A{
private:
B b;
public:
void func1(){
b.func1();
}
}
int main(){
A a;
cout << a.func1() << endl;
}
当C增加一个接口或者是功能,这个功能要想通过A对外界暴露,那么B、A都要增加相应接口。
新增一个对外功能,就要改动A、B。比如新增func2();
class C{
public:
func1(){
cout << "run func1" << endl;
}
func2(){
cout << "run func1" << endl;
}
};
class B{
private:
C c;
public:
func1(){
c.func1();
}
func2(){
c.func2();
}
};
class A{
private:
B b;
public:
void func1(){
b.func1();
}
void func2(){
b.func2();
}
}
这里只是做一个简单的比喻。如果我们把封装类和被封装的类,分别看做各位于一层的话。A作为外部接口,是一层,C为具体实现功能的类,又是一层。实际上,他们之间的类可能不止一层。增加外部接口时,除了修改接口类A、功能实现类C,中间封装的层数越多修改起来涉及的类就越多。而中间层类的修改,对我们来说是个负担,让中间层的类接口变得越来越臃肿。
那么就需要,调用者类、具体的响应类(功能实现类)和中间层的类进行解耦。
情景
调用者类(Caller)不需要知道具体的响应者(Answer)是谁。全部通过全局对象RequestCenter中转。
Answer需要 注册 到(RequestCenter)才能响应Command。需要提供 Type 以说明可以处理何种Command。
Answer 可以 取消注册 到 RequestCenter ,也就不会再处理任何请求。
RequestCenter 会根据 注册规则 审查 Answer 动作的合法性。
Caller 向 RequestCenter 发送 一个Command完成调用。Command 具有 Type 和数据(执行所必须的所有参数) 两个属性,类型是命令的唯一标识
RequestCenter 会根据 发送规则 审查 Caller 动作的合法性。
RequestCenter 查询 所有注册的Answer,类型匹配的Answer,并 传递Command给 Answer执行 。
分析
名词:
RequestCenter:存储注册的Answer,接收Caller发来的命令请求,分发给Answer执行。Answer是否有权限注册,Caller是否有权限发送命令请求等权限管理。
发送规则、注册规则:RequestCenter使用
Answer:响应者负责具体的功能实现,相应Command
Caller:调用者,负责发送Command
Type:是Command的类型,每个具体的命令都不一样,需要具有唯一性
Command:具体的指令,有Type+数据两个属性
动词:
注册:Answer注册到RequestCenter
取消注册:同注册。
发送请求:Caller发送Command到RequestCenter
规则审查:RequestCenter审查Caller和Answer的合法性
执行:RequestCenter调用Answer执行Command
隐含条件:
RequestCenter需要一个结构,存储所有注册的Answer。
使用map,key是类型,value是Answer指针
如果根据Commond确定哪个Answer才是我们需要找的。
Command有类型,Answer也要声明自己支持那种Command
如何审查Caller和Answer的合法性。
人为的定义一个config文件,确定哪种类型的Answer可以相应哪种Command,哪种Caller可以响应哪种Command
总结:
Caller、Command、Answer,都需要有类型,有唯一标识。可以用typeid获取的类型名字作为他们的唯一标识。
- Type
#include <string>
#define GET_TYPE_NAME(c) (typeid(c).name())
/**
* 基础类,所有Command,Caller,Answer都要继承这个类
* 类型使用typeid(*this).name(),这样每个继承Command的类,getName会得到不同的唯一的字符串。
*/
class Type{
public:
virtual ~Type() {}
Type() {}
std::string getCmdType(){
return GET_TYPE_NAME(*this);
}
};
- IAnswer
#include "ICommand.h"
/**
* 响应者接口
* 响应者必须实现execute接口
*/
class IAnswer : public Type{
public:
IAnswer() {}
virtual ~IAnswer() {}
/**
* 执行命令
* @param command 具体的命令指针
*/
virtual void execute(ICommand* command) = 0;
};
- ICaller
#include "Type.h"
/**
* 所有调用者都要继承这个类
* 方便获取类型唯一标识,进行检查
*/
class ICaller : public Type {
public:
ICaller() {}
virtual ~ICaller() {}
};
- ICommand
/**
* ICommand, 所有命令都需要继承这个接口
* 参数因为每个具体命令,会有不同,子类实现
*/
class ICommand : public Type {
public:
ICommand() {}
virtual ~ICommand() {}
};
- RequestCenter
//头文件
#include "IAnswer.h"
#include "ICaller.h"
#include "ICommand.h"
#include "Type.h"
#include <mutex>
#include <unordered_map>
#include <set>
#include <map>
/**
* 请求中心,单例模式
* 存储所有注册的Answer
* 接收Caller的Command,并分发给相应的Answer执行
*/
class RequestCenter {
private:
/**
* 检查Answer的合法性
* @return
*/
bool checkRegisterLegal(std::string commandName, IAnswer *answer);
/**
* 检查Caller调用者的合法性
* @return
*/
bool checkRequestLegal(ICaller* caller, std::string commandName);
/**
* 单例模式 构造函数私有化
*/
RequestCenter();
/**
* 辅助锁
*/
static std::mutex gMutex;
/**
* 唯一实例
*/
static RequestCenter *gRequestCenter;
/**
* 注册的所有Answer集中存储到Map中
* key:命令名
* value:是一堆Answer指针的集合set
*/
std::unordered_map<std::string, std::set<IAnswer*>> gMap;
/**
* 调用者白名单
* 调用者名称,命令名称
*/
std::map<std::string, std::string> allowCallerMap;
/**
* 响应者白名单
* 响应者名称,命令名称
*/
std::map<std::string, std::string> allowAnswerMap;
public:
~RequestCenter();
/**
* 获取实例,全局唯一
* @return
*/
static RequestCenter *getInstance();
/**
* 注册Answer到RequestCenter
* @param commandName 命令name
* @param answer 响应者
*/
void registerAnswer(std::string commandName, IAnswer *answer);
/**
* 取消注册
* @param commandName 命令名
* @param answer 响应者
*/
void unRegisterAnswer(std::string commandName, IAnswer *answer);
/**
* 发送命令
* @param caller 发送者
* @param command 具体命令
* @return 成功0 非0失败
*/
int callerRequest(ICaller* caller, ICommand* command);
};
//实现
#include "RequestCenter.h"
std::mutex RequestCenter::gMutex;
RequestCenter *RequestCenter::gRequestCenter = nullptr;
RequestCenter::~RequestCenter() {
}
static void insertAllow(std::map<std::string, std::string> m,
std::string commandName, std::string userName){
m.insert(std::pair<std::string, std::string> (commandName, userName));
}
RequestCenter::RequestCenter() {
}
bool RequestCenter::checkRegisterLegal(std::string commandName, IAnswer *answer) {
return true;
}
bool RequestCenter::checkRequestLegal(ICaller* caller, std::string commandName) {
return true;
}
void RequestCenter::registerAnswer(std::string commandName, IAnswer *answer) {
if(answer == nullptr){
return ;
}
if(!checkRegisterLegal(commandName, answer)){
return ;
}
std::lock_guard<std::mutex> guard(gMutex);
gMap[commandName].insert(answer);
}
void RequestCenter::unRegisterAnswer(std::string commandName, IAnswer *answer) {
if(answer == nullptr){
return ;
}
if(!checkRegisterLegal(commandName, answer)){
return ;
}
std::lock_guard<std::mutex> guard(gMutex);
if(gMap.count(commandName) != 0 && gMap[commandName].count(answer) != 0){
gMap[commandName].erase(answer);
}
}
int RequestCenter::callerRequest(ICaller *caller, ICommand *command) {
if(command == nullptr){
return -1;
}
if(!checkRequestLegal(caller, command->getCmdType())){
return -1;
}
std::lock_guard<std::mutex> guard(gMutex);
std::string commandType = command->getCmdType();
if(gMap.count(commandType) != 0){
auto &s = gMap[commandType];
for(auto it = s.begin ();it != s.end(); ++it){
(*it)->execute(command);
}
}
return 0;
}
RequestCenter *RequestCenter::getInstance() {
if(gRequestCenter == nullptr){
std::lock_guard<std::mutex> guard(gMutex);
if(gRequestCenter == nullptr){
gRequestCenter = new RequestCenter();
}
}
return gRequestCenter;
}
- 测试
#include <iostream>
#include "RequestCenter.h"
using namespace std;
class SetMicVolumeCommand : public ICommand{
private:
int volume;
const int maxVolume = 100;
public:
virtual ~SetMicVolumeCommand() {
}
void setVolume(int volume){
this->volume = volume;
}
int getVolume(){
return volume;
}
};
class SetMusicVolumeCommand : public ICommand{
private:
int volume;
const int maxVolume = 100;
public:
virtual ~SetMusicVolumeCommand() {
}
void setVolume(int volume){
this->volume = volume;
}
int getVolume(){
return volume;
}
};
class Answer : public IAnswer{
public:
Answer() {
}
virtual ~Answer() {
}
virtual void execute(ICommand *command) {
if(command->getCmdType() == GET_TYPE_NAME(SetMicVolumeCommand)){
cout << " mic volume = " <<
dynamic_cast<SetMicVolumeCommand*>(command)->getVolume() << endl;
}else if(command->getCmdType() == GET_TYPE_NAME(SetMusicVolumeCommand)){
cout << " music volume = " <<
dynamic_cast<SetMusicVolumeCommand*>(command)->getVolume() << endl;
}
}
};
int main(){
IAnswer *answer = new Answer();
RequestCenter::getInstance()->registerAnswer(GET_TYPE_NAME(SetMicVolumeCommand), answer);
RequestCenter::getInstance()->registerAnswer(GET_TYPE_NAME(SetMusicVolumeCommand), answer);
SetMicVolumeCommand *setMicVolumeCommand = new SetMicVolumeCommand();
SetMusicVolumeCommand *setMusicVolumeCommand = new SetMusicVolumeCommand();
setMusicVolumeCommand->setVolume(10);
setMicVolumeCommand->setVolume(100);
RequestCenter::getInstance()->callerRequest(nullptr, setMicVolumeCommand);
RequestCenter::getInstance()->callerRequest(nullptr, setMusicVolumeCommand);
setMusicVolumeCommand->setVolume(20);
setMicVolumeCommand->setVolume(90);
RequestCenter::getInstance()->callerRequest(nullptr, setMicVolumeCommand);
RequestCenter::getInstance()->callerRequest(nullptr, setMusicVolumeCommand);
RequestCenter::getInstance()->unRegisterAnswer(GET_TYPE_NAME(SetMicVolumeCommand), answer);
setMusicVolumeCommand->setVolume(30);
setMicVolumeCommand->setVolume(80);
RequestCenter::getInstance()->callerRequest(nullptr, setMicVolumeCommand);
RequestCenter::getInstance()->callerRequest(nullptr, setMusicVolumeCommand);
return 0;
}
输出
mic volume = 100
music volume = 10
mic volume = 90
music volume = 20
music volume = 30
遗留问题,白名单还没实现。