这可以重构为适当的依赖注入吗?

时间:2017-03-02 14:45:12

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

我遇到了重构以下代码以使用正确的依赖注入的麻烦。 那是因为我可以访问State Class 构造函数

我的主要限制现在是使用字符串完成注入实现映射,并且在输入错误的情况下会有一个很好的胖异常。

我怎么能:

  1. 有编译时检查实现确实存在吗?
  2. 拥有动态地图并摆脱字符串
  3. 中心配置点
  4. 这里有一些示例代码来演示问题

    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();)
    }
    

1 个答案:

答案 0 :(得分:1)

  
      
  1. 拥有动态地图并摆脱字符串
  2.   
  3. 中心配置点
  4.   

目前您有这种映射/关系:

                --->       --->
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. 有编译时检查实现确实存在吗?
  2.   

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