Java "instanceof" 添加多个列表

Java "instanceof" to add over multiple lists

在了解到实现优于扩展之后,我目前正在尝试重构我的代码。目前,我正在尝试创建一个将对象添加到场景中的函数。为了更好的定义每个对象是什么,有多个List比如一个list用于更新,渲染等

private List<Updatable> updatables;
private List<Removable> removables;
private List<Renderable> renderables;
private List<Collidable> collidables;

我想在我的场景中创建一个函数 class,如下所示:

public void add(Object o) {
    if(o instanceof Updatable)
        updatables.add((Updatable) o);
    if(o instanceof Removable)
        removables.add((Removable) o);
    if(o instanceof Renderable)
        renderables.add((Renderable) o);
    if(o instanceof Collidable)
        collidables.add((Collidable) o);
}

同样,我会创建一个类似的删除功能。我的问题是,这是否是糟糕的编码习惯,因为我听说过 instanceof 的缺陷,其次,如果是,是否有解决此问题/重组我的代码以使添加实例变得简单的方法?我不想每次都向每个列表添加一个实例并定义它属于哪个列表。

我会使用 Map

对工厂模式做类似的事情
static Map<String, List> maps = new HashMap<>();
static {
    maps.put(Updatable.class.getName(), new ArrayList<Updatable>());
    maps.put(Removable.class.getName(), new ArrayList<Removable>());
    maps.put(Renderable.class.getName(), new ArrayList<Renderable>());
    maps.put(Collidable.class.getName(), new ArrayList<Collidable>());
}
public static void add(Object obj){
    maps.get(obj.getClass().getName()).add(obj);
}

只需使用重载:add(Updateable u), add(Removable r),

正如您在评论中提到的,您的用例有些特殊。 UpdatableRemovableRenderableCollidable 都是 接口 。您有 类 实现了这些接口中的一个或多个。最终,您希望 add 方法将一个对象添加到它实现的接口的每个列表中。 (如果有任何错误,请纠正我。)

如果重载过于冗长,您可以通过反射来实现,如下所示:

private List<Updatable> updatables;
private List<Removable> removables;
private List<Renderable> renderables;
private List<Collidable> collidables;

@SuppressWarnings("unchecked")
public void add(Object o) {
    try {
        for (Class c : o.getClass().getInterfaces()) {
            // Changes "Updatable" to "updatables", "Removable" to "removables", etc.
            String listName = c.getSimpleName().toLowerCase() + "s";

            // Adds o to the list named by listName.
            ((List) getClass().getDeclaredField(listName).get(this)).add(o);
        }
    } catch (IllegalAccessException e) {
        // TODO Handle it
    } catch (NoSuchFieldException e) {
        // TODO Handle it
    }
}

但是如果您打算使用这种方法,请注意以下注意事项:

  1. 反射本质上很容易出现 运行时间错误。
  2. 如果您的层次结构更复杂,您可能 运行 会遇到问题。例如,如果您有 Foo implements Updatable, RemovableBar extends Foo,那么在 Bar 上调用 getClass().getInterfaces() 将 return 一个空数组。如果这听起来像您的用例,您可能想改用 Apache Commons Lang 中的 ClassUtils.getAllInterfaces

我希望您对每个列表都有一个单独的 add 方法,如下所示:

public void addUpdatable(Updatable updatable) {
    updatables.add(updatable);
}

public void addRemovable(Removable removable) {
    removables.add(removable);
}

// and so on

这样做的原因是这些是单独的列表,虽然从实现的角度来看,关注的是 API 优雅和圆滑,但从用户的角度来看,这些努力会对清晰度产生负面影响。

例如,关于添加 Updatable 也是 Removable 时会发生什么的误解(在 EJP 的回答评论中)表明这种不明确。尽管您对您的类型和您的状态使用了相似的术语,这使得它看起来多余,但您仍然应该明确将项目添加到哪个列表(或状态的一部分)。

无论如何,看起来你正在努力解决更大范围的问题。我认为花更多的时间来理解更大的问题是个好主意。

来自@Jerry06,对我来说,将实例添加到多个不同类型列表的最佳方法是使用魔术列表 getter。

private Map<Class, List> maps = new HashMap<>();

'maps' 用于查找您要查找的列表。

private <T> List<T> get(Class<T> c) {
    return maps.get(c);
}

而不是使用 maps.get,我只是使用 get(SomeClass.class)。上面的函数自动将其转换为具有正确元素类型的列表。然后我能够做到:

    get(Updatable.class).stream().filter(Updatable::isAwake).forEach(Updatable::update);

而不是:

    updatables.stream().filter(Updatable::isAwake).forEach(Updatable::update);

我最想要的是这样的结构,这样所有的列表都可以很好地包装起来,其次我可以直接说 maps.add(SomeNewClass.class, new ArrayList()),然后调用它只要我想。