2014-05-22 3 views
2

현재 저는 3 개의 사용자 클래스가있는 프로젝트에서 작업 중이며, UserA, UserB, UserC는 추상 사용자 클래스를 상속받습니다.Java 다운 캐스트를 피하려면 어떻게해야합니까?

프로그램은 사용자가 로그인하고 로그 아웃해야한다는 시스템 소원을 에뮬레이트합니다.

해당 사용자의 인스턴스는 3 개의 별도 목록에 저장됩니다.

언제든지 한 사용자 만 "기록"되어 있으므로 User currentUser 변수가 있습니다.

UserA, B 또는 C 고유의 메서드를 호출하려면 캐스팅을 수행해야합니다. 코드를 우아하게 유지하면서 그 캐스트를 수행하지 않으려면 어떻게해야합니까?

코드 예 :

public abstract class User { 
} 

public class UserA extends User { 
    public void clearAgenda(); 
} 

public class UserB extends User { 
} 

public class UserC extends User { 
} 

public class System { 
    private User loggedUser; 

    public void clearUserAgenda() { 
     ((UserA) loggedUser).clearAgenda(); 
    } 
} 

참고 : 내가 알고있는 작업이 사용되는 경우, 현재 사용자가 작업을 지원하는 한편 전. (예 : System.clearUserAgenda()가 호출 된 다음 사용자가 UserA의 인스턴스 인 경우)

+0

왜 3 개의 별도 목록이 필요합니까? –

+1

'User currentUser'를 가지고 있지만 나중에 메소드를 호출하기를 원하고 **이 'currentUser'가 어떤 사용자인지를 알기에 다소 모호한 것 같습니다. 'currentUser'는 어떻게 저장됩니까?그리고'clearUserAgenda' (또는 특정 유형의 사용자가'currentUser' 일 때만 ** ** 호출 될 수있는 다른 메소드)와 같은 메소드를 호출 할 책임이있는 ** 책임자는 누구입니까? 즉, 발신자 **는 현재 어떤 유형의 사용자가 로그인했는지를 어떻게 알 수 있습니까? – Marco13

+0

그리고 모델을 작성하려는 유스 케이스는 무엇입니까? –

답변

3

다형성이 명확합니다. User에서 (Baalthasarr가 말한 것처럼) 메소드를 선언하고 필요에 따라 하위 유형 (UserA, UserB 등)에서 메소드를 대체 할 수 있습니다. 그런 다음 loggedUser.clearAgenda()을 사용하면 JRE는 런타임에 자동으로 사용할 메소드를 파악합니다.

public abstract class User { 
    public void clearAgenda() {} // do nothing (or or something if you want) 
} 

public class UserA extends User { 
    @Override public void clearAgenda() { ... } // do stuff specific to A 
} 

public class UserB extends User { 
    @Override public void clearAgenda() { ... } // do stuff specific to B 
} 

public class UserC extends User { 
} 

public class System { 
    private User loggedUser; 

    public void clearUserAgenda() { 
     // This will use the most appropriate method. If the type is 
     // UserA, UserA's `clearAgenda` method will be used. Similar 
     // for UserB. For UserC, The abstract User class's `clearAgenda` 
     // method is used, because it was not overridden in UserC. 
     loggedUser.clearAgenda(); 
    } 
} 

당신이 모든 사용자 서브 클래스에 공통의하고 싶은 물건이 있다면, 당신은 사용자에 넣을 수 있으며,이 메소드를 오버라이드 (override)하는 경우에도 여전히 실행합니다. 예 :

public class UserA extends User { 
    @Override public void clearAgenda() { 
     super.clearAgenda(); // do the stuff common to all User subclasses 
     ... // do stuff specific to A 
    } 
} 
+0

User.clearAgenda() 메소드는 호출 된 경우 (이론적으로 절대로 호출해서는 안됨)에 예외를 발생시켜야합니까? 예 : UserC.clearAgenda()가 – user2009400

+0

이라면 호출 할 수 있지만 필요하지는 않습니다 (런타임에 예외를 지연시키지 않을 것입니다). 모든 서브 클래스가 자신의 메소드 버전을 선언하도록하려면, 앞에서 언급 한 것처럼이 구문을 사용하십시오 :'public abstract void clearAgenda();'. –

4

캐스트를 피하려면 추상 클래스 사용자로 clearAgenda() 메서드를 선언해야합니다. 그렇게하지 않으면 UserA만이 메서드를 사용할 수 있으므로 캐스팅해야합니다. 거의

+0

그러나 이러한 작업이 많이 호출되는 시스템에서 이러한 캐스트를 수행하면 성능에 엄청난 영향을 미칩니 까? – user2009400

+0

@ user2009400 아니요. – bmargulies

+1

글쎄 엄청난 여기에 큰 단어입니다. 하지만 네, 성능에 영향을 미칠 것입니다. 그러나 이것이 추상 클래스 User에서 소개한다면 왜 당신은 다운 캐스팅을 방지 할 수 있습니다. 구현하고 싶지 않은 User 클래스에 'empty'메소드를 남겨 둘 수 있습니다. –

3

두 가지 옵션이 있습니다

  1. 사용 반사하지만 거의 "깨끗한 코드를 유지"요구 사항을합니다.
  2. 모든 사용자가 공통 인터페이스 (예 : UserclearAgenda()을 입력하고 기본 빈 구현을 제공하고 UserA에서 우선 적용)을 준수하도록합니다. 모든 방법에 대해 동일한 작업을 수행하십시오.

나는 2를 추천합니다. System의 발신자에게 자신이 어떤 종류의 사용자인지 알 수있는 책임을 부여하기 때문에이 수수께끼에 빠져 있습니다. 그런 다음 System은 호출자가 옳다고 가정하고 (실제 세계에서는 실제로 나쁜 가정 임) 메소드를 맹목적으로 호출합니다. System을 알 수 없다면 괜찮습니다.하지만 잘못된 사용자를 잘못된 방법으로 호출하는 사용자를 처리 할 수 ​​있어야합니다. 이를 위해서는 UserBclearAgenda()으로 전화하는 것이 좋습니다. 수퍼 클래스 메소드에서는 메소드가 호출되었고 오버라이드되지 않았 음을 기록 할 수 있습니다.이 방법을 사용하면 캐스트 예외로 모든 것을 손상시키지 않고 문제점을 감지 할 수 있습니다.

관련 문제