2016-12-28 3 views
4

현재 확장 기능보다 선호되는 코드를 읽은 후 코드를 리팩토링하려고합니다. 현재 Scene에 Object를 추가하는 함수를 만들려고합니다.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의 함정에 대해 들어 봤기 때문에 이것이 잘못된 코딩 사례인지, 그리고 둘째로 이것이 주위에 방법이 있는지/인스턴스를 간단하게 추가하기 위해 코드를 재구성하는 것인가? 모든 목록에 인스턴스를 추가 할 필요가 없으며 매번 인스턴스에 속한 인스턴스를 정의해야합니다. 당신이 코멘트에서 언급 한 바와 같이 add(Updateable u), add(Removable r),

+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가 throw되므로'maps.get()'은 null을 반환합니다. 그것은 또한 내 대답에 대한 귀하의 의견 당 하나 이상의 인터페이스를 구현하는 클래스의 문제를 해결하지 않습니다. – EJP

+0

@EJP, 영업 이익은 언급하지 않는'Updateable'이 클래스 또는 인터페이스, 업데이트됩니다이 코드는 사용 사례에 의존하고'maps.get'를 사용할 때 몇 가지 부정적인 경우에 더 많은 작업을 수행해야합니다. 이것은 단지'그는 이후를 언급했다 Jerry06 @ – Jerry06

2

를 사용하여 공장 패턴과 유사 할 것입니다 그냥 오버로드를 사용합니다. Updatable, Removable, RenderableCollidable은 모두 인터페이스입니다. 이러한 인터페이스 중 하나 이상을 구현하는 클래스가 있습니다. 궁극적으로, 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()을 호출하면 빈 배열이 반환됩니다. 유스 케이스처럼 들리는 경우 Apache Commons Lang의 ClassUtils.getAllInterfaces을 대신 사용할 수 있습니다.
+1

이것은 Java에 대한보다 자연스러운 접근 방식처럼 보입니다. – Sudicode

+0

아 참 재미 있습니다. 나는 이것이 모든 인터페이스를 호출한다는 것을 몰랐다. 즉, 업데이트 가능하고 제거 가능한 객체를 모두 가지고 있다면 두 함수 모두 호출됩니다. 이것이 첫 번째로 불리는 경우입니다. –

+0

업데이트 :이 방법은 불행히도 나를 위해 작동하지 않습니다. 객체가 Updatable과 Removable 둘 다일 때 객체를 추가하려고하면 "모호한 메소드 호출"이 발생합니다. 이것은 캐스팅으로 해결할 수 있으므로 scene.add ((Updatable) obj); scene.add ((Removable) obj); 그러나 그것은 이상적인 해결책처럼 보이지 않습니다. –

1

, 사용 사례가 다소 특별하다 :

+0

은 Foo가 있어도 Bar가 아무 것도 구현하지 않는 것으로 간주되기 때문에입니까? 또는 ".getInterfaces()"함수가 자식 클래스에 대한 인터페이스를 반환하지 않는다는 것입니까? –

+1

'getInterfaces()'는 implements가 직접 참조하는 인터페이스 만 가져 오기 때문입니다. 아파치의'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를 우아하고 매끈하게 만드는 것입니다. 이러한 노력은 사용자 관점에서 명확성에 부정적인 영향을 미칩니다.

예를 들어, RemovableUpdatable이 추가 될 때 일어나는 일에 대한 오해 (EJP의 답변 의견에서)가 명확하지 않다는 것을 보여줍니다. 유형과 주 모두에 대해 유사한 용어를 사용하여 중복되는 것처럼 보이더라도 항목을 추가 할 목록 (또는 상태의 일부)을 분명히해야합니다.

어쨌든 더 큰 범위의 문제로 어려움을 겪고있는 것처럼 보입니다.더 큰 문제를 이해하는 데 더 많은 시간을 투자하는 것이 좋은 생각이라고 생각합니다. Jerry06, 나에게 마법 목록 게터를 사용하고 서로 다른 종류의 여러 목록에 인스턴스를 추가하는 가장 좋은 방법 @에서

+0

문제를 이해하는 데 도움을 주시겠습니까? 이것이 현재 디자인의 문제점입니까? 나는 지금 게임을 만들려고 노력하고있다. 각 게임 루프마다 특정 오브젝트가 특정 기능 (예 : 업데이트, 렌더링, 제거)을 수행하기를 원합니다. 이것은 문제의 나쁜 해결책입니까? 그렇다면 대안이 있습니다. 결국 각 목록이 분리되어야한다고 제안하는 것처럼 보이지만 Player와 같은 클래스는 어떻게 업데이트 가능하고 렌더링 가능하며 제거 가능합니까? –

0

.

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())를 호출 한 다음 원할 때마다 호출하십시오. 이 비슷한 질문