동일한 서명을 가진 두 개의 인터페이스


13

카드에 두 가지 중요한 기능 세트가있는 카드 게임을 모델링하려고합니다.

첫 번째는 효과입니다. 카드를 플레이 할 때 발생하는 게임 상태의 변경 사항입니다. 효과의 인터페이스는 다음과 같습니다.

boolean isPlayable(Player p, GameState gs);
void play(Player p, GameState gs);

그리고 비용을 충족 할 수 있고 모든 효과를 재생할 수있는 경우에만 카드를 재생할 수있는 것으로 간주 할 수 있습니다. 이렇게 :

// in Card class
boolean isPlayable(Player p, GameState gs) {
    if(p.resource < this.cost) return false;
    for(Effect e : this.effects) {
        if(!e.isPlayable(p,gs)) return false;
    }
    return true;
}

자, 지금까지는 매우 간단합니다.

카드의 다른 기능은 능력입니다. 이러한 능력은 게임 상태를 변경하여 원하는대로 활성화 할 수 있습니다. 이것에 대한 인터페이스를 생각해 냈을 때, 활성화 할 수 있는지 여부를 결정하는 방법과 활성화를 구현하는 방법이 필요하다는 것을 깨달았습니다. 결국

boolean isActivatable(Player p, GameState gs);
void activate(Player p, GameState gs);

그리고 "play"대신 "activate"라고 부르는 것을 제외 Ability하고 Effect는 정확히 같은 서명을 가지고 있다는 것을 알고 있습니다.


서명이 동일한 여러 개의 인터페이스를 갖는 것은 좋지 않습니까? 간단히 하나만 사용해야하고 동일한 인터페이스의 두 세트가 있어야합니까? 따라서 :

Set<Effect> effects;
Set<Effect> abilities;

그렇다면, 더 많은 기능이 출시됨에 따라 동일하지 않은 경우, 특히 분기가 다른 경우 (즉, 둘 중 하나만 얻는 것과는 반대로 다른 쪽에서는하지 말아야 할 것을 얻는 경우) 리팩토링 단계를 진행해야합니다. 다른 하나는 완전한 하위 집합입니다)? 특히 무언가를 변경하자마자 그것들을 결합하는 것이 지속될 수 없다고 우려하고 있습니다.

작은 글씨 :

나는이 질문이 게임 개발에 의해 생긴다는 것을 알고 있지만, 게임이 아닌 개발에서 쉽게 발생할 수있는 일종의 문제라고 생각한다. 하나 이상의 비즈니스 영향으로 수행 한 모든 프로젝트 ... 또한 사용 된 스 니펫은 Java 스 니펫이지만, 이는 여러 객체 지향 언어에도 쉽게 적용될 수 있습니다.


KISSYAGNI를 따르십시오 . 그러면 괜찮을 것입니다.
Bernard

2
이 질문이 나타나는 유일한 이유는 엄청나게 넓고 제약이없는 Player 및 GameState 매개 변수로 인해 함수가 너무 많은 상태에 액세스 할 수 있기 때문입니다.
Lars Viklund 5

답변:


18

두 인터페이스가 동일한 계약을 가지고 있다고해서 동일한 인터페이스라는 의미는 아닙니다.

Liskov 대체 원칙 상태 :

q (x)를 T 유형의 객체 x에 대해 가능한 속성으로 설정합니다. 그런 다음 S (T)의 하위 유형 인 S에 대한 객체 y에 대해 q (y)를 제공 할 수 있어야합니다.

즉, 인터페이스 또는 수퍼 타입에 해당되는 것은 모든 서브 타입에 해당되어야합니다.

내가 당신의 설명을 올바르게 이해한다면, 능력은 효과가 아니며 효과는 능력이 아닙니다. 어느 쪽이 계약을 변경하더라도 다른 쪽이 계약을 변경하지는 않습니다. 그것들을 서로 묶어 놓을 이유가 없습니다.


2

에서 위키 백과 : " 인터페이스는 종종 데이터가없는 추상적 인 유형을 정의하는 데 사용하지만, 노출하는 행위는 방법으로 정의 ". 제 생각에는 인터페이스가 동작을 설명하는 데 사용되므로 다른 동작이 있으면 다른 인터페이스를 갖는 것이 좋습니다. 귀하의 질문에 대한 인상을 읽으면 다른 행동에 대해 이야기하고 있으므로 다른 인터페이스가 최선의 방법 일 수 있습니다.

자신이 말한 또 다른 요점은 그러한 행동 중 하나가 바뀌면 말입니다. 인터페이스가 하나만 있으면 어떻게됩니까?


1

카드 게임의 규칙이 "효과"와 "능력"을 구별하는 경우, 서로 다른 인터페이스인지 확인해야합니다. 이렇게하면 다른 하나가 필요한 경우 실수로 하나를 사용하지 못하게됩니다.

즉, 그것들이 매우 유사하다면 공통 조상에서 파생시키는 것이 합리적 일 수 있습니다. 주의 깊게 고려하십시오 : 당신은 항상 것 "효과"와 "능력"을 믿을만한 이유가 않습니다 반드시 동일한 인터페이스를 가지고 있습니까? 당신이에 요소를 추가 할 경우 effect인터페이스, 당신은 추가 같은 요소가 필요 기대 ability인터페이스를?

그렇다면 이러한 요소를 feature모두 파생 된 공통 인터페이스 에 배치 할 수 있습니다 . 그렇지 않다면 통합하지 말아야합니다. 기본 인터페이스와 파생 된 인터페이스 사이를 이동하는 데 시간을 낭비하게됩니다. 그러나 실제로는 "반복하지 마십시오"라는 공통의 기본 인터페이스를 사용하지 않기 때문에 큰 차이가 없을 수 있습니다. 그리고 당신이 그 의도를 고수한다면, 처음에 잘못된 선택을하면 나중에 고치기 위해 리팩토링하는 것이 비교적 간단 할 것입니다.


0

당신이보고있는 것은 기본적으로 유형 시스템의 제한된 표현의 인공물입니다.

이론적으로 유형 시스템에서 두 인터페이스의 동작을 정확하게 지정할 수 있으면 동작이 다르기 때문에 서명이 달라집니다. 실제로, 유형 시스템의 표현력은 Halting Problem 및 Rice Theorem과 같은 근본적인 한계에 의해 제한되므로 모든 행동 양상을 표현할 수는 없습니다.

이제 다른 종류의 시스템은 표현의도 다른,하지만 항상있을 것입니다 뭔가 표현할 수없는.

예를 들어, 오류 동작이 다른 두 개의 인터페이스는 C #에서 서명이 동일하지만 Java에서는 그렇지 않습니다 (Java 예외에서는 서명의 일부이므로). 부작용 만 다른 동작을하는 두 개의 인터페이스는 Java에서 동일한 서명을 가질 수 있지만 Haskell에서는 다른 서명을 갖습니다.

따라서 다른 행동에 대해 동일한 서명으로 끝날 가능성이 항상 있습니다. 명목상의 수준 이상으로이 두 행동을 구별하는 것이 중요하다고 생각되면,보다 (또는 다른) 표현형 시스템으로 전환해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.