首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >效用IOC容器

效用IOC容器
EN

Code Review用户
提问于 2023-01-16 10:13:53
回答 1查看 109关注 0票数 3

这是一个倒置控制对象存储。它适用于多线程应用程序,它可以在启动时根据复杂的环境设置加载组件,

工作线程可以根据需要检索各个组件。

我需要2个重载的registerInstance()函数吗?

代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <memory>
#include <ranges>
#include <memory>
#include <algorithm>
#include <unordered_map>
#include <iterator>
#include <any>
#include <typeindex>
#include <functional>

template <typename... Args>
concept NP = sizeof...(Args) == 1;
template <typename... Args>
concept PRS = sizeof...(Args) > 1;

class Factory
{
public:
    template <PRS T, typename... Ps>
    using Generator2 = std::function<std::unique_ptr<T>(Ps &&...arg)>;
    template <NP T>
    using Generator = std::function<std::unique_ptr<T>()>;

    template <NP T>
    void registerInstance(Generator<T> gen)
    {
        factoryMap_[typeid(T)] = gen;
    }
    template <PRS T>
    void registerInstance(Generator2<T> gen)
    {
        factoryMap_[typeid(T)] = gen;
    }
    template <NP T>
    std::unique_ptr<T> resolve()
    {
        auto it = factoryMap_.find(typeid(T));
        try
        {
            return it == factoryMap_.end() ? nullptr : std::any_cast<Generator<T>>(it->second)();
        }
        catch (const std::bad_any_cast &o)
        {
            // logit
            throw o;
        }
    }
    static Factory &getInstance()
    {
        static Factory instance;
        return instance;
    }

private:
    std::unordered_map<std::type_index, std::any> factoryMap_;
};
class DBOperations
{
public:
    virtual ~DBOperations() = default;
    virtual std::string handle() const
    {
        return "DB Opeation";
    }
};

class NOSQLOperations
{
public:
    virtual ~NOSQLOperations() = default;
    NOSQLOperations(std::unique_ptr<DBOperations> &&obj) : compD_{std::move(obj)} {}
    std::string handle() const
    {
        return compD_->handle() + "-NO SQL";
    }

private:
    std::unique_ptr<DBOperations> compD_;
};
class JsonParser
{
public:
    JsonParser(std::unique_ptr<NOSQLOperations> &&obj) : compC_{std::move(obj)} {}
    virtual ~JsonParser() = default;
    std::string handle() const
    {
        return compC_->handle() + "-Json Parser";
    }

private:
    std::unique_ptr<NOSQLOperations> compC_;
};
class Handler
{
public:
    virtual ~Handler() = default;
    Handler(std::unique_ptr<JsonParser> &&obj) : compB_{std::move(obj)} {}
    std::string handle() const
    {
        return compB_->handle() + "-Handler";
    }

private:
    std::unique_ptr<JsonParser> compB_;
};
void registerComponents()
{
    auto &ioc = Factory::getInstance();
    ioc.registerInstance<Handler>([&] { return std::make_unique<Handler>(ioc.resolve<JsonParser>()); });
    ioc.registerInstance<JsonParser>([&] { return std::make_unique<JsonParser>(ioc.resolve<NOSQLOperations>()); });
    ioc.registerInstance<NOSQLOperations>([&] { return std::make_unique<NOSQLOperations>(ioc.resolve<DBOperations>()); });
    ioc.registerInstance<DBOperations>([&] { return std::make_unique<DBOperations>(); });
}
int main()
{
    registerComponents(); // can be called at startup
    auto &ioc = Factory::getInstance();
    auto i = ioc.resolve<Handler>();
    std::cout << i->handle() << '\n';
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2023-01-16 20:44:48

命名事物

NPPRS的缩写是什么?我一点线索都没有。特别是对于概念,我会选择清晰的名称来表达这些概念的含义。

Factory是一个非常通用的名称。什么工厂?如果您有一个需要多个工厂类型的代码库,怎么办?我会为这个类选择一个更独特的名称,即使它对任何其他源文件都是不可见的。

这不是线程安全的

它适用于多线程应用程序...。

我在您的代码中没有看到任何互斥或原子操作。请注意,std::unique_ptr与线程安全无关。

不需要两个重载

我需要2个重载的registerInstance()函数吗?

不是的。参数包可以绑定到零或多个参数,因此不需要为其设置特例。你只需写:

代码语言:javascript
复制
template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

template <typename T>
static void registerInstance(Generator<T> gen)
{
    getInstance().factoryMap_[typeid(T)] = gen;
}

同一类型的多个实例如何?

如果您有一些复杂的场景,其中有两个不同的数据库,该怎么办?也许您有两个从DBOperations派生的不同类,或者只是同一个类,但是您必须将不同的数据库URI传递给构造函数?考虑:

代码语言:javascript
复制
registerInstance<DBOperations>([] {
    return std::make_unique<MongoDBOperations>("example.com/db1");
});
registerInstance<DBOperations>([] {
    return std::make_unique<CouchDBOperations>("example.com/db2");
});

这是一个问题,因为在您的DBOperations中只有一个factoryMap_。同样有问题的是,对registerInstance()的第二个调用将悄悄地覆盖上一个调用。

你需要一个注册表吗?

注册表的问题是它是基于类型进行索引的。因为类型需要在编译时知道,所以有一个运行时映射没有多大意义。您唯一想要的就是能够在定义给定对象之前编写使用它的代码。但是可以在赋值之前声明变量,因此可以:

代码语言:javascript
复制
template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerGenerator;
Generator<JsonParser>      jsonParserGenerator;
Generator<NOSQLOperations> noSQLOperationsGenerator;
Generator<DBOperations>    dbOperationsGenerator;

handlerGenerator =         [&] { return std::make_unique<Handler>(jsonParserGenerator()); };
jsonParserGenerator =      [&] { return std::make_unique<JsonParser>(noSQLOperationsGenerator()); };
noSQLOperationsGenerator = [&] { return std::make_unique<NOSQLOperations>(dbOperationsGenerator()); };
dbOperationsGenerator =    [&] { return std::make_unique<DBOperations>(); };

auto handler = handlerGenerator();
std::cout << handler->handle() << '\n';

如果要声明包含在多个源文件中的头文件中的实例,可以将它们设置为inline

注意,现在您可以很容易地声明两个相同类型的不同生成器。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/282624

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档