对外接口类 与 内部功能实现类 解耦

对外接口类 与 内部功能实现类 解耦

有这样的场景:
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 动作的合法性。

CallerRequestCenter 发送 一个Command完成调用。Command 具有 Type数据(执行所必须的所有参数) 两个属性,类型是命令的唯一标识

RequestCenter 会根据 发送规则 审查 Caller 动作的合法性。

RequestCenter 查询 所有注册的Answer类型匹配的Answer,并 传递CommandAnswer执行 。

分析

名词:
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获取的类型名字作为他们的唯一标识。
  1. 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);
    }
};
  1. IAnswer
#include "ICommand.h"

/**
 * 响应者接口
 * 响应者必须实现execute接口
 */
class IAnswer : public Type{
public:
    IAnswer() {}
    virtual ~IAnswer() {}
    /**
     * 执行命令
     * @param command 具体的命令指针
     */
    virtual void execute(ICommand* command) = 0;
};
  1. ICaller
#include "Type.h"

/**
 * 所有调用者都要继承这个类
 * 方便获取类型唯一标识,进行检查
 */
class ICaller : public Type {
public:
    ICaller() {}
    virtual ~ICaller() {}
};
  1. ICommand
/**
 * ICommand, 所有命令都需要继承这个接口
 * 参数因为每个具体命令,会有不同,子类实现
 */
class ICommand : public Type {
public:
    ICommand() {}
    virtual ~ICommand() {}
};

  1. 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;
}
  1. 测试

#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

遗留问题,白名单还没实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值