我有一个类Level
的对象,它有几个用于管理其状态的子系统(子对象)(例如EntityManager
,InputManager
,Physics
)。我想在Level
的外部接口中公开这些子系统的一些方法。
这是一个解决方案:
uint32_t Level::CreateEntity()
{
return entityManager.CreateEntity();
}
Entity& Level::GetEntity(uint32_t entityId)
{
return entityManager.GetEntity(entityId);
}
uint16_t Level::CreateInputState()
{
return inputManager.CreateInputState();
}
void Level::AttachInputState(uint32_t entityId, uint16_t inputStateId)
{
inputManager.AttachInputState(entityId, inputStateId);
}
InputState& Level::GetInputState(uint16_t inputStateId)
{
return inputManager.GetInputState(inputStateId);
}
此解决方案要求我复制Level
类中的方法声明,并编写将控件重定向到子系统的单行调用。在我过去曾经做过的项目中,管理起来很麻烦。
另一种解决方案是在公共接口中公开子系统。最好这可以避免,因为对于Level
以外的对象来说,调用转发到哪个子系统并不重要。
是否有设计可以更优雅地处理此问题?
答案 0 :(得分:1)
根据OP的要求,我将发布我在评论中建议的解决方案。 我很确定,在新的C ++标准中有一个更好的模板,但无论如何我会发布丑陋且不应该使用的预处理器解决方案!
#define FUNCTION_DECLARATION(RETURNTYPE, FUNCTIONNAME, ...) \
RETURNTYPE FUNCTIONNAME(__VA_ARGS__)
这将在类声明中用作:
class Level {
FUNCTION_DECLARATION(uint32_t, CreateEntity);
FUNCTION_DECLARATION(Entity&, GetEntity, uint32_t);
};
定义如下:
#define FUNCTION_DEFINITION(RETURNTYPE, PROPERTY, FUNCTIONNAME, ...) \
RETURNTYPE Level::FUNCTIONNAME(__VA_ARGS__) \
{ \
return PROPERTY.FUNCTIONNAME(__VA_ARGS__); \
} \
现在使用这个非常难看的用法:
FUNCTION_DEFINITION(Entity&, entityManager, GetEntity, uint32_t(entityId))
我不能保证这对任何类型都有效,我还没有测试过大部分代码。正如您所看到的,输入的“hack”仅适用于普通类型而不适用于引用或指针。在类上它将触发复制构造函数!
当然这可以通过将VA_ARGS替换为函数的每个参数的类型和变量名的变量来改进,但这又是非常乏味的,并且必须为每个应该为的每个参数编写一个模板。用过的。结果这将产生与以前一样多的代码。
所以让我再说一遍:坚持使用模板远远比这更好! 只是我不知道如何与他们一起做。所以,如果有人知道如何使用模板,而不是抨击我 ** 代码,请写下这个美丽的解决方案,每个人都渴望。
答案 1 :(得分:1)
聚合功能的另一种方法是通过私有继承。然后,您可以使用using
指令将一些继承的私有方法转换为公共方法。例如:
#include <iostream>
class feature_A
{
public:
void func_A1() { std::cout << "A1" << std::endl; }
void func_A2() { std::cout << "A2" << std::endl; }
};
class feature_B
{
public:
void func_B1() { std::cout << "B1" << std::endl; }
void func_B2() { std::cout << "B2" << std::endl; }
};
class compound : private feature_A, private feature_B
{
public:
// Provide these functions as-is.
using feature_A::func_A1;
using feature_B::func_B1;
// Combine these two functions.
void func_C2() { func_A2(); func_B2(); }
};
int main()
{
compound c;
c.func_A1();
c.func_B1();
c.func_C2();
// c.func_A2(); // error: ‘void feature_A::func_A2()’ is inaccessible
}
这些using
声明的一个限制是它们具有名称。如果您有多个相同功能的重载,则不能只选择一个要公开的重载。类似地,对于模板方法:您不能使用using
仅选择一个专门化。
答案 2 :(得分:0)
我认识的唯一想法是让界面更优雅的是让另一个对象包含private
EntityManager
,Physics
等等,并让它{{ 1)暴露函数,这些函数调用您希望客户端能够在底层对象上调用的每个函数。使public
在公共接口中具有这些代理对象的实例,并可选择使这些代理不可复制,不可移动等。
这不会减少你的工作量(实际上会增加它),但它会使界面更有条理,更容易使用。
示例:
Level
然后你可以像
一样使用它class Physics {
public:
T gravity() { ... } // we want them to be able to call this
T2 nope() { ... } // but not this
};
class LevelPhysics {
public:
T gravity() { return phys.gravity(); }
private:
LevelPhysics(Physics& phys) : phys(phys) { }
Physics& phys;
friend class Level;
};
class Level {
public:
LevelPhysics GetPhysics() { return LevelPhysics(phys); }
private:
Physics phys;
};
不幸的是,没有语言功能可以“为你制作这些”。你不能自己做一些编码。
或者,如果您知道需要访问const LevelPhysics& phys = lvl.GetPhysics();
phys.gravity();
// but you can't use Physics::nope
的秘密成员的所有类,您可以这样做:
Physics
然后
class Physics {
public:
T gravity() { ... }
private:
T2 nope() { ... }
friend class Level;
};
class Level {
public:
Physics physics;
};