전략 패턴과 의존성 주입을 사용하여 상속을 완전히 대체 할 수 있습니까?


10

예를 들면 다음과 같습니다.

var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)

Duck 클래스에는 모든 비헤이비어 (추상)가 포함되어 있으므로 새 클래스를 만드는 MallardDuck(확장 Duck) 필요하지 않은 것 같습니다.

참조 : 헤드 퍼스트 디자인 패턴, 1 장.


Duckbehavior.quackBehavior코드 의 유형 과 기타 필드 는 무엇입니까 ?
max630

귀하의 예에는 의존성 주입이 없습니다.
David Conrad

11
중급 개발자부터 중급 개발자까지 상속은 훌륭합니다. 리팩터링 트릭과 디자인 패턴에 대한 오랜 역사가 있기 때문입니다. 그러나 내가 이야기했던 대부분의 숙련 된 개발자는 가능할 때마다 구성을 기반으로 한 매우 상속 상속 계층 구조를 선호한다고 말했습니다. 상속은 필요한 것보다 더 긴밀한 결합을 야기 할 수 있으며 변경을 어렵게 만들 수 있습니다.
Mark Rogers


5
@DavidConrad : OP가 클래스 내에서 새로운 작업을 수행하지 않습니다 . DI / IoC는 컨테이너 나 "새"를 사용 하지 않는 의존성 주입 에 관한 것입니다. 그것은 전적으로 직교하는 관심사입니다. 게다가 컴포지션 루트 또는 구성 파일이든 항상 코드를 어딘가 에서 변경해야 합니다. 제어는 반전 종속성의 컨트롤 생성이 오리의 ctor 있지만, 일부 외부 상황이 아니라는 것 같이 오리 유형 내에서 여기; 이것은 명확성을 위해 주어진 장난감의 예입니다. 외부 컨텍스트가 단지 호출 코드로 표현되는 것은 완벽합니다.
Filip Milovanović

답변:


21

물론 우리는 그 구성과 위임 이라고 부릅니다 . 전략 패턴과 의존성 주입은 구조적으로 비슷해 보이지만 의도는 다릅니다.

전략 패턴 은 동일한 인터페이스에서 동작의 런타임 수정을 허용합니다. 나는 청둥 오리 오리에게 날아 다니며 날아가는 날개를 볼 수 있습니다. 그런 다음 제트 파일럿 덕으로 바꾸고 델타 항공과 함께 날아가는 것을 지켜보십시오. 프로그램이 실행되는 동안하는 것은 전략 패턴입니다.

Dependency Injection 은 하드 코딩 종속성을 방지하여 변경시 클라이언트를 수정하지 않고도 독립적으로 변경할 수있는 기술입니다. 고객은 어떻게 충족 될지 모릅니다. 따라서 그들이 어떻게 충족되는지는 다른 곳에서 결정됩니다 (일반적으로 주된). 이 기술을 사용하기 위해 오리 두 마리가 필요하지 않습니다. 오리를 모르거나 돌보지 않고 오리를 사용하는 것. 오리를 만들거나 찾아 보지 않지만 손에 든 오리를 사용하는 것은 행복합니다.

구체적인 오리 클래스가 있다면 플라이 동작을 구현할 수 있습니다. 상태 변수를 기반으로 동작을 날개가있는 플라이에서 델타가있는 플라이로 전환하도록 할 수도 있습니다. 그 변수는 부울, int 또는 if로 테스트하지 않고도 모든 비행 스타일을 수행 FlyBehavior하는 fly메소드 가있는 메소드 일 수 있습니다 . 이제 오리 유형을 변경하지 않고도 비행 스타일을 변경할 수 있습니다. 이제 청둥 오리는 조종사가 될 수 있습니다. 이것은 구성과 위임 입니다. 오리는 FlyBehavior로 구성되어 있으며 비행 요청을 위임 할 수 있습니다. 이 방법으로 모든 오리 동작을 한 번에 바꾸거나 각 동작 또는 그 사이의 조합에 대해 무언가를 유지할 수 있습니다.

이것은 하나를 제외하고 상속이 가진 동일한 힘을 모두줍니다. 상속을 사용하면 Duck 하위 유형에서 재정의하는 Duck 메소드를 표현할 수 있습니다. 구성 및 위임을 위해서는 Duck이 처음부터 하위 유형에 명시 적으로 위임해야합니다. 이것은 훨씬 유연하지만 더 많은 키보드 입력이 필요하며 Duck은 그것이 일어나고 있음을 알아야합니다.

그러나 많은 사람들은 상속이 처음부터 명시 적으로 설계되어야한다고 생각합니다. 그리고 그렇지 않은 경우 상속을 허용하지 않도록 클래스를 봉인 / 최종으로 표시해야합니다. 그러한 견해를 취한다면 상속은 실제로 구성과 위임보다 이점이 없습니다. 따라서 어느 쪽이든 처음부터 확장 성을 위해 디자인하거나 나중에 물건을 찢어 버릴 수 있어야하기 때문입니다.

물건을 찢는 것은 실제로 인기있는 옵션입니다. 문제가있는 경우가 있다는 것을 명심하십시오. 다음 릴리스로 업데이트하지 않으려는 라이브러리 또는 코드 모듈을 독립적으로 배포 한 경우 현재 상황에 대해 전혀 모르는 클래스 버전을 처리하지 못할 수 있습니다.

나중에 물건을 찢어 버릴 의향이 있지만 과도한 디자인을 피할 수는 있지만 오리가 실제로 사용될 때 오리가 실제로 무엇을하는지 알 필요없이 오리를 사용하는 것을 디자인 할 수있는 매우 강력한 것이 있습니다. 모르는 것은 강력한 것입니다. 오리에 대한 생각을 잠시 멈추고 나머지 코드에 대해 생각할 수 있습니다.

"우리가 할 수 있을까"와 "우리가해야 할까"는 다른 질문입니다. 상속보다 호의적 인 구성은 상속을 사용 하지 않는다고 말하지 않습니다. 상속이 가장 적합한 경우가 여전히 있습니다. 내가 가장 좋아하는 예를 보여 드리겠습니다 .

public class LoginFailure : System.ApplicationException {}

상속을 사용하면 한 줄에보다 구체적이고 설명적인 이름으로 예외를 만들 수 있습니다.

작곡으로 해보십시오. 엉망이 될 것입니다. 또한 상속 체인을 재사용하고 장려 할 데이터 나 방법이 없기 때문에 상속 요요 문제 의 위험 이 없습니다. 이 모든 것이 좋은 이름입니다. 좋은 이름의 가치를 과소 평가하지 마십시오.


1
" 좋은 이름의 가치를 과소 평가하지 마십시오 ". 황제의 새로운 옷 시간 : LoginException좋은 이름이 아닙니다. 그것은 고전적인 "스머프 네이밍 (smurf naming)"이며, 내가 빼앗아 가면 Exception, 내가 가진 모든 Login것은 무엇이 잘못되었는지 전혀 알려주지 않습니다.
David Arno

슬프게도 우리는 Exception모든 예외 클래스를 끝내는 어리석은 관습에 갇혀 있지만 "고착"과 "좋은"을 혼동하지 마십시오.
David Arno

@DavidArno 더 나은 이름을 제안 할 수 있다면 나는 모두 귀입니다. 여기에서 우리는 기존 코드베이스의 규칙에 갇히지 않는 이점이 있습니다. 손가락 하나로 세상을 바꿀 힘이 있다면 무엇을 하시겠습니까?
candied_orange 2016 년

나는 그들에게, 이름 것 StackOverflow, OutOfMemory, NullReferenceAccess, LoginFailure등 기본적으로, 이름 떨어져 "예외"를 취할. 그리고 필요한 경우 무엇이 잘못되었는지 설명하도록 수정하십시오.
David Arno

당신의 명령에 의해 @DavidArno.
candied_orange

7

거의 모든 방법론을 다른 방법론으로 대체하고 여전히 작동하는 소프트웨어를 생성 할 수 있습니다. 그러나 일부는 다른 것보다 특정 문제에 더 적합합니다.

어느 것이 바람직한 지에 달려 있습니다. 응용 분야의 선행 기술, 팀에서의 경험, 예상되는 미래 개발, 개인적 취향 및 신규 이민자가 그 주위에 머리를 갖다 대는 것이 얼마나 힘든지, 몇 가지 예를 들었습니다.

다른 사람들의 창작물에 대해 더 많은 경험과 어려움을 겪으면서 마지막 묵상에 더 중점을 둘 것입니다.

상속은 여전히 ​​항상 가장 유연하지는 않지만 여전히 유효하고 강력한 모델링 도구이지만 문제 영역에 대한 명확한 매핑에 감사 할 수있는 새로운 사람들에게 강력한 지침을 제공합니다.


-1

상속은 한 번 생각했던 것만 큼 중요하지 않습니다. 여전히 중요 하며 제거하는 것은 나쁜 실수입니다.

극단적 인 예는 모든 것이 NSObject의 서브 클래스 인 Objective-C입니다 (컴파일러는 실제로 기본 클래스가없는 클래스를 선언 할 수 없으므로 컴파일러에 내장되지 않은 모든 것은 무언가의 서브 클래스 여야합니다) 컴파일러에 내장). NSObject에는 많은 유용한 것들이 내장되어 있습니다.

또 다른 흥미로운 예는 iOS 개발을위한 UIKit의 기본 "뷰"클래스 인 UIView입니다. 서브 클래스가 작성 해야하는 기능을 선언하는 추상 클래스와 약간 비슷한 클래스이지만 자체적으로 유용합니다. UIKit에서 제공하는 서브 클래스가 있으며 이는 종종있는 그대로 사용됩니다. 뷰에 하위 뷰를 설치하는 개발자의 구성이 있습니다. 그리고 종종 컴포지션을 사용하는 개발자 정의 서브 클래스가 있습니다. 엄격한 단일 규칙이나 규칙이 없으므로 요구 사항에 가장 적합한 것을 사용하십시오.


여러분의 "극단적 인 예"는 대부분의 최신 OO 언어가 작동하는 방식입니다. Python 서브 클래스 type, Java의 기본이 아닌 Object모든 것이 상속하고 , JavaScript의 모든 것은로 끝나는 프로토 타입이 있습니다 Object.
Delioth

-1

[전형적인 질문에 건방진 답변을 드리겠습니다.]

전략 패턴과 의존성 주입을 사용하여 상속을 완전히 대체 할 수 있습니까?

예 ... 전략 패턴 자체는 상속을 사용하지 않습니다. 전략 패턴은 인터페이스 상속에서 작동합니다. 상속을 구성 + 전략으로 바꾸면 상속이 다른 곳으로 이동합니다. 그럼에도 불구하고, 그러한 대체는 종종 계층을 분리 할 수 있기 때문에 수행 할 가치가 있습니다 .


2
" 전략 패턴은 인터페이스 상속에서 작동합니다 ." 전략 패턴은 디자인 패턴입니다. 구현 패턴이 아닙니다. 따라서 인터페이스를 사용하여 구현할 수 있지만 해시 / 사전 함수를 사용하여 동일하게 구현할 수 있습니다.
David Arno

3
인터페이스 상속은 전혀 상속이 아니며 단지 일종의 계약 또는 분류 자입니다. 전략 패턴에도 대리자를 사용할 수 있습니다 (대표 사용을 선호합니다).
Deepak Mishra

게다가, 친구 : ... 인터페이스 상속 에서 작동 합니다. 일반적인 용어 "인터페이스"의 올바른 사용에 대한 조언입니다. 나는 빈약하거나 무능한 수업 디자인이이 질문이 제기 된 기본적인 이유라고 생각합니다. 왜 / 어떻게 책을 가져갈 것인지 설명하기 위해; 악마는 세부 사항에 있습니다. 나는 기쁨으로 여겨 졌던 상속 디자인과 동시에 지옥이었던 깊은 상속으로 작업했습니다. 나는 interface상속으로 고정 된 끔찍한 디자인을 보았습니다 . 여기서 숨겨진 문제는 일관되지 않은 구현 모핑 종속 동작입니다. 추신. 상속과 인터페이스는 상호 배타적이지 않습니다
radarbob

@DavidArno 의견 : OP 의견에 첨부하면이 의견이 좋을 것입니다. OP 질문은 기술적으로 잘못 설명되어 있지만 여전히 문제가 있습니다. 문제는이 특정 답변이 아닙니다.
radarbob 2016 년

@DeepakMishra 코멘트 : . "인터페이스"는 클래스의 모든 공개 멤버입니다. 불행히도 "인터페이스"의미가 오버로드되었습니다. 우리는 프로그래밍 언어의 키워드와 일반적인 의미를 구별하기 위해 조심해야합니다interface
radarbob

-2

청둥 오리 오리가 다른 종류의 오리와 인스턴스화하기 위해 다른 매개 변수를 필요로한다면, 변경하는 것은 악몽 일 것입니다. 또한 오리를 인스턴스화 할 수 있어야합니까? 그것은 청둥 오리 오리 또는 만다린 오리 또는 다른 종류의 오리입니다.

또한 유형에 대한 논리를 원할 수 있으므로 유형 계층 구조가 더 좋습니다.

이제 코드 재사용이 일부 기능에 문제가되는 경우 생성자를 통해 함수를 전달하여 작성할 수 있습니다.

다시 말하지만, 실제로는 사용 사례에 따라 다릅니다. 오리와 청둥 오리 오리 만 있으면 클래스 계층 구조가 훨씬 간단한 솔루션입니다.

그러나 다음은 전략 패턴을 사용하려는 예입니다. 고객 클래스가 있고 행복한 시간 청구 전략 또는 일반 청구 전략이 될 수있는 청구 전략 (인터페이스)을 전달하려는 경우 전달할 수 있습니다. 생성자에서. 이렇게하면 서로 다른 두 종류의 고객을 만들 필요가 없으며 계층 구조가 필요하지 않습니다. 고객 클래스는 하나뿐입니다.

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