programing

빌더 패턴 및 다수의 필수 매개 변수

goodcopy 2021. 1. 15. 19:17
반응형

빌더 패턴 및 다수의 필수 매개 변수


지금까지 다음과 같은 빌더 패턴 구현을 사용했습니다 ( 여기에 설명 된 구현과 반대 ).

public class Widget {
    public static class Builder {
        public Builder(String name, double price) { ... }
        public Widget build() { ... }
        public Builder manufacturer(String value) { ... }
        public Builder serialNumber(String value) { ... }
        public Builder model(String value) { ... }
    }

    private Widget(Builder builder) { ... }
}

이것은 다양한 필수 / 필수 및 선택적 매개 변수를 사용하여 복잡한 객체를 구축해야하는 대부분의 상황에서 잘 작동합니다. 그러나 최근에 모든 매개 변수가 필수 (또는 적어도 대다수가 필수) 인 경우 패턴이 어떤 이점이 있는지 이해하기 위해 노력하고 있습니다.

이 문제를 해결하는 한 가지 방법은 빌더 생성자에 전달되는 매개 변수 수를 줄이기 위해 자체 클래스에 전달되는 매개 변수를 논리적으로 그룹화하는 것입니다.

예를 들어 다음 대신 :

Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
                           .addOptional(opt9)
                           .build();

다음과 같이 그룹화됩니다.

Object1 group1 = new Object1(req1, req2, req3, req4);
Object2 group2 = new Object2(req5, req6);

Widget example2 = new Widget.Builder(group1, group2, req7, req8)
                            .addOptional(opt9)
                            .build();

별도의 개체를 사용하면 작업이 상당히 단순화되지만 코드에 익숙하지 않은 경우 작업을 수행하기가 조금 어려워집니다. 내가 고려한 한 가지는 모든 매개 변수를 자체 addParam(param)메서드 로 이동 한 다음 메서드에서 필요한 매개 변수에 대한 유효성 검사를 수행하는 build()것입니다.

모범 사례는 무엇이며 내가 고려하지 않은 더 나은 접근 방식이 있습니까?


그러나 최근에 모든 매개 변수가 필수 (또는 적어도 대다수가 필수) 인 경우 패턴이 어떤 이점이 있는지 이해하기 위해 노력하고 있습니다.

유창한 빌더 패턴은 여전히 ​​유용합니다.

  1. 더 가독성-이름이 지정된 매개 변수를 효과적으로 허용하므로 호출이 이름이 지정되지 않은 인수의 긴 목록이 아닙니다.

  2. 순서가 지정되지 않음-단일 빌더 setter 호출의 일부로 또는 단순히이 특정 인스턴스화를 가장 잘 이해할 수있는 빌더 setter 메소드를 호출하는 데 자연스러운 순서를 사용하여 인수를 논리적 그룹으로 그룹화 할 수 있습니다.


Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
                               .addOptional(opt9)
                               .build();

다음과 같이 그룹화됩니다.

Object1 group1  = new Object1(req1, req2, req3, req4);
Object2 group2  = new Object2(req5, req6);
Widget example2 = new Widget.Builder(group1, group2, req7, req8)
                            .addOptional(opt9)
                            .build();

별도의 개체를 사용하면 작업이 상당히 단순화되지만 코드에 익숙하지 않은 경우 작업을 수행하기가 조금 어려워집니다. 내가 고려한 한 가지는 모든 매개 변수를 자체 addParam(param)메서드 로 이동 한 다음 메서드에서 필요한 매개 변수에 대한 유효성 검사를 수행하는 build()것입니다.

나는 적절하거나 자연 스러울 때 하이브리드를 선호합니다. 그것은 생성자에서 모든 일 필요는 없습니다 또는 각 PARAM은 자신의 addParam 방법이있다. Builder는 하나, 다른, 중간 또는 콤보를 수행 할 수있는 유연성을 제공합니다.

Widget.Builder builder = new Widget.Builder(Widget.BUTTON);

builder.withWidgetBackingService(url, resource, id);
builder.withWidgetStyle(bgColor, lineWidth, fontStyle);
builder.withMouseover("Not required");

Widget example = builder.build();

필수 매개 변수가 많은 경우 단계 빌더를 사용할 수 있습니다 . 간단히 말해서, 모든 단일 필수 매개 변수에 대한 인터페이스를 정의하고 빌더 메소드는 다음 필수 빌더 인터페이스 또는 선택적 메소드에 대한 빌더 자체를 리턴합니다. 빌더는 모든 인터페이스를 구현하는 단일 클래스로 유지됩니다.

Kotlin 및 Scala와 같은 언어는 기본값과 함께 명명 된 매개 변수를 제공하므로 여기서 더 편리합니다.


최근에 모든 매개 변수가 필수 인 경우 패턴이 어떤 이점이 있는지 이해하기 위해 애 쓰고 있습니다.

이 패턴은 불변 클래스의 생성을 용이하게하고 읽기 쉬운 코드를 촉진합니다. 아래의 Person 클래스를 고려하십시오 (일반적인 생성자와 빌더 사용).

public static class Person {

    private static final class Builder {
        private int height, weight, age, income, rank;
        public Builder setHeight(final int height) { this.height = height; return this; }
        public Builder setWeight(final int weight) { this.weight = weight; return this; }
        public Builder setAge(final int age) { this.age = age; return this; }
        public Builder setIncome(final int income) {    this.income = income; return this; }
        public Builder setRank(final int rank) { this.rank = rank; return this; }
        public Person build() { return new Person(this); }
    }

    private final int height;
    private final int weight;
    private final int age;
    private final int income;
    private final int rank;

    public Person(final int height, final int weight, final int age, final int income, final int rank) {
        this.height = height; this.weight = weight; this.age = age; this.income = income; this.rank = rank;
    }

    private Person(final Builder builder) {
        height = builder.height; weight = builder.weight; age = builder.age; income = builder.income; rank = builder.rank;
        // Perform validation
    }

    public int getHeight() { return height; }
    public int getWeight() { return weight; }
    public int getAge() { return age; }
    public int getIncome() { return income; }
    public int getRank() {  return rank; }

}

어떤 건설 방법이 이해하기 더 쉬운가요?

final Person p1 = new Person(163, 184, 48, 15000, 23);
final Person p2 = new Person.Builder().setHeight(163).setWeight(184).setAge(48).
    setIncome(15000).setRank(23).build();

이 문제를 해결하는 한 가지 방법은 자신의 클래스에 전달되는 매개 변수를 논리적으로 그룹화하는 것입니다.

물론 이것은 응집 의 원리 이며 객체 구성 의미에 관계없이 채택되어야합니다.


내가 거의 (적어도) 승격되지 않는 빌더 패턴의 한 가지 장점은 모든 필수 매개 변수가 정확하거나 다른 필수 리소스를 사용할 수있는 경우에만 객체를 조건부로 구성하는 데 사용할 수도 있다는 것입니다. 그런 점에서 정적 팩토리 방법 과 유사한 이점을 제공합니다 .


A builder/factory still lets you decouple interface from implementation type (or lets you plug in an adapter, etc), assuming Widget becomes an interface and you have a way to inject or hide the new Widget.Builder.

If you don't care about decoupling, and your implementation is a one-off, then you're right: the builder pattern isn't much more useful than a normal constructor (it still labels its arguments with the attribute-per-builder-method style.)

If you are repeatedly creating objects with little variation in the arguments, then it may still be helpful. You could pass in, cache, etc the intermediate builder acquired after plugging in several attributes:

Widget.Builder base = new Widget.Builder(name, price).model("foo").manufacturer("baz");

// ...

Widget w1 = base.serialNumber("bar").build();
Widget w2 = base.serialNumber("baz").build();
Widget w3 = base.serialNumber("quux").build();

This assumes that your builders are immutable: builder setters don't set an attribute and returning this, but return a new copy of themselves with the change instead. As you pointed out above, parameter objects are another way to get around repeated argument boilerplate. There, you don't even need the builder pattern: just pass the parameter object to your implementation constructor.

ReferenceURL : https://stackoverflow.com/questions/7302891/the-builder-pattern-and-a-large-number-of-mandatory-parameters

반응형