引言
本篇文章主要解决的问题是我们在开发一些软件的时候,当业务发生变化的时候,我们尽量要少改动代码,所以我们引入了配置文件,我们只需要读取对应的配置文件里面的信息,就可以改变一些变量的性质。当我们读取了配置文件之后,我们必然要找到对应的类函数,但是那么多的类函数我们应该如何找到对应的呢,因为我们是从配置里面读的数据,所以在代码里面我们也不知道该写什么具体的类,所以我们这里就引入了动态类工厂。
动态类工厂
首先我们要介绍一下这个架构,typedef void* (*createFun)(void)这个是这个动态类工厂的核心,类工厂里面有 prt_createFunMap,存储的是类名(字符串类型)和对应的构建这个类的函数。ptr_dyinstances是用来存储创建的函数,用栈是因为可以方便销毁。然后这里我们使用的是单例模式,也就是这个类工厂全局只有一个,因为只有这一个工厂才可以动态存储,如果有多个工厂,那么我们无法快速找到我们应该要的类函数。
我们为了快速调用,我们提供了三个宏:
DECLARE_CLASS是用在.h文件里面的,为的就是创建prt_dcInstance指针,还有一个createFun()函数就是为了提供一个接口创建一个我们传进来的类
IMPLEMENT_CLASS是在.cpp文件里面的,目的就是根据我们的把类转化成字符串和对应的创建函数存储在map里面。
GET_INSTANCE_BY_NAME是通过我们传入的类名字得到对应的类指针,当然我们需要对应的基类指针接受(一般都是这样,因为这样才有动态的意义)。
DynamicClass类主要就是动态类,顾名思义就是表示存储里面的类,而这个构造函数里面就是注册类
cpp
#pragma once
#ifndef _CLASSHELPER_H
#define _CLASSHELPER_H
#include <map>
#include <string>
#include <stack>
typedef void* (*createFun)(void);
class ClassFactory {
private:
ClassFactory();
~ClassFactory();
std::map<std::string, createFun> prt_createFunMap; // 存储类名和创建函数的映射
std::stack<void*> ptr_dyinstances; // 用于存储创建的对象,方便销毁
ClassFactory(const ClassFactory&) = delete;
ClassFactory& operator=(const ClassFactory&) = delete;
public:
// 获取单例
void* getClassInstanceByName(std::string name);
// 把类名和创建函数注册到工厂中
void registerClass(std::string name, createFun method, void* dyinstance);
static ClassFactory& getInstance();
};
// 动态类,用于向类工厂注册支持通过类名创建的类
class DynamicClass {
public:
DynamicClass(std::string name, createFun method);
};
// 声明动态类
#define DECLARE_CLASS(className) \
private: \
static DynamicClass* prt_dcInstance; \
public: \
static void* createFun() {return new className();}
#define IMPLEMENT_CLASS(className) \
DynamicClass* className::prt_dcInstance = new DynamicClass(#className, className::createFun);
/*
typeName:告诉 C++ 返回什么类型
varName:给新对象起变量名字
className:告诉工厂创建哪个类
*/
#define GET_INSTANCE_BY_NAME(typeName, varName, className) \
typeName varName = (typeName)ClassFactory::getInstance().getClassInstanceByName(className)
#endif
这个就是实现的cpp
cpp
#include "ClassHelper.h"
ClassFactory::ClassFactory()
{
}
void* ClassFactory::getClassInstanceByName(std::string name)
{
auto iter = prt_createFunMap.find(name);
if (iter != prt_createFunMap.end()) {
return iter->second();
}
return nullptr;
}
void ClassFactory::registerClass(std::string name, createFun method, void* dyinstance)
{
prt_createFunMap.insert(make_pair(name, method));
ptr_dyinstances.push(dyinstance);
}
ClassFactory& ClassFactory::getInstance()
{
static ClassFactory instance;
return instance;
}
ClassFactory::~ClassFactory()
{
while (!ptr_dyinstances.empty()) {
delete ptr_dyinstances.top();
ptr_dyinstances.pop();
}
}
DynamicClass::DynamicClass(std::string name, createFun method)
{
ClassFactory::getInstance().registerClass(name, method, this);
}
读取配置文件
这是读取配置文件的一个架构,这个文件里面的数据是key=value的形式,有着基本的功能,一个是加载文件里面的内容到数组里面,一个是获取对应key的value,一个是通过key,value,对进行设置,一个是通过key移除,一个是把所有的数组内容保存到文件里面。
我们存储信息用的是map,存储所有的key值用的是set,方便对map里面的value进行查找。
最后我们提供了一个宏CREATE_PROPERTIES,相当于是提供一个varName的钥匙来对文件进行操作,如果之后想得到value,就只需要varName.getProperties("key");
cpp
#pragma once
#ifndef _PROPERTIES_H
#define _PROPERTIES_H
#include<iostream>
#include<fstream>
#include<map>
#include<string>
#include<algorithm>
#include<sstream>
#include<vector>
#include<set>
class Properties final {
private:
std::map<std::string, std::string>* props = nullptr;
std::set<std::string>* keys = nullptr;
void trim(std::string& s); // 去除字符串两端的空格
std::vector<std::string> split(const std::string& str, char pattern);
void loadKeys();
public:
Properties();
~Properties();
std::map<std::string, std::string>* getPorps() const;
std::set<std::string>* getKeys() const;
// 加载指定位置配置
bool load(const std::string& path);
// 通过key值获取属性
std::string getProperty(std::string key);
// 设置属性
void setPoperty(const std::string& key, const std::string& value);
// 移除属性
void removeProperty(std::string& key);
// 保存配置到指定的位置
bool save(const std::string& path);
};
// 创建Properties对象
#define CREATE_PROPERTIES(varName, fileName) \
Properties varName; \
varName.load("config/"#fileName".properties")
#endif
移除空格
cpp
void Properties::trim(std::string& s)
{
if (!s.empty()) {
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
}
}
通过传入的pattern来分割字符串,getline(input, temp, pattern)规定了读取的规则,就是从输入流input中读取字符串到temp中,然后遇到pattern停止。然后把对应的字符串放到res中,那么根据key = value的形式,res0就是key,res1就是value
cpp
std::vector<std::string> Properties::split(const std::string& str, char pattern)
{
std::vector <std::string> res;
std::stringstream input(str);
std::string temp;
while (getline(input, temp, pattern)) {
res.push_back(temp);
}
return res;
}
cpp
void Properties::loadKeys()
{
for (auto i : *props) {
keys->insert(i.first);
}
}
Properties::Properties()
{
this->props = new std::map<std::string, std::string>();
this->keys = new std::set<std::string>();
}
Properties::~Properties()
{
delete props;
delete keys;
}
std::map<std::string, std::string>* Properties::getPorps() const
{
return props;
}
std::set<std::string>* Properties::getKeys() const
{
return keys;
}
首先用ifstream读取文件,然后用line接受input的输入流,我们拿到line的时候先进行除空格和分割,分割成了两个之后按照pair的模板进行插入到props里面,不过为了考虑实际情况,我们还需要去除注释。
cpp
bool Properties::load(const std::string& path)
{
std::vector<std::string> paths = split(path, '.');
if (paths.size() == 0 || paths[paths.size() - 1] != "properties") {
throw std::runtime_error(path + " is not a properties file!");
}
std::ifstream input(path);
if (!input) {
throw std::runtime_error("can't load " + path);
}
std::string line;
while (getline(input, line)) {
trim(line);
if (line.empty() || line == "\r" || line[0] == '#') {
continue;
}
std::vector<std::string> res = split(line, '=');
if (res.size() < 2) {
std::cerr << "Properties format error!" << std::endl;
throw std::runtime_error("Properties format error!");
}
// 删除注释
size_t pos = res[1].find("#");
if (pos != std::string::npos) {
res[1].erase(pos);
}
for_each(res.begin(), res.end(),
[=](std::string& s)-> void {
trim(s);
});
props->insert(make_pair(res[0], res[1]));
}
input.close();
loadKeys();
return true;
}
cpp
std::string Properties::getProperty(std::string key)
{
if (props->find(key) == props->end()) {
std::cerr << "can't find value with " << key << std::endl;
return "";
}
std::string value = this->props->at(key);
return value;
}
void Properties::setPoperty(const std::string& key, const std::string& value)
{
this->props->insert(make_pair(key, value));
}
void Properties::removeProperty(std::string& key)
{
auto iter = props->find(key);
if (iter != props->end())
{
props->erase(iter);
}
}
bool Properties::save(const std::string& path)
{
std::ofstream output(path);
if (!output) {
throw std::runtime_error("can't construct the output file!");
}
for (auto item : *props) {
output << item.first << "=" << item.second << std::endl;
}
output.close();
return true;
}
总结
本篇文章到这里就结束了,这个是一个优化设计模式代码的工具,希望可以帮助大家理解~~~