태그 보관물: design-patterns

design-patterns

체인 세터가 왜 전통적인가? public String getBar() {

Bean에 체인을 구현하면 생성자, 메가 생성자, 팩토리를 오버로드 할 필요가없고 가독성이 향상됩니다. 객체를 불변으로 만들고 싶지 않다면 어떤 단점도 생각할 수 없습니다 .이 경우 어쨌든 setter가 없습니다. 그렇다면 이것이 OOP 규칙이 아닌 이유가 있습니까?

public class DTO {

    private String foo;
    private String bar;

    public String getFoo() {
         return foo;
    }

    public String getBar() {
        return bar;
    }

    public DTO setFoo(String foo) {
        this.foo = foo;
        return this;
    }

    public DTO setBar(String bar) {
        this.bar = bar;
        return this;
    }

}

//...//

DTO dto = new DTO().setFoo("foo").setBar("bar");


답변

이것이 왜 OOP 규칙이 아닌 이유가 있습니까?

내 최선의 추측 : CQS를 위반하기 때문에

명령 (객체 상태 변경)과 쿼리 (이 경우 객체 자체를 반환하는 상태의 복사본을 반환)와 동일한 메소드가 혼합되어 있습니다. 반드시 문제는 아니지만 일부 기본 지침을 위반합니다.

예를 들어 C ++에서 std :: stack :: pop ()은 void를 반환하는 명령이고 std :: stack :: top ()은 스택의 최상위 요소에 대한 참조를 반환하는 쿼리입니다. 고전, 두 결합하고 싶지만, 당신은 할 수 없습니다 예외 안전합니다. (Java의 할당 연산자가 발생하지 않기 때문에 Java에서는 문제가되지 않습니다).

DTO가 가치 유형 인 경우와 비슷한 결과를 얻을 수 있습니다.

public DTO setFoo(String foo) {
    return new DTO(foo, this.bar);
}

public DTO setBar(String bar) {
    return new DTO(this.foo, bar);
}

또한 체인 반환 값은 상속을 처리 할 때 엄청난 고통입니다. “다채롭게 반복되는 템플릿 패턴”을 참조하십시오.

마지막으로, 기본 생성자가 유효한 상태의 객체를 남겨 두어야하는 문제가 있습니다. 개체를 유효한 상태로 복원하기 위해 많은 명령을 실행 해야하는 경우 무언가 잘못되었습니다.


답변

  1. 몇 번의 키 스트로크 저장은 설득력이 없습니다. 좋을지 모르지만 OOP 규칙은 키 입력이 아닌 개념과 구조에 더 관심이 있습니다.

  2. 반환 값은 의미가 없습니다.

  3. 사용자가 반환 값의 의미를 기대할 수 있기 때문에 의미가없는 것보다 반환 값이 잘못 되었습니다. 그들은 그것이 “불변의 세터”라고 기대할 수있다

    public FooHolder {
        public FooHolder withFoo(int foo) {
            /* return a modified COPY of this FooHolder instance */
        }
    }
    

    실제로 세터는 객체를 변경합니다.

  4. 상속에서는 잘 작동하지 않습니다.

    public FooHolder {
        public FooHolder setFoo(int foo) {
            ...
        }
    }
    
    public BarHolder extends FooHolder {
        public FooHolder setBar(int bar) {
            ...
        }
    } 
    

    난 쓸수있다

    new BarHolder().setBar(2).setFoo(1)

    하지만

    new BarHolder().setFoo(1).setBar(2)

저에게는 # 1에서 # 3까지가 중요한 것들입니다. 잘 작성된 코드는 유쾌하게 배열 된 텍스트에 관한 것이 아닙니다. 잘 작성된 코드는 기본 개념, 관계 및 구조에 관한 것입니다. 텍스트는 코드의 진정한 의미를 외적으로 반영한 것입니다.


답변

나는 이것이 OOP 규칙이라고 생각하지 않으며, 언어 디자인 및 그 규칙과 관련이 있습니다.

Java를 사용하는 것 같습니다. Java에는 setter의 리턴 유형을 무효화하도록 지정 하는 JavaBeans 스펙이 있습니다. 즉 setter의 체인과 충돌합니다. 이 사양은 다양한 도구로 널리 수용되고 구현됩니다.

물론 사양의 일부가 체인이 아닌 이유를 물을 수 있습니다. 나는 대답을 모른다. 아마도이 패턴은 당시에 알려지지 않았거나 인기가 없었습니다.


답변

다른 사람들이 말했듯이, 이것은 종종 유창한 인터페이스 라고합니다 .

일반적으로 setter는 응용 프로그램 의 논리 코드에 대한 응답으로 변수 를 전달하는 호출입니다 . DTO 수업이 이에 대한 예입니다. setter가 아무것도 반환하지 않을 때의 기존 코드는 정상적입니다. 다른 답변은 방법을 설명했습니다.

그러나 유창한 인터페이스가 좋은 솔루션 일 수 있는 몇 가지 경우가 있습니다.

  • 상수는 주로 세터에게 전달됩니다.
  • 프로그램 로직은 세터에 전달되는 내용을 변경하지 않습니다.

fluent-nhibernate 와 같은 구성 설정

Id(x => x.Id);
Map(x => x.Name)
   .Length(16)
   .Not.Nullable();
HasMany(x => x.Staff)
   .Inverse()
   .Cascade.All();
HasManyToMany(x => x.Products)
   .Cascade.All()
   .Table("StoreProduct");

특수 TestDataBulderClasses (객체 어머니)를 사용하여 단위 테스트에서 테스트 데이터 설정

members = MemberBuilder.CreateList(4)
    .TheFirst(1).With(b => b.WithFirstName("Rob"))
    .TheNext(2).With(b => b.WithFirstName("Poya"))
    .TheNext(1).With(b => b.WithFirstName("Matt"))
    .BuildList(); // Note the "build" method sets everything else to
                  // senible default values so a test only need to define
                  // what it care about, even if for example a member
                  // MUST have MembershipId  set

그러나 유창한 인터페이스를 만드는 것은 매우 어렵 기 때문에 “정적”설정이 많은 경우에만 유용합니다. 또한 유창한 인터페이스는 “정상”클래스와 혼용해서는 안됩니다. 따라서 빌더 패턴이 자주 사용됩니다.


답변

나는 하나의 세터를 다른 체인으로 연결하는 것이 관례가 아닌 많은 이유를 생각합니다.이 경우 생성자에서 옵션 객체 또는 매개 변수를 보는 것이 더 일반적이기 때문입니다. C #에는 초기화 구문도 있습니다.

대신에:

DTO dto = new DTO().setFoo("foo").setBar("bar");

하나는 쓸 수 있습니다 :

(JS에서)

var dto = new DTO({foo: "foo", bar: "bar"});

(C #에서)

DTO dto = new DTO{Foo = "foo", Bar = "bar"};

(자바)

DTO dto = new DTO("foo", "bar");

setFoo그리고 setBar다음 더 이상 초기화 필요하지 않습니다, 나중에 돌연변이에 사용할 수 있습니다.

체인 가능성은 일부 상황에서 유용하지만 줄 바꿈 문자를 줄이기 위해 모든 것을 한 줄로 채우지 않는 것이 중요합니다.

예를 들어

dto.setFoo("foo").setBar("fizz").setFizz("bar").setBuzz("buzz");

무슨 일이 일어나고 있는지 이해하고 이해하기가 어렵습니다. 다음으로 다시 포맷 :

dto.setFoo("foo")
    .setBar("fizz")
    .setFizz("bar")
    .setBuzz("buzz");

이해하기가 훨씬 쉽고 첫 번째 버전의 “실수”를보다 분명하게 만듭니다. 코드를 해당 형식으로 리팩토링하면 다음과 같은 이점이 없습니다.

dto.setFoo("foo");
dto.setBar("bar");
dto.setFizz("fizz");
dto.setBuzz("buzz");

답변

이 기술은 실제로 빌더 패턴에서 사용됩니다.

x = ObjectBuilder()
        .foo(5)
        .bar(6);

그러나 일반적으로 모호하기 때문에 피해야합니다. 반환 값이 객체인지 (따라서 다른 설정자를 호출 할 수 있는지) 또는 반환 객체가 방금 할당 된 값인지 (공통 패턴 임) 확실하지 않습니다. 따라서 최소한의 서프라이즈 원칙은 객체 디자인의 기본이 아니라면 사용자가 하나의 솔루션 또는 다른 솔루션을 보길 원한다고 가정해서는 안된다고 제안합니다.


답변

이것은 답변보다 더 많은 의견이지만 의견을 말할 수는 없습니다.

나는이 질문이 전혀 드문 것으로 보지 않기 때문에이 질문이 나를 놀라게했다고 언급하고 싶었습니다 . 사실, 제 작업 환경 (웹 개발자)은 매우 일반적입니다.

생성 : 예를 들어,이 심포니의 교리는 어떻게 개체가 자동 생성 명령 을 모두 setters , 기본적으로 .

jQuery를이 매우 유사한 방식으로 체인 메서드의 대부분을.