2016-12-28 15 views
4

私は現在、コードをリファクタリングしようとしていますが、その実装は拡張機能より優先されています。現在、シーンにオブジェクトを追加する関数を作成しようとしています。複数のリストに追加するためのJava "instanceof"

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); 
} 
:優れた各オブジェクトが何であるかを定義するためには、などの更新、レンダリングのためのリスト、

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

私はそうのような私のシーンクラスに機能を作りたいなど、複数のリストがあります

同様に私は同様の削除機能を作成します。私はinstanceofの落とし穴について聞いたことがあるので、これは悪いコーディングの練習であるかどうかです.2番目は、これを回避する方法がありますか、インスタンスを簡単に追加できるようにコードを再構築していますか?私はすべてのリストにインスタンスを追加する必要はなく、いつどのインスタンスに属するのかを定義することを好みます。

+2

これに関する同様の質問がたくさんあります。 [たとえば](https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=site:stackoverflow.com+java+instanceof+code+smell) –

答えて

1

私はオーバーロードを使用し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); 
} 
+0

それはクラス 'String'の代わりにクラス 'Class'を使用し、次に行うことができます。maps.put(Updatable.class、new ArrayList ());?私はこの方法を取り入れると思う。 –

+1

@MichaelChoiあなたはそれを試したこともなく、答えを受け入れるべきではありません。これは、これらのインターフェースの実装クラスを持っているのでNPEを投げるので、 'maps.get()'はnullを返します。また、私の答えに対するあなたのコメントのように、複数のインターフェースを実装しているクラスの問題も解決していません。 – EJP

+0

@EJP、OPは 'Updateable'がクラスまたはインタフェースであるとは言及していませんが、このコードはユースケースに依存して更新され、' maps.get'を使用するといくつかの否定的なケースでもっと必要とします。これはちょうど 'インスタンス'を確認するために 'else'を避けることを提案するアイデアの1つです – Jerry06

2

を使用して工場出荷時のパターンと類似しただろう:など

+1

これはJavaにとってより自然なアプローチのようです。 – Sudicode

+0

これは面白いです。私はこれがすべてのインターフェイスを呼び出すことは知らなかった。これは、更新可能と取り外し可能の両方を実装するオブジェクトを持っている場合、両方の関数が呼び出されることを意味しますか?これが最初に呼ばれる場合です。 –

+0

更新:この方法は残念なことに私のためには機能しません。オブジェクトがUpdatableとRemovableの両方である場合、オブジェクトを追加しようとすると "あいまいなメソッド呼び出し"が発生します。これはキャストすることで解決できるので、scene.add((Updatable)obj); scene.add((Removable)obj);それは理想的な解決策のようには見えません。 –

1

add(Updateable u), add(Removable r),をあなたはコメントで述べたように、あなたのユースケースはやや特殊です。 Updatable,Removable,RenderableおよびCollidableはすべてインターフェイスである。これらのインタフェースの1つ以上を実装するクラスがあります。最終的には、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がある場合、BargetClass().getInterfaces()を呼び出すと空の配列が返されます。これがあなたのユースケースのように思える場合は、代わりにApache Commons LangのClassUtils.getAllInterfacesを使用するとよいでしょう。
+0

ですが、これはFooがあってもBarは何も実装していないと考えられるからです。それとも、 ".getInterfaces()"関数が子クラス用のインタフェースを返さないということだけですか? –

+1

これは、 'getInterfaces()'は、implementsによって直接参照されるインタフェースだけを取得するためです。 Apacheの 'ClassUtils.getAllInterfaces(Class)'がそうするように、クラス階層をトラバースすることはありません。しかし、 'Bar extends FooはUpdatable、Removable'を実装しているので、Barクラスを重複して宣言すると、' getInterfaces() 'がそれらを選択します。 – Sudicode

0

私は何を期待することは、あなたがそうのように、これらのリストのそれぞれに対して別々のadd方法を持っているということです。

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

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

// and so on 
この理由は、これらは別々のリストであり、ということである

から心配ながら、実装の視点は、APIをエレガントで滑らかにすることです。これらの取り組みは、ユーザーの観点から明快に影響します。

たとえば、RemovableでもあるUpdatableが追加されたときに何が起こったのかについての誤解(EJPの回答コメント)が追加されたことは、この明快さの欠如を示しています。あなたのタイプとあなたの状態の両方に似た言葉を使って冗長であるように見せても、アイテムが追加されているリスト(または状態の一部)を明確にする必要があります。

いずれの場合でも、より大きな範囲で問題に苦しんでいるようです。より大きな問題を理解するために、より多くの時間を投資することは良い考えだと思います。

+0

問題を理解するのを手助けできますか?これは現在のデザインの問題ですか?私は現時点でゲームを作ろうとしています。各ゲームのループについて、私は特定のオブジェクトが特定の機能、すなわち更新、レンダリング、削除を実行することを望みます。これは問題の悪い解決策ですか?もしそうなら、代替手段があります。結局のところ、それぞれのリストを別々にすることを提案しているようですが、Playerのようなクラスはどのように更新可能で、レンダリング可能で、リムーバブルなのでしょうか? –

0

@ Jerry06より、私のために異なるタイプの複数のリストにインスタンスを追加する最善の方法は、魔法のリストゲッターを使用することでした。

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

「マップ」は、あなたが探しているリストを見つけるために使用されます。

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

私は主にこのようにすべてのリストをきれいに包ますることができ、そして第二に私はちょうどマップを言うことができるような構造を望んでいました。 add(SomeNewClass.class、new ArrayList())を呼び出すと、いつでも呼び出すことができます。

関連する問題