유형이 빌더와 결합 된 이유는 무엇입니까?


20

최근 에 Code Review 에서 내 답변을 삭제했습니다 .

private Person(PersonBuilder builder) {

중지. 빨간 깃발. PersonBuilder는 Person을 빌드합니다. 그것은 사람에 대해 알고 있습니다. Person 클래스는 PersonBuilder에 대해 아무 것도 알 수 없습니다. 불변 유형입니다. 여기에 원형 커플 링을 만들었습니다. 여기서 A는 A에 의존하고 B는 A에 의존합니다.

사람은 그 매개 변수를 섭취해야합니다. 사람을 만들지 않고 기꺼이 만들려는 고객은 그렇게 할 수 있어야합니다.

나는 공감대를 때렸고 레드 플래그를 말한 이유는 무엇입니까? 이 구현은 Joshua Bloch의 "Effective Java"책 (항목 # 2)에서 설명한 것과 동일한 모양을 갖습니다.

따라서 Java에서 빌더 패턴 을 구현하는 올바른 방법은 빌더를 중첩 유형 (이 질문은 아니지만)으로 만든 다음 제품 (작성중인 객체의 클래스) 을 만드는 것입니다 ) 다음과 같이 빌더에 대한 종속성을 가져옵니다 .

private StreetMap(Builder builder) {
    // Required parameters
    origin      = builder.origin;
    destination = builder.destination;

    // Optional parameters
    waterColor         = builder.waterColor;
    landColor          = builder.landColor;
    highTrafficColor   = builder.highTrafficColor;
    mediumTrafficColor = builder.mediumTrafficColor;
    lowTrafficColor    = builder.lowTrafficColor;
}

https://en.wikipedia.org/wiki/Builder_pattern#Java_example

동일한 빌더 패턴에 대한 동일한 Wikipedia 페이지에는 C #에 대해 매우 다른 (그리고 훨씬 더 유연한) 구현이 있습니다.

//Represents a product created by the builder
public class Car
{
    public Car()
    {
    }

    public int Wheels { get; set; }

    public string Colour { get; set; }
}

보시다시피, 여기에 있는 제품Builder클래스 에 대해 아무것도 알지 못하며 직접 생성자 호출, 추상 팩토리 또는 ... 또는 빌더로 인스턴스화 할 수 있습니다. 내가 이해하는 한 창조 패턴의 산물 은 그것을 창조하는 것에 대해 아무것도 알 필요가 없습니다 .

필자는 Bloch의 책에서 명백하게 방어 된 반론을 받았고 빌더 패턴을 사용하여 생성자가 수십 개의 선택적 인수로 부풀린 유형을 재 작업하는 데 사용할 수 있다고했습니다. 그래서 그 대신 내가 무엇을 엄선 생각 나는이 사이트에 약간의 연구 알고, 내가 의심으로, 발견 이 인수하지 않는 물을 개최 .

그래서 거래는 무엇입니까? 처음에는 존재하지 않아야하는 문제에 대해 과도하게 엔지니어링 된 솔루션 을 제안하는 이유는 무엇 입니까? Joshua Bloch를 1 분 동안 받침대에서 떼어 내면 두 가지 콘크리트 유형을 결합하고 모범 사례라고 부르는 한 가지 좋은 정당한 이유를 생각 해낼 수 있습니까?

이것은 나에게 화물 컬트 프로그래밍 의 모든 악취 .


12
그리고이 "질문"은 그저 화를내는 경향이 있습니다.
Philip Kendall

2
@PhilipKendall 조금있을 것입니다. 그러나 모든 Java 빌더 구현에 왜 엄격한 결합이 있는지 이해하는 데 진심으로 관심이 있습니다.
Mathieu Guindon

19
@PhilipKendall 그것은 나에게 호기심이 있습니다.
돛대

8
@PhilipKendall 그것은 진창처럼 읽습니다. 그러나 핵심에는 유효한 주제에 관한 질문이 있습니다. 아마도 그 진창이 편집 될 수 있을까요?
Andres F.

"너무 많은 생성자 인수"인수에 감명받지 않았습니다. 그러나 나는이 두 빌더 예제 모두에 대해 덜 인상적입니다. 아마도 예제는 너무 단순하지 않은 동작을 보여주기에는 너무 간단하지만 Java 예제는 복잡한 유창한 인터페이스 처럼 읽히고 C # 예제는 속성을 구현하는 간단한 방법입니다. 어느 예제도 어떤 형태의 매개 변수 유효성 검사도 수행하지 않습니다. 생성자는 최소한 제공된 인수의 유형과 수를 확인합니다. 이는 너무 많은 인수를 가진 생성자가 승리 함을 의미합니다.
Robert Harvey

답변:


22

나는 그것이 붉은 깃발이라는 당신의 주장에 동의하지 않습니다. 또한 빌더 패턴을 표현하는 올바른 방법이 있다는 점에서 반대 점에 대한 귀하의 의견에 동의하지 않습니다.

나에게 한 가지 질문이 있습니다 : 빌더가 유형 API의 필요한 부분입니까? 여기, PersonBuilder가 Person API의 필수 부분입니까?

유효성 체크 - 불변 및 / 또는 단단하게 캡슐화 Person 클래스는 것을 전적으로 합리적인 반드시 (빌더 관계없이 중첩 또는 인접한 여부)가 제공 빌더를 통해 생성 될 수있다. 그렇게함으로써, 개인은 모든 필드를 개인 또는 패키지 개인 및 최종으로 유지하고 진행중인 작업 인 경우 수정을 위해 클래스를 열어 둘 수 있습니다. (그것은 수 있습니다 결국 수정을 위해 폐쇄 및 확장 오픈,하지만 그건 중요하지 권리를 지금, 불변의 데이터 객체에 특히 논쟁의 여지가있다.)

단일 패키지 레벨 API 디자인의 일부로 제공된 PersonBuilder를 통해 Person을 작성해야하는 경우에는 원형 커플 링만으로도 충분하며 여기에는 적신호가 보장되지 않습니다. 특히 API 설계 의견이나 선택이 아니라 논란의 여지가없는 사실이라고 말했기 때문에 잘못된 응답에 기여했을 수 있습니다. 난 당신을 downvoted하지 않았지만, 나는 그렇게하는 다른 사람을 비난하지 않습니다, 나는 Code Review 도움말 센터메타에 "공개를 보증하는 것"에 대한 나머지 토론을 남길 것 입니다. Downvotes가 발생합니다. "때리고"없습니다.

물론 Person 객체를 만들기 위해 많은 방법을 열어두고 싶다면 PersonBuilder가 소비자 또는 유틸리티 가 될 수 있습니다.Person 클래스에 대해 직접 의존해서는 안됩니다. 더 많은 객체 생성 옵션을 원하지 않는이 선택은 더 유연 해 보이지만 (불변성 또는 직렬화 가능성과 같은) 보장을 유지하기 위해 개인은 매우 긴 생성자와 같은 다른 종류의 공개 생성 기술을 노출해야합니다. . 빌더가 생성자를 많은 선택적 필드 또는 수정자가 열려있는 생성자로 나타내거나 대체하려는 경우 클래스의 긴 생성자가 구현 세부 사항을 숨기면 더 나을 수 있습니다. (공식 및 필요한 빌더는 자신의 건설 유틸리티 작성을 방해하지 않으며, 건설 유틸리티가 위의 원래 질문에서와 같이 빌더를 API로 사용할 수 있음을 명심하십시오.)


추신 : 귀하가 나열된 코드 검토 샘플에는 불변의 Person이 있지만 Wikipedia의 반례에는 게터와 세터가있는 변이 가능한 자동차가 나열되어 있습니다. 언어와 불변 값을 일관되게 유지하면 필요한 기계를 자동차에서 생략하는 것이 더 쉬울 수 있습니다.


생성자를 구현 세부 사항으로 취급하는 것에 대한 요점을 생각합니다. Effective Java 가이 메시지를 제대로 전달하는 것처럼 보이지 않습니다 (저는 그 책을 읽지 못했거나 읽지 않았습니다). . 나는 Wikipedia 예제들이 비교하기에는 좋지 않다는 것에 동의한다. 그러나이 Wikipedia이다. 그리고 FWIW 나는 실제로 downvote를 신경 쓰지 않는다. 삭제 된 답변은 어쨌든 긍정적 인 순 점수로 앉아 있습니다 (그리고 마침내 [배지 : 피어 압력]을 얻을 수있는 기회가 있습니다).
Mathieu Guindon

1
그러나 너무 많은 생성자 인수가 있는 유형 이 설계 문제 (SRP가 깨지는 냄새)이며 빌더 패턴이 카펫 아래에서 신속하게 밀려 나간다 는 생각을 떨칠 수 없습니다 .
Mathieu Guindon

3
@ Mat'sMug 위의 게시물 은 클래스에 많은 생성자 인수가 필요하다는 점을 감안할 때이 게시물을 가져옵니다 . 임의의 비즈니스 로직 구성 요소에 대해 이야기하고 있다면 디자인 냄새로 취급하고 싶지만 데이터 개체의 경우 유형이 10 ​​개 또는 20 개의 직접 속성을 갖는 것은 문제가되지 않습니다. 예를 들어, 객체가 로그에 강력한 형식단일 레코드 를 나타내는 것은 SRP 위반이 아닙니다 .
Jeff Bowman은 Monica

1
충분합니다. 그래도 여전히 String(StringBuilder)생성자를 얻지 못합니다. 이는 생성자에 대한 종속성을 갖는 것이 일반적인 "원칙"을 따르는 것 같습니다. 그 생성자 과부하를 보았을 때 나는 화를 냈다. String42 개의 생성자 매개 변수가 없습니다 ...
Mathieu Guindon

3
@Luaan Odd. 나는 문자 그대로 그것이 사용되는 것을 보지 못했고 그것이 존재하는지 몰랐다. toString()보편적입니다.
chrylis

7

나는 '권한에 대한 호소'가 토론에 사용될 때 그것이 얼마나 성 가실 수 있는지 이해합니다. 인수는 자체 IMO에 있어야하며 존경받는 사람의 조언을 가리키는 것은 잘못된 것이 아니지만, 아리스토텔레스가 말했기 때문에 태양이 지구를 여행한다는 것을 알면 실제로 그 자체로 완전한 주장으로 간주 될 수는 없습니다. .

나는 당신의 주장의 전제가 같은 것이라고 생각합니다. 우리는 절대 두 가지 유형의 콘크리트를 결합해서는 안되며 최선의 방법이라고 부를 수 없습니다.

커플 링에 문제가 있다는 주장은 유효하기 때문에 특정 문제가 발생해야합니다. 이 접근 방식이 이러한 문제에 영향을받지 않으면 이러한 형태의 커플 링에 문제가 있다고 논리적으로 주장 할 수 없습니다. 따라서 여기에서 지적하고 싶다면이 접근법이 어떻게 이러한 문제에 영향을 받는지 및 / 또는 빌더를 분리하여 설계를 개선하는 방법을 보여 주어야한다고 생각합니다.

한편, 결합 된 접근 방식으로 문제가 발생하는 방식을 파악하기 위해 고심하고 있습니다. 클래스는 완전히 결합되어 있지만 빌더는 클래스 인스턴스를 작성하기 위해서만 존재합니다. 그것을 보는 또 다른 방법은 생성자의 확장입니다.


그래서 ... 높은 결합, 낮은 응집력 => 행복한 결말? 흠 ... 난,하지만 또 다른 예를 가져올에, 내가 무엇을보고 실패 "생성자 확장"빌더에 대한 포인트 참조 String복용하는 생성자 과부하로하고있다 StringBuilder(매개 변수를 그것의 논쟁의 여지가 여부 있지만 StringBuilderA는 빌더 만의 그 다른 이야기).
Mathieu Guindon

4
@ Mat'sMug 분명히, 나는 이것에 대해 강한 느낌을 가지고 있지 않습니다. 상태 입력이 많은 클래스를 실제로 만들지 않기 때문에 빌더 패턴을 사용하지 않습니다. 나는 일반적으로 다른 곳에서 암시하는 이유로 그러한 클래스를 분해합니다. 내가 말하는 것은이 접근법이 어떻게 문제를 일으키는 지 보여줄 수 있다면, 하나님이 내려 와서 그 건축 양식을 돌판 위에 모세에게 건네주는 것은 중요하지 않다는 것입니다. 네가 이겼다'. 반면에 당신이 할 수 없다면 ...
JimmyJames

내가 작성한 코드 기반의 빌더 패턴을 합법적으로 사용하는 것은 VBIDE API를 조롱하는 것입니다. 기본적으로 코드 모듈의 문자열 컨텐츠를 제공하고 빌더는 VBE창, 활성 코드 분할 창, 프로젝트 및 사용자가 제공 한 문자열을 포함하는 모듈이 포함 된 컴포넌트로 구성된 모의를 제공합니다. 그것 없이는 적어도 내가 볼 때마다 눈 을 찌르지 않고 Rubberduck 에서 테스트의 80 %를 어떻게 쓸 수 있을지 모르겠습니다 .
Mathieu Guindon

@ Mat'sMug 데이터를 변경 불가능한 객체로 비 정렬 화하는 데 실제로 유용 할 수 있습니다. 이러한 종류의 객체는 속성의 백인 경향이 있습니다. 문제의 전체 공간은 PITA와 같이 좋은 습관을 따르는 지 여부에 관계없이 수행하는 데 도움이되는 모든 것이 익숙해 질 것입니다.
JimmyJames

4

왜 유형이 빌더와 결합되는지에 대한 답을 얻으려면, 왜 누군가가 빌더를 처음 사용하는지 이해해야합니다. 구체적으로, Bloch는 생성자 매개 변수가 많은 경우 빌더 사용을 권장합니다. 이것이 빌더를 사용해야하는 유일한 이유는 아니지만 가장 일반적이라고 생각합니다. 간단히 말해서 빌더는 해당 생성자 매개 변수를 대체하며 종종 빌더가 빌드하는 동일한 클래스에서 선언됩니다. 따라서 엔 클로징 클래스는 이미 빌더에 대한 지식을 가지고 있으며 생성자 인수로 전달해도 변경되지 않습니다. 이것이 타입이 빌더와 결합되는 이유입니다.

많은 수의 매개 변수가 빌더를 사용해야 함을 의미하는 이유는 무엇입니까? 실제로 많은 매개 변수를 갖는 것이 현실에서는 드문 일이 아니며 단일 책임 원칙을 위반하지도 않습니다. 또한 10 개의 String 매개 변수가 있고 어떤 매개 변수가 어느 매개 변수인지, null이어야하는 다섯 번째 문자열인지 여섯 번째 문자열인지 여부를 기억하려고합니다. 빌더를 사용하면 매개 변수를보다 쉽게 ​​관리 할 수 ​​있으며 책을 읽어야하는 다른 멋진 작업을 수행 할 수도 있습니다.

유형과 결합되지 않은 빌더를 언제 사용합니까? 일반적으로 객체의 구성 요소를 즉시 사용할 수 없으며 해당 부분이있는 객체가 빌드하려는 유형에 대해 알 필요가 없거나 제어 할 수없는 클래스의 빌더를 추가하는 경우 .


홀수, 빌더가 필요한 유일한 시간 은 IDE의 API를 모의하는 이 MockVbeBuilder 와 같이 결정적인 형태가 아닌 유형을 가진 경우 였습니다 . IMO - 하나 개의 테스트는 다른 테스트는 두 가지 형태와 클래스 모듈이 필요 수있는 간단한 코드 모듈을해야 할 수도 있습니다 GoF의의 빌더 패턴에 대한 무엇입니다. 즉, 미친 생성자로 다른 클래스에 사용할 수 있지만 커플 링은 전혀 느끼지 않습니다.
Mathieu Guindon

1
@ Mat 's Mug 제시 한 클래스는 반드시 단순한 빌더 클래스가 아니라 빌더 디자인 패턴 (Gamma 등의 Design Patterns 등)을 대표하는 것으로 보입니다. 둘 다 물건을 만들지 만 같은 것은 아닙니다. 또한 빌더를 "필요"하지 않고 다른 것들을 더 쉽게 만듭니다.
올드 팻 네드

1

창조 패턴의 산물은 그것을 창조하는 것에 대해 아무것도 알 필요가 없습니다.

Person클래스를 생성 무엇을 알 수 없습니다. 매개 변수가있는 생성자 만 있으면 무엇입니까? 매개 변수 유형의 이름은 ... Builder로 끝나며 실제로는 아무 것도 강요하지 않습니다. 결국 이름 일뿐입니다.

빌더는 영광이다 1 개 구성 객체입니다. 그리고 구성 객체는 실제로 서로 속하는 속성을 그룹화하는 것입니다. 그들이 클래스의 생성자에게 전달 될 목적으로 만 서로 속해 있다면, 그렇게하십시오! 모두 origindestinationStreetMap예 유형입니다 Point. 각 점의 개별 좌표를지도에 전달할 수 있습니까? 물론, 그러나 속성의 속성은 Point함께 속해 있으며, 구성에 도움이되는 모든 종류의 메소드를 가질 수 있습니다.

1 차이점은 빌더가 단순한 데이터 오브젝트가 아니라 이러한 체인 설정자 호출을 허용한다는 것입니다. Builder대부분 자신을 구축하는 방법에 관한 것이다.

이제 자세히 살펴보면 빌더가 실제로 명령이기 때문에 약간의 명령 패턴 을 믹스에 추가해 보겠습니다 .

  1. 액션을 캡슐화합니다 : 다른 클래스의 메소드 호출
  2. 해당 조치를 수행하는 데 필요한 정보를 보유합니다. 메소드의 매개 변수 값

빌더의 특별한 부분은 다음과 같습니다.

  1. 호출 할 메소드는 실제로 클래스의 생성자입니다. 이제 그것을 창조 패턴 이라고 부르는 것이 좋습니다.
  2. 매개 변수의 값은 빌더 자체입니다.

Person생성자 에게 전달되는 것은 그것을 만들 수 있지만 반드시 그럴 필요는 없습니다. 평범한 이전 구성 오브젝트 또는 빌더 일 수 있습니다.


0

아니, 그들은 완전히 틀렸고 당신은 절대적으로 옳습니다. 그 빌더 모델은 바보입니다. 생성자 인수가 나오는 Person의 비즈니스는 아닙니다. 빌더는 사용자에게 편의 일 뿐이며 더 이상 없습니다.


4
덕분에 ... 난 확신이 좀 더 확장하면 더 투표를 수집하는 것이 대답은, 그것은 미완성 대답처럼 보인다 =)
마티유 Guindon

예, 나는 +1, 커플 링없이 동일한 보호 기능을 제공하는 빌더에 대한 대안 접근 방식입니다.
Matt freake

3
이에 대한 대응 방법 은 API 의 필수 부분 인 사례를 언급 한 허용 된 답변을 참조하십시오 . 이 답변은 그 주장을 주장하지 않습니다. PersonBuilderPerson
Andres F.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.