이 시나리오에서 구성 또는 상속을 선호해야합니까?


11

인터페이스를 고려하십시오.

interface IWaveGenerator
{
    SoundWave GenerateWave(double frequency, double lengthInSeconds);
}

이 인터페이스는 다양한 모양 (예 : SineWaveGeneratorSquareWaveGenerator)의 파도를 생성하는 여러 클래스로 구현됩니다 .

SoundWave원시 사운드 데이터가 아닌 음악 데이터를 기반으로 클래스를 생성하고 싶습니다 . 비트 이름 (초가 아님)으로 메모의 이름과 길이를 수신하고 내부적으로 IWaveGenerator기능을 사용하여 SoundWave그에 따라 작성합니다 .

질문은 NoteGenerator포함 IWaveGenerator해야하거나 IWaveGenerator구현 에서 상속해야 합니까?

나는 두 가지 이유 때문에 작곡에 기울고 있습니다.

1 그것은 나를 어떤을 주입 할 수 있도록 IWaveGenerator받는 NoteGenerator동적으로. 또한, 나는 단 하나 개의 필요 NoteGenerator가 아닌 클래스 SineNoteGenerator, SquareNoteGenerator

2-로 NoteGenerator정의 된 하위 레벨 인터페이스를 노출 할 필요가 없습니다 IWaveGenerator.

그러나 나는 이것에 관한 다른 의견을 듣기 위해이 질문을 게시하고 있습니다.

BTW : 나는 말할 것 NoteGenerator 입니다 개념적으로 IWaveGenerator는 생성하기 때문에 SoundWave들.

답변:


14

IWaveGenerator를 NoteGenerator에 동적으로 주입 할 수 있습니다. 또한 SineNoteGenerator , SquareNoteGenerator 대신 NoteGenerator 클래스가 하나만 필요합니다 .

즉, 여기에 사용 조성물에 좋을 것이다 명확한 기호이며, 상속하지 SineGeneratorSquareGenerator또는 (더 나쁜) 모두. 그럼에도 불구하고 NoteGenerator를 IWaveGenerator조금 변경하면 NoteGenerator를 직접 상속하는 것이 좋습니다 .

여기서 실제 문제 는 아마도 다음 NoteGenerator과 같은 방법 으로 갖는 것이 의미가 있다는 것입니다.

SoundWave GenerateWave(string noteName, double noOfBeats, IWaveGenerator waveGenerator);

그러나 방법으로는 아닙니다

SoundWave GenerateWave(double frequency, double lengthInSeconds);

이 인터페이스가 너무 구체적이기 때문입니다. 당신이 원하는 IWaveGenerator생성 객체로들 SoundWave들하지만, 현재 인터페이스 표현의 IWaveGenerator들 생성 객체 SoundWave전용 주파수와 길이와는 . 이런 식으로 인터페이스를 더 잘 설계하십시오.

interface IWaveGenerator
{
    SoundWave GenerateWave();
}

과 같은 매개 변수를 전달 frequency하거나 lengthInSeconds, 또는의 생성자를 통해 매개 변수의 완전히 다른 설정 SineWaveGenerator하는 SquareGenerator당신이 마음에, 또는 어떤 다른 발전기. 이를 IWaveGenerator통해 완전히 다른 구성 매개 변수로 다른 종류의을 만들 수 있습니다 . 주파수와 두 개의 길이 매개 변수가 필요한 구형파 발생기를 추가하고 싶을 수도 있고, 적어도 세 개의 매개 변수와 함께 다음에 삼각파 발생기를 추가 할 수도 있습니다. 또는 NoteGenerator생성자 매개 변수 noteName가있는 noOfBeats, 및 waveGenerator.

따라서 일반적인 해결책은 출력 매개 변수에서 입력 매개 변수를 분리하고 출력 기능 만 인터페이스의 일부로 만드는 것입니다.


흥미 롭습니다. 그러나 나는 이것이 궁금합니다 : 생성자에서 '매개 변수를 다형성 함수로 설정'하는 것이 실제로 실제로 작동합니까? 따라서 코드는 실제로 어떤 유형을 다루어야하는지 알아야하므로 다형성을 망칠 수 있습니다. 이것이 작동하는 예를 들어 줄 수 있습니까?
Aviv Cohn 2016 년

2
@AvivCohn : "코드는 실제로 어떤 유형을 다루는 지 알아야합니다."-아니요, 오해입니다. 특정 유형의 생성기 (공장)를 구성하는 코드 부분만이 처리하는 유형을 항상 알아야 합니다.
Doc Brown

... 그리고 객체의 구성 프로세스를 다형성으로 만들어야하는 경우 "추상 팩토리"패턴 ( en.wikipedia.org/wiki/Abstract_factory_pattern )을 사용할 수 있습니다.
Doc Brown

이것이 내가 선택할 솔루션입니다. 작고 불변의 수업이 여기에가는 올바른 방법입니다.
Stephen

9

NoteGenerator가 "개념적으로"인지 여부는 중요하지 않습니다.

Liskov 대체 원칙에 따라, 즉 올바른 의미와 올바른 구문을 사용하여 정확한 인터페이스를 구현하려는 경우 인터페이스에서 상속해야합니다.

NoteGenerator는 구문 상 동일한 인터페이스를 가지고있는 것처럼 들리지만 의미 (이 경우 필요한 매개 변수의 의미)는 매우 다르므로이 경우 상속을 사용하면 오류가 발생하기 쉬울 수 있습니다. 당신은 여기에서 작곡을 선호합니다.


실제로 나는 매개 변수를 다르게 NoteGenerator구현 GenerateWave하고 해석 한다는 것을 의미하지는 않았다 . 그렇다. 나는 그것이 끔찍한 아이디어라고 동의한다. 나는 NoteGenerator가 웨이브 제너레이터의 일종의 특수화라는 것을 의미했습니다. 원시 사운드 데이터 (예 : 주파수 대신 노트 이름) 대신 '상위 레벨'입력 데이터를 사용할 수 있습니다. 즉 sineWaveGenerator.generate(440) == noteGenerator.generate("a4"). 따라서 질문, 구성 또는 상속이 있습니다.
Aviv Cohn

하이 레벨 및 로우 레벨 웨이브 생성 클래스 모두에 적합한 단일 인터페이스를 생각 해낼 수 있다면 상속이 가능할 수 있습니다. 그러나 그것은 매우 어려운 것처럼 보이며 실제 혜택을 얻지 못할 것입니다. 작곡은 더 자연스러운 선택처럼 보입니다.
Ixrec 2016 년

@Ixrec : 실제로, 발전기의 모든 종류의 단일 인터페이스를 가지고하는 것은 매우 어려운 일이 아니다, 영업 이익은 아마 낮은 수준의 발전기 주입하기 위해 사용 구성을 모두해야 하고 단순화 된 인터페이스에서 상속을 (아니지만에서 NoteGenerator 상속 low level generator implementation) 내 답변을 참조하십시오.
Doc Brown

5

2- NoteGenerator가 IWaveGenerator에 의해 정의 된 하위 레벨 인터페이스를 노출 할 필요가 없습니다.

같은 소리 NoteGenerator는 아니므로 WaveGenerator인터페이스를 구현하지 않아야합니다.

작문이 올바른 선택입니다.


나는 말할 것 NoteGenerator IS 개념적으로 IWaveGenerator는 생성하기 때문에 SoundWave들.
Aviv Cohn 2016 년

1
이 노출 할 필요가없는 경우 음, GenerateWave다음, 그것은 아니다 IWaveGenerator. 그러나 IWaveGenerator를 사용 하는 것처럼 들립니다.
Eric King

@ EricKing : 이것은 GenerateWave질문에 쓰여진 기능 에 충실 해야하는 한 정답 입니다. 그러나 위의 의견에서 OP가 실제로 생각한 것이 아니라고 생각합니다.
Doc Brown

3

컴포지션에 대한 견고한 케이스가 있습니다. 상속 추가해야 할 수도 있습니다. 말하는 방법은 호출 코드를 보는 것입니다. NoteGenerator을 기대하는 기존 호출 코드 를 사용 IWaveGenerator하려면 인터페이스를 구현해야합니다. 대체 가능성이 필요합니다. 그것이 개념적으로 "is-a"파 발생기인지의 여부는 요점입니다.


이 경우, 즉 컴포지션을 선택하지만 대체 가능성을 만들기 위해 상속을 여전히 필요로하는 경우 "상속"은 예를 들어 IHasWaveGenerator, 해당 인터페이스의 관련 메소드 GetWaveGenerator는의 인스턴스를 반환합니다 IWaveGenerator. 물론 이름을 변경할 수 있습니다. (저는 더 자세한 내용을
살피

2

그것은 괜찮지 NoteGenerator위해, 또한 인터페이스를 구현하고, NoteGenerator저 (구성에 의해) 참조 다른 내부 구현을 가지고 IWaveGenerator.

일반적으로 컴포지션은 추론에 대한 재정의가 복잡하지 않기 때문에 유지 관리가 용이 ​​한 (즉, 읽을 수있는) 코드를 생성합니다. 상속을 사용할 때 가질 수있는 클래스 매트릭스에 대한 관찰도 중요하며 컴포지션을 가리키는 코드 냄새로 생각할 수 있습니다.

상속은 전문화하거나 사용자 정의하려는 구현이있을 때 더 잘 사용됩니다. 여기서는 그렇지 않습니다. 인터페이스 만 사용하면됩니다.


1
음표에는 비트가 필요하기 때문에 NoteGenerator구현 IWaveGenerator하기에 좋지 않습니다 . 초가 아닙니다.
Tulains Córdova 2016

그렇습니다. 인터페이스의 합리적인 구현이 없다면 클래스는 그것을 구현해서는 안됩니다. 그러나 OP는 "나는 그것이 s를 생성하기 때문에 NoteGenerator개념적으로 말하고 싶다"고 진술 했으며 상속을 고려하고 있었기 때문에 인터페이스가 구현되었지만 인터페이스가 구현되었을 가능성에 대해 정신적으로 위도를 찾았습니다. 더 나은 인터페이스 또는 클래스 서명. IWaveGeneratorSoundWave
Erik Eidt 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.