Java에서 많은 매개 변수를 사용하여 생성자 관리


105

일부 프로젝트에는 체인을 따라 내려갈 때 더 많은 매개 변수를 추가하는 클래스 계층 구조가 있습니다. 맨 아래에서 일부 클래스는 최대 30 개의 매개 변수를 가질 수 있으며이 중 28 개는 수퍼 생성자로 전달됩니다.

나는 Guice와 같은 것을 통해 자동화 된 DI를 사용하는 것이 좋을 것임을 인정할 것입니다. 그러나 몇 가지 기술적 이유 때문에 이러한 특정 프로젝트는 Java로 제한됩니다.

유형에 따라 인수를 알파벳순으로 구성하는 규칙은 작동하지 않습니다. 유형이 리팩토링되면 (인수 2로 전달한 원이 이제 Shape가 됨) 갑자기 순서가 잘못 될 수 있기 때문입니다.

이 질문은 구체적이고 "그게 당신의 문제라면, 당신은 디자인 수준에서 잘못하고있다"는 비판으로 가득 차있을 수 있지만, 저는 단지 어떤 관점을 찾고 있습니다.

답변:


264

빌더 디자인 패턴이 도움이 될 수 있습니다. 다음 예를 고려하십시오.

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

이렇게하면 다음과 같은 코드를 작성할 수 있습니다.

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

필수 필드를 생략하면 (아마 이름이 필요함) Student 생성자가 예외를 throw하도록 할 수 있습니다. 그리고 어떤 종류의 인수 순서를 추적 할 필요없이 기본 / 선택 인수를 가질 수 있습니다. 이러한 호출의 순서가 동일하게 잘 작동하기 때문입니다.


10
물론 정적 임포트를 사용하면 이러한 "빌더"를 전혀 "보지"않아도됩니다. 예를 들어 빌더를 반환하는 정적 메서드 name (String name)과 학생을 반환하는 Student (StudentBuilder)가있을 수 있습니다. 따라서 Student (name ( "Joe"). age (15) .motto ( "나는 나 자신을 적시다"));
oxbow_lakes

2
@oxbow_lakes : 귀하의 예에서 어떤 클래스에 정적 메서드 이름 (문자열 이름)이 있습니까?
user443854

엄밀히 말하면 Student 클래스를 사용하여 새로운 학생을 구축하는 것이 가능합니다. Student 클래스 내부에 메서드를 추가했는데 제대로 작동했습니다. 이렇게하면 다른 빌더 클래스가 필요하지 않았습니다. 그래도 이것이 바람직한 지 확실하지 않습니다. 다른 (StudentBuilder) 클래스를 사용하여 빌드해야하는 이유가 있습니까?
WVrock 2014

1
@WVrock : 구현에 따라 다릅니다. 내 대답에서 말했듯이 학생 클래스 자체로 이것을 수행하면 아직 초기화되지 않은 필수 필드가있는 경우와 같이 잠재적으로 클래스가 절반 초기화 상태로 남을 수 있습니다.
Eli Courtwright 2014

@EliCourtwright 나는 그것이 선호도 / 코드 디자인에 관한 것이라고 생각한다. 생성자가 예외를 던지는 대신 buildStudent()메서드가 예외를 던지도록했습니다.
WVrock 2014

24

객체 내부에 관련 매개 변수를 캡슐화 할 수 있습니까?

예 : 매개 변수가 다음과 같은 경우


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

대신 다음을 가질 수 있습니다.


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}



8

글쎄, 빌더 패턴을 사용하는 것이 하나의 해결책 이 될 수 있습니다 .

하지만 20 ~ 30 개의 매개 변수에 도달하면 매개 변수간에 높은 관계가 있다고 생각합니다. 따라서 (제안 된대로) 논리적으로 정상적인 데이터 개체로 래핑하는 것이 가장 합리적 일 것입니다. 이러한 방식으로 데이터 개체는 매개 변수 간의 제약 조건의 유효성을 이미 확인할 수 있습니다.

과거의 모든 프로젝트에 대해 너무 많은 매개 변수 (28 개가 아니라 8 개였습니다!)가 너무 많아서 더 나은 데이터 모델을 만들어 코드를 정리할 수있었습니다.


4

Java 1.4로 제한되어 있으므로 DI를 원한다면 Spring 은 매우 적절한 옵션이 될 것입니다. DI는 생성자 매개 변수가 서비스이거나 런타임 중에 변하지 않는 부분에서만 유용합니다.

객체를 생성하는 방법에 대한 변수 옵션을 원한다는 사실로 인해 이러한 모든 생성자가 있다면 Builder 패턴 사용을 진지하게 고려해야합니다.


매개 변수는 대부분 언급 한대로 서비스이므로 DI가 필요합니다. 나는 몇 가지 다른 답변에서 언급 된 Builder 패턴이 내가 기대했던 것과 정확히 일치한다고 생각합니다.
Steve Armstrong

4

가장 좋은 해결책은 생성자에 너무 많은 매개 변수가없는 것입니다. 생성자에서 실제로 필요한 매개 변수 만 객체를 올바르게 초기화하는 데 필요한 매개 변수입니다. 여러 매개 변수가있는 생성자를 가질 수 있지만 최소 매개 변수 만있는 생성자를 가질 수도 있습니다. 추가 생성자는이 간단한 생성자를 호출하고 그 후에 다른 매개 변수를 설정하기 위해 setter를 호출합니다. 이렇게하면 점점 더 많은 매개 변수를 사용하여 체인 문제를 피할 수있을뿐만 아니라 편의 생성자를 가질 수도 있습니다.



1

매개 변수의 수와 상속 계층 구조의 깊이를 줄이기위한 리팩토링은 제가 생각할 수있는 전부입니다. 20 개 매개 변수를 똑바로 유지하는 데 실제로 도움이되는 것은 없기 때문입니다. 문서를 보는 동안 모든 호출을해야합니다.

한 가지 할 수있는 일은 논리적으로 그룹화 된 매개 변수를 자신의 상위 수준 개체로 그룹화하는 것입니다.하지만 그 자체로 문제가 있습니다.

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