2011-04-09 4 views
4

나는 블랙 잭 게임에서 사용할 Card 클래스를 디자인하고 있었다.모든 불변 클래스가 최종해야합니까?

제 디자인은 J에 대해 11, Q에 대해 12, K에 대해 13을 반환하는 getValue()를 사용하여 Card 클래스를 만든 다음 BlackjackCard 클래스로 확장하여 해당 메서드를 재정 의하여 카드는 10을 반환합니다.

그런 다음 뭔가 잘못되었습니다. Card 클래스의 객체는 불변이어야합니다. 그래서 나는 Effective Java 2nd Edition을 다시 읽음으로써 무엇을해야 하는지를 알게되었고, 불변 클래스가 최종성을 가져야 만 불변성을 깨뜨리는 것을 피할 수 있었다.

나는 또한 인터넷에서 보았고 모두가 그 점에서 동의하는 것으로 보인다.

그럼 카드 클래스가 최종해야합니까? 이 클래스의 불변성을 깰 수있는 방법

은, 그것을 확장 할 수 :

class Card { 
    private final Rank rank; 
    private final Suit suit; 
    public Card(Rank rank, Suit suit) { 
    this.rank = rank; 
    this.suit = suit; 
    } 
    public Rank getRank() { 
    return rank; 
    } 
    public Suit getSuit() { 
    return suit; 
    } 
    public int getValue() { 
    return rank.getValue(); 
    } 
} 

감사합니다.

답변

1

불변 클래스는 final이어야한다고 말하면 무언가가 변경 불가능하므로 무결성을 보장 할 수있는 방법을 말합니다. 그것의 작은 구별. 학급 확장을 원하지 않는다면 최종적이어야합니다.

2

당신은이 작업을 수행 할 수 있습니다

class MyCard extends Card { 

    public MyCard(Rank rank, Suit suit) { 
    super(rank, suit); 
    } 

    @Override 
    public Rank getRank() { 
    // return whatever Rank you want 
    return null; 
    } 

    @Override 
    public Suit getSuit() { 
    // return whatever Suit you want 
    return null; 
    } 

    @Override 
    public int getValue() { 
    // return whatever value you want 
    return 4711; 
    } 

}

연재 클래스도 부모 클래스와 같은 생성자를 선언 할 필요가 없습니다. 기본 생성자를 가질 수 있으며 부모 클래스의 최종 구성원에 대해서는 아무 것도 신경 쓰지 않습니다. [그 진술은 잘못되었습니다 - 주석 참조.

+1

'MyCard' 클래스가 컴파일되지 않습니다. '계급'과'소송 '에 접근 할 수 없다. –

+0

마지막 문장이 참이 아닙니다. 최종 멤버 변수를 설정해야합니다. 또한 예제가 컴파일되지 않을 것이므로'this.rank'와'this.suit'를 재설정 할 수 없습니다. 나머지 부분은 여전히 ​​합법적입니다. – jtahlborn

+0

@lwburk & @jtahlborn 당신은 맞습니다 - 최종 회원은 재설정 할 수 없습니다 - 빨리 입력하십시오! – FrVaBe

5

서브 클래스는 실제로 부모의 private final 속성 값을 수정할 수 있지만, 무엇 Effective Java warns against입니다시킨 것처럼이 행동 수 :

이 클래스를 확장 할 수 없습니다 있는지 확인 . 하위 클래스가 클래스의 변경 불가능한 동작을 손상시키지 않도록합니다 (예 : ). 개체의 상태가 인 것처럼 동작합니다.

+0

+1 관련 점은 클래스 또는 그 메소드를 확장 할 수 없다는 것입니다. 클래스를 final로 선언하는 것은 여러 가지 방법 중 하나 일뿐입니다. – Grundlefleck

2

답변은 예입니다. 카드는 최종해야합니다.

public class MyCard extends Card { 
    private Rank myRank; 
    private Suit mySuit; 

    public MyCard(Rank rank, Suit suit) { 
     this.myRank = rank; 
     this.mySuit = suit; 
    } 

    @Override public Rank getRank() { return myRank; } 

    public void setRank(Rank rank) { this.myRank = rank; } 

    @Override public Suit getSuit() { return mySuit; } 

    public void setSuit(Suit suit) { this.mySuit = suit; } 

    @Override public int getValue() { return myRank.getValue(); } 
} 

이 확장은 완전히 부모의 상태를 무시하고 자신의, 변경 가능한 상태로 바꿉니다 : K. Claszen 및 lwburk의 응답을 결합

은 다음을 참조하십시오. 이제 다형성 컨텍스트에서 Card를 사용하는 클래스는 불변 인 것에 의존 할 수 없습니다.

+0

그러면 블랙 잭 카드를 어떻게 코딩 할 수 있습니까? – gaijinco

+0

카드를 마지막으로 만들면 좋을 것입니다 ... –

1

일반적으로 이는 권장 사항입니다. 그러나 모든 코드를 제어하는 ​​경우 불변 클래스를 확장 할 수있는 경우가 있습니다 (추가 정보가있는 다른 불변 클래스를 만들 수 있음). 대부분의 권장 사항과 마찬가지로, 때로는 이해할 수있는 시점과 그렇지 않은 시점에 대해 현명한 선택을해야합니다.

1

임의의 코드가 불변 클래스를 확장 할 수있는 경우, 임의의 코드는 불변 클래스와 같이 많이 동작하지만 불변은 아닌 객체를 생성 할 수 있습니다. 그러한 코드를 작성하는 사람이 스스로를 손상시킬 수 없으면 그러한 상황은 용인 될 수 있습니다. 누군가가 그러한 클래스를 사용하여 보안 조치를 우회 할 수 있다면 허용되지 않아야합니다.

확장 가능한 클래스를 갖는 것이 유용 할 수 있지만 모든 파생 클래스를 대신하여 불변을 약속한다는 점에 유의하십시오. 물론 그러한 클래스와 소비자는 클래스의 상속자에게 이상하고 이상한 행동을하지 말아야하지만, 때로는 이러한 접근 방식이 어떤 대안보다 더 나을 수도 있습니다. 예를 들어, 특정 시간에 어떤 동작을 수행해야하는 클래스를 가질 수 있지만, 클래스의 객체는 임의로 앨리어스 될 수 있습니다. 그러한 클래스는 클래스의 정의가 어떤 유형의 액션이 될 수 있는지를 제한 할 것이기 때문에 클래스 나 필드, 필드 나 파생 클래스의 필드가 파생 된 타입을 사용할 수없는 경우에는별로 유용하지 않습니다. 공연. 가장 좋은 접근법은 클래스가 최종 선언되지 않도록하는 것이지만, 변경 가능한 하위 클래스의 동작이 일관성이 없거나 예측 가능하지 않다는 것을 문서에서 명확하게 밝혀야합니다.

1

당신은 게임에 묶여 Valuator이 있고 클래스의 getValue,이 방법으로 제거 할 수 Cardfinal 될 수 있습니다

final class Card { 
    private final Rank rank; 
    private final Suit suit; 
    public Card(Rank rank, Suit suit) { 
    this.rank = rank; 
    this.suit = suit; 
    } 
    public Rank getRank() { 
    return rank; 
    } 
    public Suit getSuit() { 
    return suit; 
    } 
} 

그리고 Valuators만을은 다음과 같이 작동 :

interface CardValuator { 
    int getValue(Card card); 
} 

class StandardValuator implements CardValuator { 
    @Override 
    public int getValue(Card card) { 
    return card.getRank().getValue(); 
    } 
} 

class BlackjackValuator implements CardValuator { 
    @Override 
    public int getValue(Card card) { 
    ... 
    } 
} 

지금 Card 계층 구조를 계속 유지하려면 Card방법을으로 표시하십시오.은 하위 클래스가이를 재정의하는 것을 방지합니다.

관련 문제