【问题标题】:Can this be refactored for proper Dependency Injection?这可以重构为正确的依赖注入吗?
【发布时间】:2017-07-22 08:33:51
【问题描述】:

我在重构以下代码以使用正确的依赖注入时遇到了麻烦。 那是因为我没有访问 State Class constructors

我现在的主要限制是注入实现映射是用字符串完成的,如果出现拼写错误,将会出现一个很好的异常。

我该怎么做:

  1. 编译时检查该实现确实存在?
  2. 拥有动态地图并摆脱字符串
  3. 配置中心点

这里有一些示例代码来演示这个问题

struct IState
{
    virtual void Entry() = 0;
    virtual void Update() = 0;
};

struct ABase :IState
{
    void Entry() override { /* Default implementation..*/ }
    void Update() override { /* Default implementation..*/}
};

struct A1 : ABase
{
    void Entry() override { /*...*/ }
    void Update() override { /*...*/ }
};

struct A2 :ABase
{
    void Entry() override { /*...*/ }
};

struct BBase :IState
{
    void Entry() override { /* Default implementation..*/ }
    void Update() override { /* Default implementation..*/ }
};

struct B1 :BBase
{
    void Entry() override { /*...*/ }
};

// This is to return the desired implementation based on a key string
struct SFactory
{
    SFactory()
    {  
// This is the binding of the implementations and the States. 
// I don't really like it, 
// but I could live with it IF it was the only place 
// that the keys "A" "B" were mentioned.
        mImplementedStates.insert(std::make_pair("A", std::shared_ptr<IState>(new A2())));
        mImplementedStates.insert(std::make_pair("B", std::shared_ptr<IState>(new B1())));
    }
    static SFactory& GetInstance()
    {
        static SFactory msInstance;
        return msInstance;
    }

    std::shared_ptr<IState> GetState(std::string implementation) {
        auto it = mImplementedStates.find(implementation);
        if (it == mImplementedStates.end())
        {
            throw std::invalid_argument("Unregistered Implementation: " + implementation);
        }
        return it->second;
    }

private:
    std::map<std::string, std::shared_ptr<IState>> mImplementedStates;
};

// this is the class that I want to inject functionality. This is a wrapper of the actual implementation.
struct AStateConcrete : ThirdPartyLink
{
    // Cannot have my onw constructor because of the library
    // The library instantiate me.

private: std::shared_ptr<IState> mState;

    public: 
        // This is how I pick the  once by the 3rd party library
        void Entry()
        {
           // this is the ugly part. This "A" wont change however someone
// that wants to create a new implementation has to visit this code
// to know which "id" he should use in the factory. IF he makes a typo
// this will an throw an exception
            mState = SFactory::GetInstance().GetState("A");
            mState->Entry();
        }

        void Update()
        {
            mState->Update();
        }

        void GoB()
        {
            //...
        }
};

// this is another class that I want to inject functionality. This is a wrapper of the actual implementation.
struct BStateConcrete : ThirdPartyLink
{
    // Cannot have my onw constructor because of the library
    // The library instantiate me.

private: std::shared_ptr<IState> mState;

public:
    // This is how I pick the functionalityCalled once by the 3rd party library
    void Entry()
    {
        mState = SFactory::GetInstance().GetState("B");
        mState->Entry();
    }

    void Update()
    {
        mState->Update();
    }

    void GoA()
    {
        //...
    }
};
int main()
{
    SFactory::GetInstance();
    ThirdPartyStateMachine<ThirdPartyLink, AStateConcrete /*As initial State*/> sm; // D
    // A::Entry() is called;

    sm->Update(); // A::Update() is called (thus A2::Update();)

    sm->GoB(); 
    //  B::Entry() is called (Thus B1::Entry();)

    sm->Update(); // B::Update() is called (thus B1::Update();)
}

【问题讨论】:

标签: c++ dependency-injection refactoring inversion-of-control


【解决方案1】:
  1. 拥有动态地图并摆脱字符串
  2. 配置中心点

目前你有这个映射/关系:

                --->       --->
AStateConcrete        "A"        A2
BStateConcrete        "B"        B1

您可以省略中间步骤并直接映射:

                --->
AStateConcrete        A2
BStateConcrete        B1

为此,您可以将工厂中的地图替换为

std::map<std::type_info, std::shared_ptr<IState>> mImplementedViews;

并使用typeid 运算符(返回所需的std::type_info)来填充它。

虽然这对您的第一点没有帮助:

  1. 编译时检查该实现确实存在?

为此,您需要以某种类型对有关可用实现的信息进行编码(否则编译器无法检查它)。这是一些非常有趣的元编程,或者您可以使用例如boost MPL set.

【讨论】:

  • 感谢您的意见。我按照您的建议进行了重构,感觉已经好 1000 倍。但是我使用 type_index 而不是 type_info 作为:std::map&lt;std::type_index, std::shared_ptr&lt;IState&gt;&gt; mStatesImplementations; // For a reason the std::make_pair fails. mImplementedViews.insert( std::pair&lt;std::type_index, std::shared_ptr&lt;IState&gt;&gt;( typeid(AStateConecrete), std::shared_ptr&lt;IState&gt;(new A2()) )); 和具体状态 ... mState = SFactory::GetInstance().GetState(typeid(ConecreteStateX));
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-23
  • 2020-04-28
  • 2020-05-11
  • 2019-02-25
  • 2016-09-15
相关资源
最近更新 更多