2011-01-12 11 views
1

자바 리플렉션을 과도하게 사용하고 있는지 궁금합니다.
두 개의지도에 대한 데이터 보유자 인 클래스가 있습니다. 입력으로 키를 제공하는 공개 get (...) 메소드를 사용하여 해당 맵에 연관된 값을 리턴합니다.
지도가 크기 때문에 실제로 액세스하려는 경우에만지도를로드합니다. 그래서 모든 get (...) 메소드에서 맵이 null인지 확인합니다. 일치하는 경우 해당하는 loadMap (..) 메서드를 호출합니다.
다음은 샘플 코드는 문제는 내가 여러지도를 가지고있다자바 : 반사 과용?

public getId(String name) 
{ 
    try 
    { 
    if(nameMap1 == null) 
     loadNameMap1(); 
    } catch(...) {....} 

    return nameMap1.getId(name); 
} 

스 니펫입니다. 그래서 각 맵을로드하기 위해 getMap (..) 메소드와 get (...) 메소드에서 try catch 블록을 가지고있다. 그래서 대신에 리플렉션을 사용하여 적절한 메소드를 호출하고 모든 예외를 처리하는 loadMap (Object map, String methodName)이라는 메소드를 작성했습니다.

private synchronized void loadMap(Object map, String methodName) 
{ 
if (map == null) 
    try 
    { 
    Method method = this.getClass().getDeclaredMethod(methodName, new Class[0]); 
    method.invoke(this, new Object[0]); 
    } 
    catch (..) 
} 

여기 반사를 과용합니까? 이 작업을 수행하는 더 좋은 방법이 있습니까? (사이드 노트 : 나는 여러 클래스로 클래스를 리팩토링 할 수 없음)이 Effective Java by Joshua Bloch
에 작성된 "반사의 사용이 제한"으로 자격이 있습니까 내가 생각

+0

"클래스를 여러 클래스로 리팩터링 할 수 없습니다." 왜 그런데? 현재 인터페이스를 외부에 유지할 수 있지만 구현은 일부 MapLoader 인터페이스를 사용하여 리팩터링 될 수 있습니다. – Thilo

+0

@Thilo : 디자인을 제어 할 권한이 없습니다. – athena

+0

디자인에 대한 통제권이 없다면 어떻게 리플렉션을 사용하기로 결정할 수 있습니까? 확실히 사내 내부 클래스 (구현 세부 정보)를 추가하면 사물 디자인과 충돌하지 않습니다. – Thilo

답변

3
// could also be static 
private Map<String, Callable<Map>> myLoaders; 

private synchronized void loadMap(Object map, String mapName) 
{ 
if (map == null) 
    try 
    { 
     Callable<Map> mapLoader = myLoaders.get(mapName); 
     map = mapLoader.call(); 
    } 
    catch (..) 
} 

// and in the constructor or other init code 
myLoaders.put("map1", new Callable<Map>(){ 
    Map call(){ 
     // load map 1 
    }}); 

것은, 그것이 당신이하고있는 모든 경우 비록 일반적인 이동 try/catch 로직을 두 곳에서 반복해야 할 필요가있는 경우, 이는 잘못된 접근 방법입니다. 이 방법으로 많은 컴파일러 오류 검사를 잃게됩니다. 어떤 사람들은 Aspect/J와 같은 도구를 사용할 것입니다. 그러나 자바가 실제 기능이 없다는 사실과 공유 된 개인 기능을 사용하여 최소한의 혼란을 줄이고 몇 가지 복사/붙여 넣기. 이 행에 "실제 코드"가없는 한 실제로 코드 중복이 아닙니다. 그래서

:

public getId(String name){ 
    try{ 
     if (nameMap1 == null) 
      loadNameMap1(); 
     } 
     catch (....){ 
      privateHelperFunctionThatCutsThisDownToOneLine(name, "id", "nameMap1"); 
     } 
    } 

    // you are left with the above repetitive three (or seven) lines, 
    // but that is Java for you... 
    // in return, you get nice, static compile-time error checking 


private void privateHelperFunctionThatCutsThisDownToOneLine(){ 
     // all the long repeated code in the exception handler 
     // goes here. 
} 
+0

고마워요. 솔루션을 구현했습니다. 그냥 호기심에서 나온 것인데, 어떤 종류의 패턴입니까? – athena

+0

Perl에서는이를 "디스패치 테이블"이라고 부릅니다. 자바에서는 아마도 나쁜 디자인 일 것입니다 (런타임에 구성/확장 가능하다면, 그렇지 않다면 말이됩니다). – Thilo

+0

왜 나쁜 디자인입니까? 어떻게 개선 될 수 있습니까? (아마도 디자인을 변경할 수는 없지만 여전히 잘 알 수 있습니다.) – athena

2

나는 네 당신이 반사를 남용하고 있습니다 말하고 싶지만.

아마도 당신은 더 OO 접근 방식이 너무 크기 때문에 당신은 모든지도를로드하지 않으

public interface MapMaker <K,V> { 
public Map<K,V> create(); 
} 

public class LazyMap<K,V> implements Map<K,V> { 

private MapMaker<K,V> creation; 
private Map<K,V> theMap = null; 

public LazyMap(MapMaker<K,V> creation) { 
    this.creation=creation; 
} 

protected Map<K,V> getMap() { 
    if(theMap == null) { 
    synchronized(this) { 
     if(theMap == null) { 
     theMap = creation.create(); 
     } 
    } 
    } 
    return theMap; 
} 
//Map interface 
public V get(Object key) { return getMap().get(key); } 
//repeat for all 
} 
+1

좋은 답변이지만'getMap()'에 * double check locking * 패턴을 구현하고 있으며이 패턴은 [developerworks] (http://www.ibm.com/developerworks/java/)에서 설명한대로 실패하는 것으로 알려져 있습니다. library/j-dcl.html) – gabuzo

+0

알아요. 자바에서 휘발성이있는 AFAIK는 적절한 방법이 아닙니다. – KitsuneYMG

+0

필자는 주제를 찾아 보았고 자바 5에는 새로운 [메모리 모델] (http://xlct.it/faz2fl)이 있기 때문에 이중 검사 잠금은 Java 5 이상의 JVM에서 작동해야합니다. 또한 퇴거 전략이 필요할 수도 있음을 언급 한이 기사의 끝 부분 (http://xlct.it/ekzRnO) – gabuzo

2

을해야합니다. 그러나 당신의 방법을 사용하면 결국 메모리에로드 된 모든 것을 끝낼 것입니다. 더 이상 필요하지 않은 경우 요소 이탈을 사용하여 지연지도 시스템을 구성 할 수있는 ehcache을 살펴볼 수 있습니다.

+0

+1을 참조하십시오. – Thilo