생성자 전용 서브 클래스 : 안티 패턴입니까?


37

나는 동료와 토론을하고 있었고, 결국 서브 클래 싱의 목적에 대해 직관이 상충되었습니다. 내 직감은 서브 클래스의 주요 기능이 부모의 가능한 범위의 값을 표현하는 것이라면 아마도 서브 클래스가 아니어야한다는 것입니다. 그는 반대 직관을 주장했다. 서브 클래 싱은 객체의 "특정"성을 나타내므로 서브 클래스 관계가 더 적절하다.

직관을보다 구체적으로 설명하기 위해 부모 클래스를 확장하는 하위 클래스가 있지만 하위 클래스를 재정의하는 유일한 코드는 생성자 인 경우 (생성자는 일반적으로 "재정의하지 않음을 알고 있습니다.) 실제로 필요한 것은 도우미 방법이었습니다.

예를 들어 다음과 같은 실제 클래스를 고려하십시오.

public class DataHelperBuilder
{
    public string DatabaseEngine { get; set; }
    public string ConnectionString { get; set; }

    public DataHelperBuilder(string databaseEngine, string connectionString)
    {
        DatabaseEngine = databaseEngine;
        ConnectionString = connectionString;
    }

    // Other optional "DataHelper" configuration settings omitted

    public DataHelper CreateDataHelper()
    {
        Type dataHelperType = DatabaseEngineTypeHelper.GetType(DatabaseEngine);
        DataHelper dh = (DataHelper)Activator.CreateInstance(dataHelperType);
        dh.SetConnectionString(ConnectionString);

        // Omitted some code that applies decorators to the returned object
        // based on omitted configuration settings

        return dh;
    }
}

그의 주장은 다음과 같은 하위 클래스를 갖는 것이 전적으로 적절하다는 것입니다.

public class SystemDataHelperBuilder
{
    public SystemDataHelperBuilder()
        : base(Configuration.GetSystemDatabaseEngine(),
               Configuration.GetSystemConnectionString())
    {
    }
 }

따라서 질문 :

  1. 디자인 패턴에 대해 이야기하는 사람들 중에서이 직관 중 어느 것이 맞습니까? 위에서 설명한 서브 클래스가 안티 패턴입니까?
  2. 안티 패턴이라면 그 이름은 무엇입니까?

이것이 쉽게 Google 답변이 될 수 있음을 사과드립니다. 구글에서 내 검색은 주로 텔레 스코핑 생성자 안티 패턴에 대한 정보를 반환했지만 실제로 내가 찾고있는 것이 아닙니다.


2
이름에서 "Helper"라는 단어를 반 패턴으로 생각합니다. 그것은 물건과 Whatsit에 대한 지속적인 참조로 에세이를 읽는 것과 같습니다. 이 무슨 말을 입니다 . 공장입니까? 건설자? 나에게 그것은 게으른 사고 과정을 깨뜨 렸고 적절한 의미 론적 이름으로 지시 할 것이기 때문에 단일 책임 원칙을 위반하는 것을 장려합니다. 나는 다른 유권자들에 동의하며 @lxrec의 답변을 수락해야합니다. 팩토리 메소드의 서브 클래스 작성은 문서에 지정된대로 사용자가 올바른 인수를 전달하도록 신뢰할 수 없으면 완전히 의미가 없습니다. 이 경우 새로운 사용자를 확보하십시오.
Aaron Hall

@Ixrec의 답변에 전적으로 동의합니다. 결국, 그의 대답은 내가 옳았 고 내 동료가 잘못되었다고 말합니다. ;-) 그러나 진지하게, 그것이 내가 그것을 받아들이는 것이 이상하게 느껴지는 이유입니다. 편견으로 고발 될 수 있다고 생각했습니다. 그러나 저는 프로그래머 인 StackExchange를 처음 사용합니다. 에티켓을 위반하지 않을 것이라고 확신하면 기꺼이 받아 들일 것입니다.
HardlyKnowEm

1
아닙니다. 가장 가치 있다고 생각되는 답변을 수락 할 것으로 예상됩니다. 커뮤니티가 지금까지 가장 가치가 있다고 생각하는 것은 Ixrec의 3 대 1 이상입니다. 따라서 그들은 당신이 그것을 받아들이기를 기대할 것입니다. 이 공동체에서는, 오답 자에게는 오답을 나타내는 답이 거의 없지만, 답을 절대 받아들이지 않는 질문자에게는 많은 답답이 있습니다. 여기에서 수락 된 답변에 대해 불평하는 사람은 아무도 없으며 대신 투표 자체에 대해 불평하는 것 같습니다. stackoverflow.com/q/2052390/541136
Aaron Hall

아주 잘 받아들입니다. 시간을내어 커뮤니티 표준에 대해 교육 해 주셔서 감사합니다.
HardlyKnowEm

답변:


54

특정 인수를 사용하여 클래스 X를 작성하기 만하면 서브 클래 싱은 클래스와 상속이 제공하는 기능을 사용하지 않으므로 의도를 표현하는 이상한 방법입니다. 그것은 실제로 안티 패턴이 아니며, 이상하고 약간 무의미합니다 (다른 이유가 없다면). 이 의도를 표현하는보다 자연스러운 방법은 Factory Method 이며,이 경우 "도우미 방법"의 이름입니다.

일반적인 직관과 관련하여 "보다 구체적인"및 "제한된 범위"는 잠재적으로 서브 클래스에 대해 생각하는 데 해로운 방법입니다. 둘 다 Square를 Rectangle의 서브 클래스로 만드는 것이 좋습니다. LSP와 같은 공식적인 것에 의존하지 않으면 서, 서브 클래스 가 기본 인터페이스 의 구현제공 하거나 새로운 기능을 추가하기 위해 인터페이스확장 하는 것이 더 직관이라고 말할 것 입니다.


2
그러나 팩토리 메소드를 사용하면 코드베이스에서 특정 동작 (인수로 관리)을 시행 할 수 없습니다. 때로는이 클래스 / 메소드 / 필드가 SystemDataHelperBuilder구체적으로 사용 된다는 것을 명시 적으로 주석 처리하는 것이 유용합니다 .
Telastyn

3
아, 제곱 대 직사각형 논쟁은 내가 찾던 것과 정확히 일치한다고 생각합니다. 감사!
HardlyKnowEm

7
물론 사각형을 사각형의 하위 클래스로 만드는 것은 불변이라면 괜찮을 수 있습니다. 당신은 정말로 LSP를 봐야합니다.
David Conrad

10
정사각형 / 직사각형 "문제"에 대한 것은 기하학적 모양이 변경 불가능하다는 것입니다. 사각형이 의미가있는 곳이면 항상 사각형을 사용할 수 있지만 크기 조정은 사각형이나 사각형이 아닌 것이 아닙니다.
Doval

3
@nha LSP = Liskov 대체 원칙
Ixrec

16

이 직관 중 어느 것이 맞습니까?

동료가 정확합니다 (표준 유형 시스템 가정).

생각해보십시오. 클래스는 가능한 법적 가치를 나타냅니다. class A에 1 바이트 필드 가있는 경우 256 개의 유효한 값이 F있다고 생각하는 경향이 A있지만 직감이 잘못되었습니다. A"값의 모든 순열"을 "필드 F가 0-255 여야 함"으로 제한합니다 .

다른 바이트 필드를 A가진 확장 하면 제한추가됩니다 . "값 이 바이트 인 값"대신 " 바이트가있는 값 && 도 바이트"입니다. 이전 규칙을 유지하므로 기본 유형에 효과가 있던 모든 것이 여전히 하위 유형에 적용됩니다. 그러나 규칙을 추가하기 때문에 "아무것도 될 수 없습니다"에서 더 전문화되고 있습니다.BFFFFFF

아니면 또 다른 방법을 생각하는, 즉 가정 A: 아형의 무리를했다 B, C하고 D. 유형 변수는 A이러한 하위 유형 중 하나 일 수 있지만 유형 변수 B가 더 구체적입니다. (대부분의 유형 시스템에서) a C또는 a 일 수 없습니다 D.

반 패턴입니까?

응? 특수한 객체를 갖는 것은 유용하며 코드에 명백히 해롭지 않습니다. 서브 타이핑을 사용하여 결과를 달성하는 것은 다소 의문의 여지가 있지만, 어떤 도구를 사용할 수 있는지에 달려 있습니다. 더 나은 도구를 사용하더라도이 구현은 간단하고 간단하며 강력합니다.

나는 어떤 상황에서도 분명히 잘못되고 나쁘지 않기 때문에 이것을 안티 패턴으로 간주하지 않을 것입니다.


5
"규칙이 많을수록 가능한 값이 적고 더 구체적임을 의미합니다." 나는 이것을 정말로 따르지 않는다. Type byte and byte은 단순히 type보다 많은 값을 가지고 있습니다 byte. 256 배 그 외에도, 규칙과 여러 요소 (또는 정확한 경우 카디널리티)를 혼동 할 수 있다고 생각하지 않습니다. 무한 세트를 고려하면 고장납니다. 정수만큼의 짝수도 있지만 숫자가 여전히 제한적이라는 것을 아는 것은 정수라는 것을 아는 것보다 더 많은 정보를 제공합니다! 자연수에 대해서도 마찬가지입니다.
Doval

6
@Telastyn 우리는 주제를 벗어 났지만, 짝수 정수 세트의 카디널리티 (느슨하게 "크기")가 모든 정수 세트 또는 모든 합리적인 숫자와 같을 가능성이 있습니다. 정말 멋진 물건입니다. 참조 math.grinnell.edu/~miletijo/museum/infinite.html
HardlyKnowEm

2
집합 이론은 매우 직관적이지 않습니다. 정수와 짝수를 일대일 대응으로 넣을 수 있습니다 (예 : 함수 사용 n -> 2n). 항상 가능한 것은 아닙니다. 정수를 실수에 매핑 할 수 없으므로 실수 세트는 정수보다 "더 큰"것입니다. 그러나 (실제) 간격 [0, 1]을 모든 실수 세트에 매핑 할 수 있으므로 동일한 "크기"가됩니다. 하위 집합은 "모든 요소는" A의 요소이기도합니다. B정확하게 정의 되면 일단 집합이 무한 해지면 동일한 카디널리티이지만 다른 요소를 가질 수 있기 때문입니다.
Doval

1
현재 다루고있는 주제와 더 관련이있는 예제는 객체 지향 프로그래밍 측면에서 실제로 추가 필드 측면에서 기능을 확장하는 제약 조건입니다. 원 타원 문제와 같이 기능을 확장하지 않는 하위 클래스에 대해 이야기하고 있습니다.
HardlyKnowEm

1
@Doval : "더 적은"이라는 의미는 여러 가지가 있습니다. 즉, 집합에 대한 둘 이상의 순서 관계입니다. "집합의 하위 집합"관계는 세트에 대해 잘 정의 된 (부분) 순서를 제공합니다. 더욱이, "더 많은 규칙들"은 "집합의 모든 요소들이 (특정 세트로부터) 더 많은 (즉, 수퍼 세트) 제안을 만족시킨다"는 것을 의미 할 수있다. 이러한 정의에서 "더 많은 규칙"은 실제로 "더 적은 값"을 의미합니다. 이것은 스콧 도메인과 같은 컴퓨터 프로그램의 여러 시맨틱 모델에 의해 취해진 접근법입니다.
psmears

12

아니요, 안티 패턴이 아닙니다. 이에 대한 여러 가지 실제 사용 사례를 생각할 수 있습니다.

  1. 컴파일 시간 검사를 사용하여 오브젝트 콜렉션이 하나의 특정 서브 클래스 만 준수하는지 확인하려는 경우. 예를 들어 시스템 이 MySQLDao있고 SqliteDao시스템에 있지만 어떤 이유로 컬렉션에 한 소스의 데이터 만 포함시키려는 경우 설명에 따라 서브 클래스를 사용하는 경우 컴파일러에서이 특정 정확성을 확인할 수 있습니다. 반면에 데이터 소스를 필드로 저장하면 런타임 검사가됩니다.
  2. 현재 응용 프로그램은 AlgorithmConfiguration+ ProductClass와 사이에 일대일 관계가 AlgorithmInstance있습니다. 당신이 구성하는 경우 즉, FooAlgorithm에 대한 ProductClass속성 파일에서, 당신은 하나 얻을 수있는 FooAlgorithm해당 제품 클래스를. FooAlgorithm주어진 것에 대해 두 가지를 얻는 유일한 방법 ProductClass은 서브 클래스를 만드는 것 FooBarAlgorithm입니다. 이는 특성 파일을 사용하기 쉽게하므로 (특성 파일의 한 섹션에 여러 구성에 대한 구문이 필요하지 않기 때문에), 주어진 제품 클래스에 대해 둘 이상의 인스턴스가있는 경우는 거의 없습니다.
  3. @Telastyn의 답변 은 또 다른 좋은 예입니다.

결론 :이 작업을 수행하는 데 실제로 아무런 해가 없습니다. 안티 패턴으로 정의된다 :

반 패턴 (또는 반 패턴)은 일반적으로 비 효과적이고 비생산적인 위험이 반복되는 문제에 대한 일반적인 대응입니다.

여기서는 반제품 생산의 위험이 없으므로 반 패턴이 아닙니다.


2
그래, 내가 다시 질문을해야한다면 "코드 냄새"라고 말할 것 같아. 다음 세대의 젊은 개발자에게이를 피하도록 경고하는 것만으로는 나쁘지 않지만, 그것을 보았을 때, 중요한 디자인에 문제 있음을 나타낼 수 있지만 궁극적으로는 정확할 수도 있습니다.
HardlyKnowEm

포인트 1이 목표 바로 위에 있습니다. 나는 실제로 포인트 2를 이해하지 못했지만, 그것이 좋은지 확신합니다 ... :)
Phil

9

어떤 패턴이나 반 패턴 인 경우 어떤 언어와 환경을 작성 하느냐에 따라 크게 달라집니다. 예를 들어, for루프 어셈블리, C 및 유사한 언어의 패턴이고 lisp의 반 패턴입니다.

오래 전에 작성한 코드에 대한 이야기를 들려 드리겠습니다 ...

LPC 라는 언어 로 게임에서 주문을 발동하기위한 프레임 워크를 구현했습니다. 당신은 몇 가지 수표를 처리하는 전투 주문의 서브 클래스와 단일 목표 직접 피해 주문을위한 서브 클래스를 가진 주문 슈퍼 클래스를 가졌으며, 그런 다음 개별 주문 (매직 미사일, 번개 등)으로 서브 클래 싱되었습니다.

LPC의 작동 방식은 싱글 톤이라는 마스터 객체를 가지고 있다는 것입니다. 당신이 'eww 싱글 톤'에 가기 전에-그것은 "eww"가 아니었다. 주문의 사본을 만들지 않고 주문에서 (효과적으로) 정적 메소드를 호출했습니다. 매직 미사일 코드는 다음과 같습니다.

inherit "spells/direct";

void init() {
  ::init();
  damage = 10;
  cost = 3;
  delay = 1.0;
  caster_message = "You cast a magic missile at ${target}";
  target_message = "You are hit with a magic missile cast by ${caster}";
}

그리고 당신은 그것을 참조하십시오? 생성자 전용 클래스입니다. 그것은 부모 추상 클래스에있는 일부 값을 설정했습니다 (아니, setter를 사용해서는 안됩니다-불변). 제대로 작동 했습니다 . 코딩하기 쉽고 확장하기 쉽고 이해하기도 쉬웠습니다.

이러한 종류의 패턴은 추상 클래스를 확장하고 자체 값을 설정하는 서브 클래스가있는 다른 상황으로 변환되지만 모든 기능은 상속하는 추상 클래스에 있습니다. Java의 StringBuilder 를 살펴보십시오. 이 예제는 꽤 생성자 일뿐 만 아니라 메소드 자체에서 많은 논리를 찾아야합니다 (모두 AbstractStringBuilder에 있음).

이것은 나쁜 일이 아니다, 그것은이다 확실히 안티 패턴하지 (일부 언어는 패턴 자체를 할 수있다).


2

내가 생각할 수있는 이러한 서브 클래스의 가장 일반적인 사용은 예외 계층 구조이지만 언어가 생성자를 상속 할 수있게하는 한 클래스에서 아무것도 정의하지 않는 퇴보 적 인 경우입니다. 우리는 상속을 사용 ReadError하여이를 특별한 경우 등 으로 표현 IOError하지만의 ReadError메소드를 재정의 할 필요는 없습니다 IOError.

유형을 확인하여 예외가 발생하기 때문에이 작업을 수행합니다. 따라서 누군가가 원하는 경우 ReadError모두 잡을 필요없이 잡을 수 있도록 유형의 전문화를 인코딩 IOError해야합니다.

일반적으로 사물의 유형을 확인하는 것은 끔찍한 형태이며 피하려고 노력합니다. 이를 피하는 데 성공하면 유형 시스템에서 전문화를 표현할 필요가 없습니다.

예를 들어, 유형의 이름이 객체의 로그 된 문자열 형식으로 나타나는 경우에도 여전히 유용 할 수 있습니다. 언어 및 프레임 워크에 따라 다를 수 있습니다. 원칙적으로 그러한 시스템의 유형 이름을 클래스의 메소드 호출로 대체 SystemDataHelperBuilder있습니다 loggingname(). 따라서 시스템에 이러한 로깅이 있으면 클래스가 문자 그대로 생성자를 재정의하지만 "도덕적으로"클래스 이름을 재정의하는 것이므로 실제로는 생성자 전용 하위 클래스가 아니라고 생각할 수 있습니다. 다른 행동이 바람직하다.

따라서 SystemDataHelperBuilder분기에 명시 적으로 (예 : 유형에 따라 분기를 잡는 예외와 같이) 인스턴스를 확인하는 코드가 있거나 아마도 일반적인 코드가 있기 때문에 그의 코드가 좋은 아이디어가 될 수 있다고 말하고 싶습니다. SystemDataHelperBuilder로깅하는 경우에도 유용한 방법으로 이름 을 사용합니다 .

만약 때문에, 특별한 경우를 사용하는 유형, 혼란으로 이어질 않습니다 ImmutableRectangle(1,1)ImmutableSquare(1)다르게 행동 어떤 식 으로든 아마 그것을하지 않았다하고자하는 이유를 다음 결국 누군가가 궁금해 것입니다. 예외 계층 구조와는 달리, 당신은 사각형 여부를 확인하여 사각형 여부를 확인 height == width하지로가 instanceof. 귀하의 예 에서 정확히 동일한 인수 SystemDataHelperBuilderDataHelperBuilder작성된 a 와 a 사이에 차이 가 있으며 문제가 발생할 수 있습니다. 따라서 "올바른"인수로 작성된 객체를 반환하는 함수는 일반적으로 옳은 일입니다. 서브 클래스는 "동일한"것의 두 가지 다른 표현을 생성하며, 사용자가 무언가를 수행하는 경우에만 도움이됩니다. 일반적으로하지 않으려 고합니다.

참고 난 것을 하지 내가 프로그래머가 지금까지 생각했다고 모든 좋고 나쁜 아이디어의 분류를 제공 할 수 있다고 생각하지 않기 때문에, 디자인 패턴과 안티 패턴의 관점에서 이야기. 따라서 모든 아이디어가 패턴이나 반 패턴이 아니라는 것은 누군가가 다른 상황에서 반복적으로 발생하는 것을 식별하고 그 이름을 명명 할 때만된다.


가능한 경우 내용을 렌더링하도록 ImmutableSquareMatrix:ImmutableMatrix요청하는 효율적인 방법이 있다면의 용도를 볼 수 있습니다. 해도는 기본적으로이었다 누구의 생성자 그 높이와 폭 일치, 필요한 방법을 간단하게 (오히려 예를 들어 건설보다 자체를 반환 같은 보조 배열을 감싸는가), 이러한 디자인은 사각 필요 메소드 컴파일시 매개 변수 유효성 검사를 허용 것 행렬ImmutableMatrixImmutableSquareMatrixImmutableSquareMatrixImmutableMatrixAsSquareMatrixImmutableSquareMatrix
supercat

@ supercat : 좋은 지적, 질문자 예제에서 전달 해야하는 함수가 있고 SystemDataHelperBuilder단순히 DataHelperBuilder수행하지 않는 함수 가있는 경우 그 유형을 정의하는 것이 좋습니다 (DI를 그런 식으로 처리한다고 가정하면 인터페이스) . 귀하의 예에서, 정사각형 행렬이 비 결정적이지 않은 결정 요인과 같은 몇 가지 작업을 수행 할 수 있지만 시스템에 유형별로 확인하려는 작업이 필요하지 않은 경우에도 좋습니다 .
Steve Jessop

... 그래서 나는 그렇지 않은 가정 전체가 "당신이 사각형 여부를 확인하여 사각형인지 여부를 확인하는 것이, 내가 말한 사실 height == width,하지로가 instanceof." 정적으로 주장 할 수있는 것을 선호 할 수 있습니다.
Steve Jessop

생성자 구문과 같은 것이 정적 팩토리 메소드를 호출 할 수있는 메커니즘이 있기를 바랍니다. 표현의 유형은 또는 foo=new ImmutableMatrix(someReadableMatrix)그것의 유형 보다 명확 하지만, 후자의 형태는 전자가 부족한 유용한 의미 론적 가능성을 제공한다. 생성자가 행렬이 정사각형이라고 말할 수 있으면 새 객체 인스턴스를 만들기 위해 정사각 행렬이 필요한 다운 스트림 코드를 요구하는 것보다 유형을 반영하여 유형을 반영 할 수 있습니다. someReadableMatrix.AsImmutable()ImmutableMatrix.Create(someReadableMatrix)
supercat

@ supercat : 내가 좋아하는 파이썬에 관한 한 가지 작은 것은 new연산자 가 없다는 것입니다 . 클래스는 호출 가능하며 호출되면 기본적으로 자체 인스턴스를 반환합니다. 따라서 인터페이스 일관성을 유지하려면 이름이 대문자로 시작하는 팩토리 함수를 작성하는 것이 완벽하게 가능하므로 생성자 호출처럼 보입니다. 공장 기능으로 일상적으로 수행하는 것이 아니라 필요할 때 제공됩니다.
Steve Jessop

2

서브 클래스 관계의 가장 엄격한 객체 지향 정의를«is-a»라고합니다. 사각형과 사각형의 전통적인 예를 사용하여 사각형«is-a»사각형.

이를 통해«is-a»가 코드와 의미있는 관계라고 생각되는 상황에서는 서브 클래 싱을 사용해야합니다.

생성자 외에 아무것도없는 서브 클래스의 경우«is-a»관점에서 분석해야합니다. SystemDataHelperBuilder«is-a»DataHelperBuilder임을 알 수 있으므로 첫 번째 단계에서는 올바른 사용임을 나타냅니다.

다른 코드가이«is-a»관계를 활용하는지 여부는 대답해야합니다. 다른 코드는 SystemDataHelperBuilders와 DataHelperBuilders의 일반 인구를 구별하려고 시도합니까? 그렇다면 관계가 상당히 합리적이므로 하위 클래스를 유지해야합니다.

그러나 다른 코드에서이 작업을 수행하지 않으면«is-a»관계이거나 팩토리로 구현 될 수있는 relationshp입니다. 이 경우 어느 쪽이든 갈 수 있지만«is-a»관계보다는 Factory를 권장합니다. 다른 개인이 작성한 객체 지향 코드를 분석 할 때 클래스 계층 구조는 주요 정보 소스입니다. 코드를 이해하는 데 필요한«is-a»관계를 제공합니다. 따라서이 동작을 서브 클래스에 넣으면 "슈퍼 클래스의 메소드를 파헤치는 사람이 찾을 수있는 것"에서 "코드에 들어가기 전에 모든 사람이 살펴볼 수있는 것"까지의 동작이 촉진됩니다. 귀도 반 로섬 (Guido van Rossum)은 "코드는 코드보다 더 자주 읽습니다" 다가오는 개발자를 위해 코드의 가독성을 낮추는 것은 지불하기 힘든 가격입니다. 공장을 대신 사용할 수 있다면 지불하지 않기로 결정했습니다.


1

하위 유형의 인스턴스를 항상 하위 유형의 인스턴스로 교체하고 모든 것이 여전히 올바르게 작동하는 한 하위 유형은 "잘못"되지 않습니다. 서브 클래스가 슈퍼 타입이 보증하는 것을 약화시키지 않는 한 체크 아웃됩니다. 그것은 더 강력하고 (보다 구체적인) 보장을 할 수 있으므로 그런 의미에서 동료의 직관이 정확합니다.

당신이 만든 서브 클래스는 아마도 틀리지 않을 것입니다. (내가하는 일을 정확히 알지 못하기 때문에 확실하게 말할 수는 없습니다.) 깨지기 쉬운 기본 클래스 문제에주의해야합니다. 의 interface혜택이 있는지 여부를 고려 하십시오. 그러나 그것은 상속의 모든 용도에 해당됩니다.


1

이 질문에 대답하는 열쇠는이 특정 사용 시나리오를 보는 것입니다.

상속은 연결 문자열과 같은 데이터베이스 구성 옵션을 시행하는 데 사용됩니다.

이것은 클래스가 Single Responsibility Principal을 위반하기 때문에 반 패턴입니다. --- "한 가지 일을하고 잘 수행하지 않는"구성의 특정 소스로 자체 구성됩니다. 클래스 구성을 클래스 자체에 구워서는 안됩니다. 데이터베이스 연결 문자열을 설정하기 위해 대부분의 경우 응용 프로그램이 시작될 때 발생하는 일종의 이벤트 중에 응용 프로그램 수준에서 발생합니다.


1

나는 다른 개념을 표현하기 위해 생성자 서브 클래스를 아주 정기적으로 사용합니다.

예를 들어 다음 클래스를 고려하십시오.

public class Outputter
{
    private readonly IOutputMethod outputMethod;
    private readonly IDataMassager dataMassager;

    public Outputter(IOutputMethod outputMethod, IDataMassager dataMassager)
    {
        this.outputMethod = outputMethod;
        this.dataMassager = dataMassager;
    }

    public MassageDataAndOutput(string data)
    {
        this.outputMethod.Output(this.dataMassager.Massage(data));
    }
}

그런 다음 하위 클래스를 만들 수 있습니다.

public class CapitalizeAndPrintOutputter : Outputter
{
    public CapitalizeAndPrintOutputter()
        : base(new PrintOutputMethod(), new Capitalizer())
    {
    }
}

그런 다음 코드의 어느 곳에서나 CapitalizeAndPrintOutputter를 사용할 수 있으며 어떤 유형의 출력기인지 정확히 알 수 있습니다. 또한이 패턴은 실제로 DI 컨테이너를 사용하여이 클래스를 만드는 것이 매우 쉽습니다. DI 컨테이너가 PrintOutputMethod 및 Capitalizer에 대해 알고 있으면 자동으로 CapitalizeAndPrintOutputter를 만들 수도 있습니다.

이 패턴을 사용하는 것은 실제로 매우 유연하고 유용합니다. 예, 팩토리 메소드를 사용하여 동일한 작업을 수행 할 있지만 (적어도 C #에서는) 유형 시스템의 일부 기능을 잃어 버리고 DI 컨테이너를 사용하는 것이 더 번거로워집니다.


1

먼저 코드가 작성 될 때 초기화 된 두 필드가 모두 클라이언트 코드에 의해 설정 될 수 있으므로 서브 클래스는 부모보다 더 구체적이지 않습니다.

서브 클래스의 인스턴스는 다운 캐스트를 통해서만 구별 될 수 있습니다.

이제 서브 클래스 에서 DatabaseEngineand ConnectionString속성을 다시 구현 한 디자인을 사용하여 액세스 한 경우에는 서브 클래스 Configuration를 갖는 것이 더 의미있는 제약 조건이 있습니다.

"반 패턴"은 내 취향에 비해 너무 강하다. 여기에는 정말로 잘못된 것이 없습니다.


0

당신이 제시 한 예에서, 당신의 파트너는 옳습니다. 두 개의 데이터 헬퍼 빌더는 전략입니다. 하나는 다른 것보다 더 구체적이지만 일단 초기화되면 둘 다 교환 가능합니다.

기본적으로 datahelperbuilder의 클라이언트가 systemdatahelperbuilder를 받으면 아무것도 깨지지 않습니다. 또 다른 옵션은 systemdatahelperbuilder를 datahelperbuilder 클래스의 정적 인스턴스로 만드는 것입니다.

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