Java 创建特定于运行时类的注册系统的最佳方法?
我目前正在尝试创建一个系统,在该系统中,我希望能够在运行时为类分配唯一的族ID。本质上,我希望在运行时注册类之后,能够基于整数值区分它们 这种情况下的用例是,该系统将用作组件系统的管理器。所有类都是类组件的后代(不一定是直接的);并在运行时注册 我希望能够做到这一点有几个原因:Java 创建特定于运行时类的注册系统的最佳方法?,java,c++,oop,Java,C++,Oop,我目前正在尝试创建一个系统,在该系统中,我希望能够在运行时为类分配唯一的族ID。本质上,我希望在运行时注册类之后,能够基于整数值区分它们 这种情况下的用例是,该系统将用作组件系统的管理器。所有类都是类组件的后代(不一定是直接的);并在运行时注册 我希望能够做到这一点有几个原因: 扩展的安全性和可用性:人们不必修改基本组件系统中的庞大列表来添加组件 效率:内部查找使用此整数值完成,因此可以是O(1)而不是搜索查找。由于这些组件将构成系统的大部分,我不希望将其作为实例变量。我不能忽略这一方面,因为
- 扩展的安全性和可用性:人们不必修改基本组件系统中的庞大列表来添加组件
- 效率:内部查找使用此整数值完成,因此可以是O(1)而不是搜索查找。由于这些组件将构成系统的大部分,我不希望将其作为实例变量。我不能忽略这一方面,因为测试用例已经表明我无法承受>O(1)的删除和插入。(第一个使用地图查找表的实现)
interface Component {
// Should return the value of a static variable
int getFamilyID();
int setFamilyID(int id);
}
class Foo implements Component {
static int familyID = 0;
int getFamilyID(){ return familyID; }
int setFamilyID(int id){ familyID = id; }
}
class System { // Singleton
static int registeredComponents = 0;
void register(Component c){ c.setFamilyID(registeredComponents++); }
}
这显然不起作用,原因有二:
- 我不能指定A.getFamilyID(),除非我将变量公开;用例:c.getFamilyID()==Foo.getFamilyID();(而不是instanceof)
- 到处都是冗余代码;组件的每个实现都需要复制粘贴的实现
我认为我可以用指定静态变量的模板来解决C++中的问题,但是当类不是组件的直接后裔时,这将变得不可用。 我也不能在Java中使用枚举,因为它们是特定于语言的,而且组件的数量会使单个文件的代码非常庞大。(此外,必须在同一个地方再次指定所有项目)
在这件事上的任何帮助,或者对我为什么试图“做错事”(TM)的理解,都会非常有帮助:-)编辑:为了澄清这一点,我需要一些方法来确保在编译时可以在组件类中设置静态整数的代码约定。您基本上要求的是特定的运行时 必须在编译时检查行为。在一般情况下 这简直是不可能的:你可以编写你想要的所有函数, 但是编译器永远无法确保您调用 对于每种类型,给定函数一次,且仅一次。最好的你 可以做的就是使用某种静态变量,使函数 private,并在 建造商:
class Component
{
protected:
class Registrator
{
static int nextId;
int id;
public:
Registrator() ; id( nextId ++ ) {}
int id() const { return id; }
};
// ...
};
class Derived ; public Component
{
static Registrator ourId;
// ...
};
(您也可以在Java中执行此操作。只需放置静态注册器
ourId=new registator();
在每个派生对象的静态块中
类。)
您仍然必须(通过契约)要求每个派生类
包含一个且仅包含一个类型为Registrator
的静态成员。
一开始,我认为你无法避免这一点
请注意,通常,只要有基类和派生类
类,您需要依赖合同。如果基类
例如,具有虚拟功能克隆
(使用通常的
语义),每个派生类都必须实现它。有
无法在编译时强制执行此类操作;一些
契约式编程习惯用法将允许在
对象clone
的动态类型返回的运行时
正确,但即使在运行时也无法强制执行,
返回的对象是一个实际的副本,而不是一些完整的副本
不相关的实例
对此我只能说,我从来没有发现这是一个
实践中的问题。从组件
(或任何
其他基类)必须知道
组件
。你可以(也应该)验证一些事情,但是
最后,你不能验证所有的事情,实际上,有人
谁派生,忽略合同,将创建
不起作用,你也无能为力。(代码
审查在这方面有很长的路要走,特别是代码审查
包括测试范围,坚持所有合同
问题(已测试)
编辑:
最后一点意见:我反对使用int
作为
标识符。如果比较标识符的性能
重要信息,您仍然可以使用字符常量[]
;如果你保证
所有正确获取的标识符都指向(自实际
您使用的标识符将是同一字符串的字符常量*
),
你可以比较一下指针。派生合同
然后是:
class Derived : public Component
{
public:
static char const* className() { return "Derived"; }
// overriding virtual function in Component...
char const* type() const { return className(); }
// ...
};
然后,只需使用className
返回的char const*
,或者
键入
作为您的标识符
对于派生类的作者来说,这需要更多的输入,
但至少在C++中,总是有宏来简化它。
事实上,我会为这类事情推荐一个宏,甚至
使用上面的原始解决方案。如果派生类
使用宏,您可以更改策略而不更改
其他的。在C++中,可以使用它来避免代码重复。
class Component
{
public:
virtual ~Component() {}
virtual int getFamilyId() const = 0;
};
// each instance is assigned a unique int at construction
class FamilyId
{
static int numberOfExistingIds = 0;
int id;
public:
FamilyId() : id( numberOfExistingIds++ ) {}
int getId() const { return id; }
};
// implementation is done only in this template class
template <typename Derived, typename Base = Component>
class ComponentImpl : public Base
{
static FamilyId familyId; // one instance per class for unique id
public:
virtual int getFamilyId() const
{
assert( typeid(*this) == typeid(Derived) );
return familyId.getId();
}
};
在运行时。否则,这个派生类将使用与直接基相同的接口实现,并使用相同的静态变量,这将是一个bug
您可能已经注意到,您不需要手动注册任何类。这是通过在main()函数启动之前动态初始化静态变量来完成的。所以你不需要做任何事情。通过这种方式,您可以轻松地在一个地方实现您的类,而不改变其他文件,并且没有大量的代码重复-卓越。“语言特定”。那么,模板、泛型、接口等是否已检查。当您说“编译时已检查”时,您希望检查什么?你能举一个你希望能够使用的代码的例子吗
// first derived class, automagically implemented by template magic
class MyGeneralComponent
: public ComponentImpl<MyGeneralComponent>
{
/* add new methods here */
};
// class further down in the hierarchy are also possible,
// by using the second template argument. The implementation still works.
class MySpecificComponent
: public ComponentImpl<MySpecificComponent,MyGeneralComponent>
{
/* add new methods here */
};
class MySpecificComponent : MyGeneralComponent
{
};