다형성의 맥락에서 하위 유형에 추가 된 메소드를 처리하는 방법은 무엇입니까?


14

다형성의 개념을 사용할 때 클래스 계층 구조를 작성하고 부모 참조를 사용하면 어떤 특정 유형에 객체가 있는지 몰라도 인터페이스 함수를 호출합니다. 대단하다. 예:

당신은 동물의 컬렉션을 가지고 있으며 모든 동물 기능을 호출하고 eat그것이 개를 먹는 것인지 고양이인지는 신경 쓰지 않습니다. 상속 클래스에서 구현 다른 것보다 -하지만 같은 클래스 계층 구조에서 당신은 추가로이 동물이 Animal예를 들어 makeEggs, getBackFromTheFreezedState등등을. 따라서 어떤 경우에는 기능을 수행하여 추가 동작을 호출하는 특정 유형을 알고 싶을 수도 있습니다.

예를 들어, 경우에 아침 시간과 그 다음 당신이 전화를 그냥 동물의 경우 eat는 인간이고, 그렇지 않은 경우, 먼저 전화를 washHands, getDressed단지 다음 전화 eat. 이 경우를 처리하는 방법? 다형성이 죽습니다. 코드 냄새처럼 들리는 객체의 유형을 찾아야합니다. 이 경우를 처리하는 일반적인 방법이 있습니까?


7
당신이 설명 다형성의 유형이라고 다형성을 하위 유형 ,하지만 (볼 수있는 유일한 종류 아니다 다형성을 ). 다형성을 수행하기 위해 클래스 계층 구조를 만들 필요는 없습니다 (실제로 상속이 다형성 다형성을 달성하는 가장 일반적인 방법은 아니며 인터페이스를 구현하는 것이 훨씬 보편적이라고 주장합니다).
Vincent Savard

24
당신이 정의하는 경우 Eater와 인터페이스 eat()방법을, 다음 클라이언트로, 당신은 것을 걱정하지 않는다 Human구현 처음 호출이 washHands()하고 getDressed()는이 클래스의 구현 세부 사항을이다. 클라이언트로서이 사실에 관심이 있다면, 해당 작업에 올바른 도구를 사용하지 않을 가능성이 높습니다.
Vincent Savard

3
또한 아침에는 인간이 점심을 먹기 getDressed전에 미리 해야 할 수도 있다는 점을 고려해야합니다 eat. 상황에 따라 washHands();if !dressed then getDressed();[code to actually eat]사람에게이를 구현하는 가장 좋은 방법 일 수 있습니다. 다른 가능성은 다른 것들이 그것을 필요로 washHands하거나 getDressed부르면 어떻게 될까요? 당신이 있다고 가정 leaveForWork합니까? 어쨌든 오래 전에 호출되도록 프로그램 흐름을 구성해야 할 수도 있습니다.
던컨 X 심슨

1
정확한 유형을 검사하는 것은 OOP에서 코드 냄새 일 수 있지만 FP에서는 매우 일반적인 관행입니다 (즉, 패턴 일치를 사용하여 차별 조합의 유형을 판별 한 후 조치).
Theodoros Chatzigiannakis

3
동물과 같은 OO 계층의 학교 방 예를 조심하십시오. 실제 프로그램에는 그러한 분류법이 거의 없습니다. 예 : ericlippert.com/2015/04/27/wizards-and-warriors-part-one . 또는 전체 돼지를 파고 전체 패러다임에 의문을 제기하려면 Object-Oriented Programming is Bad 입니다.
jpmc26

답변:


18

다릅니다. 불행히도 일반적인 해결책은 없습니다. 요구 사항에 대해 생각하고 이러한 작업을 수행해야하는 것을 파악하십시오.

예를 들어, 당신은 아침에 다른 동물들이 다른 일을한다고 말했습니다. 방법에 대해 당신은 방법 소개 getUp()prepareForDay()그런이나 뭐. 그런 다음 다형성을 계속하고 각 동물이 아침 루틴을 실행할 수 있습니다.

동물을 구별하려면 목록에 무차별 적으로 저장해서는 안됩니다.

다른 방법이 없다면 Visitor Pattern을 시도해 볼 수 있습니다.이 패턴일종의 역동적 파견을 허용하여 동물로부터 정확한 콜백을받을 방문자를 제출할 수 있습니다. 그러나 다른 모든 것이 실패하면 이것이 최후의 수단이어야한다고 강조합니다.


33

이것은 좋은 질문이며 OO를 사용하는 방법을 이해하려고 할 때 많은 사람들이 어려움을 겪습니다. 나는 대부분의 개발자들이 이것으로 어려움을 겪고 있다고 생각합니다. 나는 그것을 지나칠 수 있다고 말할 수 있기를 바란다. 그러나 나는 그것이 확실하지 않다. 내 경험상 대부분의 개발자는 pseudo-OO 속성 백을 사용 합니다.

먼저, 명확하게하겠습니다. 이것은 당신의 잘못이 아닙니다. OO가 일반적으로 가르치는 방식에는 결함이 있습니다. 그 Animal예는 최고의 범죄자 인 IMO입니다. 기본적으로 우리는 객체에 대해 이야기 할 수 있습니다. 는 Animal할 수 eat()그것은 할 수 있습니다 speak(). 감독자. 이제 몇 가지 동물을 만들고 그들이 먹고 말하는 방식을 코딩하십시오. 이제 당신은 OO를 알고 있습니까?

문제는 이것이 잘못된 방향에서 OO로오고 있다는 것입니다. 이 프로그램에 동물이 왜 있고 왜 말하고 먹어야합니까?

Animal유형 에 대한 실제 사용을 생각하기가 어렵습니다 . 나는 그것이 존재한다고 확신하지만 트래픽 시뮬레이션과 같이 추론하기가 더 쉬운 것에 대해 논의 해 보자. 다양한 시나리오에서 트래픽을 모델링하려고한다고 가정합니다. 여기에 필요한 몇 가지 기본 사항이 있습니다.

Vehicle
Road
Signal

우리는 모든 종류의 보행자와 기차로 더 깊게 갈 수 있지만 단순하게 유지할 것입니다.

고려해 봅시다 Vehicle. 차량에는 어떤 기능이 필요합니까? 도로를 여행해야합니다. 신호에서 멈출 수 있어야합니다. 교차로를 탐색 할 수 있어야합니다.

interface Vehicle {
  move(Road road);
  navigate(Road... intersection);
}

이것은 아마도 너무 간단하지만 시작입니다. 지금. 차량이 할 수있는 다른 모든 일은 어떻습니까? 도로에서 도랑으로 변할 수 있습니다. 시뮬레이션의 일부입니까? 아니요. 필요 없습니다. 일부 자동차와 버스에는 유압 장치가있어 각각 튀거나 무릎을 꿇을 수 있습니다. 시뮬레이션의 일부입니까? 아니요. 필요 없습니다. 대부분의 자동차는 휘발유를 태 웁니다. 그렇지 않은 사람도 있습니다. 발전소가 시뮬레이션의 일부입니까? 아니요. 필요 없습니다. 휠 사이즈? 필요하지 않습니다. GPS 네비게이션? 인포테인먼트 시스템? 그들을 필요로하지 않습니다.

사용하려는 동작 만 정의하면됩니다. 이를 위해 상호 작용하는 코드에서 OO 인터페이스를 작성하는 것이 더 낫다고 생각합니다. 빈 인터페이스로 시작한 다음 존재하지 않는 메소드를 호출하는 코드 작성을 시작하십시오. 이것이 인터페이스에서 어떤 방법이 필요한지 아는 방법입니다. 그런 다음이 동작을 구현하는 클래스를 정의하기 시작합니다. 사용되지 않은 동작은 관련이 없으며 정의 할 필요가 없습니다.

OO의 요점은 나중에 호출 코드를 변경하지 않고 이러한 인터페이스의 새로운 구현을 추가 할 수 있다는 것입니다. 작동하는 유일한 방법은 호출 코드의 요구에 따라 인터페이스에 적용되는 내용이 결정되는 것입니다. 나중에 생각할 수있는 모든 가능한 것들의 모든 행동을 정의 할 수있는 방법은 없습니다.


13
이것은 좋은 대답입니다. "그러므로 상호 작용하는 코드에서 OO 인터페이스를 구축하는 것이 더 낫다고 생각합니다." 물론, 그것이 유일한 방법이라고 주장합니다. 구현을 통해서만 인터페이스의 공개 계약을 알 수 없으며 항상 클라이언트의 관점에서 정의됩니다. (그리고 보조 노트로,이 TDD에 대해 실제로 것입니다.)
빈센트 Savard

@VincentSavard "나는 그것이 유일한 방법이라고 주장 할 것이다." 네 말이 맞아 나는 절대적으로 그렇게하지 않았다는 이유는 일단 당신이 일단 아이디어를 가지고 있다면, 당신은 인터페이스를 살살 벗기고 이런 식으로 그것을 다듬을 수 있기 때문입니다. 궁극적으로 황동 압정에 도달하면 그것이 유일한 문제입니다.
JimmyJames

@ jpmc26 어쩌면 조금 강하게 말했을 것입니다. 나는 이것을 구현하는 것이 드문 것에 동의하지 않습니다. 끔찍한 아이디어라고 생각되는 마커 인터페이스를 제외하고 이러한 방식으로 인터페이스를 사용하지 않으면 인터페이스가 어떻게 유용 할 수 있는지 잘 모르겠습니다.
JimmyJames

9

TL; DR :

모든 서브 클래스에 적용 할 수있는 추상화와 메소드를 생각하고 필요한 모든 것을 커버하십시오.

먼저 eat()예를 들어 봅시다 .

먹는 전제 조건으로, 인간은 손을 씻고 식사 전에 옷을 입기를 원하는 것은 인간의 재산입니다. 누군가가 나와 함께 아침 식사를하도록하려는 경우, 손을 씻고 옷을 입으 라고 말하지 말고 초대 할 때 스스로 시키거나 "아니오, 나는 올 수 없다 끝까지 손을 씻지 않았으며 아직 옷을 입지 않았습니다. "

소프트웨어로 돌아 가기 :

A와 Human전제 조건없이 식사를하지 않습니다 예, 나는이 줄 Humaneat()수행 방법 washHands()getDressed()그 수행하지 않은 경우. eat()그 특이성에 대해 아는 것이 당신의 일이 아니어야합니다 . 완고한 인간의 대안은 전제 조건이 충족되지 않으면 예외를 던지는 것입니다 ( "나는 먹을 준비가되어 있지 않습니다!"), 당신은 좌절하지만 최소한 식사는 효과가 없다고 통보했습니다.

무엇에 대해 makeEggs()?

당신의 사고 방식을 바꾸는 것이 좋습니다. 아마 모든 존재의 예정된 아침 의무를 수행하고 싶을 것입니다. 다시 한 번, 발신자로서 그들의 의무가 무엇인지 아는 것은 당신의 일이되어서는 안됩니다. 따라서 doMorningDuties()모든 클래스가 구현 하는 방법을 권장합니다 .


이 답변에 동의합니다. Narek은 코드 냄새에 대해 맞습니다. 그것은 냄새가 나는 인터페이스 디자인이므로 수정하십시오.
Jonathan van de Veen 8

이 답변에서 설명하는 것을 일반적으로 Liskov 대체 원칙 이라고합니다 .
Philipp

2

대답은 매우 간단합니다.

예상보다 더 많은 것을 할 수있는 물건을 다루는 방법?

목적에 맞지 않기 때문에 처리 할 필요가 없습니다. 인터페이스는 일반적으로 사용 방법에 따라 설계됩니다. 인터페이스에서 손 씻기를 정의하지 않으면 인터페이스 호출자로 신경 쓰지 않아도됩니다. 그렇게했다면 다르게 디자인했을 것입니다.

예를 들어, 아침 시간 인 경우 동물 일 경우에는 eat라고하고, 그렇지 않은 경우 인간 인 경우 먼저 washHands, getDressed를 호출 한 다음 eat를 호출하십시오. 이 경우를 처리하는 방법?

예를 들어, 의사 코드에서 :

interface IEater { void Eat(); }
interface IMorningRoutinePerformer { void DoMorningRoutine(); }
interface IAnimal : IEater, IMorningPerformer;
interface IHuman : IEater, IMorningPerformer; 
{
  void WashHands();
  void GetDressed();
}

void MorningTime()
{
   IList<IMorningRoutinePerformer> items = Service.GetMorningPerformers();
   foreach(item in items) { item.DoMorningRoutine(); }
}

지금 당신은 구현 IMorningPerformer을 위해 Animal대한 그냥 먹기를 수행하고, Human당신은 또한 손을 씻어야하고 옷을 입으을 구현합니다. MorningTime 메서드를 호출하면 사람이든 동물이든 상관 없습니다. 원하는 것은 매일 아침 수행되는 일이며 각 객체는 OO 덕분에 훌륭합니다.

다형성이 죽습니다.

아니면?

객체의 유형을 찾아야합니다

왜 그렇게 가정합니까? 나는 이것이 잘못된 가정 일 수 있다고 생각합니다.

이 경우를 처리하는 일반적인 방법이 있습니까?

예, 일반적으로 신중하게 설계된 클래스 또는 인터페이스 계층 구조로 해결됩니다. 위의 예에는 예를 들어 설명과 모순되는 것이 없지만 글을 쓰는 시점에서 질문에 쓰지 않았다는 가정을 더 많이 했으므로 불만족 스러울 것입니다. 이러한 가정은 위반 될 수 있습니다.

가정을 강화하고 여전히 만족하도록 대답을 수정하여 토끼 구멍에 갈 수는 있지만 유용하다고 생각하지는 않습니다.

좋은 계층 구조를 설계하는 것은 어렵고 비즈니스 영역에 대한 많은 통찰력이 필요합니다. 복잡한 도메인의 경우 비즈니스 모델의 다른 엔터티가 적절한 모델에 도달 할 때까지 상호 작용하는 방식에 대한 이해를 구체화함에 따라 하나, 둘, 셋 또는 그 이상의 반복이 진행됩니다.

단순한 동물 사례가 부족한 곳입니다. 우리는 단순하게 가르치고 싶지만, 우리가 해결하려고하는 문제는 더 깊이 들어가기 전까지는 분명하지 않습니다. 더 복잡한 고려 사항과 영역이 있습니다.


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