这可以重构为适当的依赖注入吗?
Can this be refactored for proper Dependency Injection?
我在重构以下代码以使用正确的依赖注入时遇到问题。
那是因为我 没有 访问状态 Class constructors
我现在的主要限制是注入实现映射是用字符串完成的,如果出现错字,会有一个很好的提示脂肪异常。
我怎样才能:
- 编译时检查该实现是否确实存在?
- 拥有动态映射并去掉字符串
- 配置的中心点
这里有一些示例代码来演示这个问题
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();)
}
- having dynamic map and get rid of the strings
- Central point of configuration
目前你有这个 mapping/relation:
---> --->
AStateConcrete "A" A2
BStateConcrete "B" B1
您可以省略中间步骤并直接映射:
--->
AStateConcrete A2
BStateConcrete B1
为此,您可以将工厂中的地图替换为
std::map<std::type_info, std::shared_ptr<IState>> mImplementedViews;
并使用 typeid
运算符(returns 所需的 std::type_info
)来填充它。
虽然这对你的第一点没有帮助:
- have compile time checks that the implementation indeed exists?
为此,您需要以某种类型对有关可用实现的信息进行编码(否则编译器无法对其进行检查)。这是一些非常有趣的元编程,或者您使用例如 boost MPL set.
我在重构以下代码以使用正确的依赖注入时遇到问题。 那是因为我 没有 访问状态 Class constructors
我现在的主要限制是注入实现映射是用字符串完成的,如果出现错字,会有一个很好的提示脂肪异常。
我怎样才能:
- 编译时检查该实现是否确实存在?
- 拥有动态映射并去掉字符串
- 配置的中心点
这里有一些示例代码来演示这个问题
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();)
}
- having dynamic map and get rid of the strings
- Central point of configuration
目前你有这个 mapping/relation:
---> --->
AStateConcrete "A" A2
BStateConcrete "B" B1
您可以省略中间步骤并直接映射:
--->
AStateConcrete A2
BStateConcrete B1
为此,您可以将工厂中的地图替换为
std::map<std::type_info, std::shared_ptr<IState>> mImplementedViews;
并使用 typeid
运算符(returns 所需的 std::type_info
)来填充它。
虽然这对你的第一点没有帮助:
- have compile time checks that the implementation indeed exists?
为此,您需要以某种类型对有关可用实现的信息进行编码(否则编译器无法对其进行检查)。这是一些非常有趣的元编程,或者您使用例如 boost MPL set.