Java 메소드에 너무 많은 매개 변수, Part 3 : 빌더 패턴

바로 앞의 두 게시물에서 사용자 지정 형식 및 매개 변수 개체를 통해 생성자 또는 메서드 호출에 필요한 매개 변수 수를 줄이는 방법을 살펴 보았습니다. 이 게시물에서는 빌더 패턴을 사용하여 생성자에 필요한 매개 변수 수를 줄이는 방법을 살펴보고이 패턴이 너무 많은 매개 변수를 사용하는 비 생성자 메서드에서도 어떻게 도움이 될 수 있는지에 대해 논의합니다.

효과적인 자바 제 2 판에서 Josh Bloch는 너무 많은 매개 변수가 필요한 생성자를 처리하기 위해 항목 # 2의 빌더 패턴 사용을 소개합니다. Bloch는 Builder를 사용하는 방법을 보여줄뿐만 아니라 많은 수의 매개 변수를 받아들이는 생성자보다 이점을 설명합니다. 이 글의 끝에서 이러한 이점에 대해 알아볼 것이지만 Bloch가 그의 책에서이 관행에 전체 항목을 바쳤다는 점을 지적하는 것이 중요하다고 생각합니다.

이 접근 방식의 장점을 설명하기 위해 다음 예제 Person클래스를 사용하겠습니다 . 구성에 집중하고 싶기 때문에 일반적으로 이러한 클래스에 추가하는 모든 메서드가있는 것은 아닙니다.

Person.java (빌더 패턴 없음)

package dustin.examples; /** * Person class used as part of too many parameters demonstration. * * @author Dustin */ public class Person { private final String lastName; private final String firstName; private final String middleName; private final String salutation; private final String suffix; private final String streetAddress; private final String city; private final String state; private final boolean isFemale; private final boolean isEmployed; private final boolean isHomewOwner; public Person( final String newLastName, final String newFirstName, final String newMiddleName, final String newSalutation, final String newSuffix, final String newStreetAddress, final String newCity, final String newState, final boolean newIsFemale, final boolean newIsEmployed, final boolean newIsHomeOwner) { this.lastName = newLastName; this.firstName = newFirstName; this.middleName = newMiddleName; this.salutation = newSalutation; this.suffix = newSuffix; this.streetAddress = newStreetAddress; this.city = newCity; this.state = newState; this.isFemale = newIsFemale; this.isEmployed = newIsEmployed; this.isHomewOwner = newIsHomeOwner; } } 

이 클래스의 생성자는 작동하지만 클라이언트 코드가 제대로 사용하기는 어렵습니다. 빌더 패턴을 사용하여 생성자를 더 쉽게 사용할 수 있습니다. NetBeans는 내가 이전에 작성한 것처럼 이것을 리팩토링 할 것입니다. 리팩토링 된 코드의 예가 다음에 표시됩니다 (NetBeans는 모든 새 Builder 클래스를 생성하여이를 수행합니다).

PersonBuilder.java

package dustin.examples; public class PersonBuilder { private String newLastName; private String newFirstName; private String newMiddleName; private String newSalutation; private String newSuffix; private String newStreetAddress; private String newCity; private String newState; private boolean newIsFemale; private boolean newIsEmployed; private boolean newIsHomeOwner; public PersonBuilder() { } public PersonBuilder setNewLastName(String newLastName) { this.newLastName = newLastName; return this; } public PersonBuilder setNewFirstName(String newFirstName) { this.newFirstName = newFirstName; return this; } public PersonBuilder setNewMiddleName(String newMiddleName) { this.newMiddleName = newMiddleName; return this; } public PersonBuilder setNewSalutation(String newSalutation) { this.newSalutation = newSalutation; return this; } public PersonBuilder setNewSuffix(String newSuffix) { this.newSuffix = newSuffix; return this; } public PersonBuilder setNewStreetAddress(String newStreetAddress) { this.newStreetAddress = newStreetAddress; return this; } public PersonBuilder setNewCity(String newCity) { this.newCity = newCity; return this; } public PersonBuilder setNewState(String newState) { this.newState = newState; return this; } public PersonBuilder setNewIsFemale(boolean newIsFemale) { this.newIsFemale = newIsFemale; return this; } public PersonBuilder setNewIsEmployed(boolean newIsEmployed) { this.newIsEmployed = newIsEmployed; return this; } public PersonBuilder setNewIsHomeOwner(boolean newIsHomeOwner) { this.newIsHomeOwner = newIsHomeOwner; return this; } public Person createPerson() { return new Person(newLastName, newFirstName, newMiddleName, newSalutation, newSuffix, newStreetAddress, newCity, newState, newIsFemale, newIsEmployed, newIsHomeOwner); } } 

나는 내 Builder를 자신이 빌드하는 객체의 클래스 내부에 중첩 된 클래스로 사용하는 것을 선호하지만 독립형 Builder의 NetBeans 자동 생성은 사용하기 매우 쉽습니다. NetBeans 생성 빌더와 내가 작성하고 싶은 빌더의 또 다른 차이점은 선호하는 빌더 구현에는 인수가없는 생성자를 제공하는 대신 빌더의 생성자에 제공된 필수 필드가 있다는 것입니다. 다음 코드 목록은 Person중첩 된 클래스로 빌더가 추가 된 위의 클래스를 보여줍니다 .

중첩 된 Person.Builder가있는 Person.java

package dustin.examples; /** * Person class used as part of too many parameters demonstration. * * @author Dustin */ public class Person { private final String lastName; private final String firstName; private final String middleName; private final String salutation; private final String suffix; private final String streetAddress; private final String city; private final String state; private final boolean isFemale; private final boolean isEmployed; private final boolean isHomewOwner; public Person( final String newLastName, final String newFirstName, final String newMiddleName, final String newSalutation, final String newSuffix, final String newStreetAddress, final String newCity, final String newState, final boolean newIsFemale, final boolean newIsEmployed, final boolean newIsHomeOwner) { this.lastName = newLastName; this.firstName = newFirstName; this.middleName = newMiddleName; this.salutation = newSalutation; this.suffix = newSuffix; this.streetAddress = newStreetAddress; this.city = newCity; this.state = newState; this.isFemale = newIsFemale; this.isEmployed = newIsEmployed; this.isHomewOwner = newIsHomeOwner; } public static class PersonBuilder { private String nestedLastName; private String nestedFirstName; private String nestedMiddleName; private String nestedSalutation; private String nestedSuffix; private String nestedStreetAddress; private String nestedCity; private String nestedState; private boolean nestedIsFemale; private boolean nestedIsEmployed; private boolean nestedIsHomeOwner; public PersonBuilder( final String newFirstName, final String newCity, final String newState) { this.nestedFirstName = newFirstName; this.nestedCity = newCity; this.nestedState = newState; } public PersonBuilder lastName(String newLastName) { this.nestedLastName = newLastName; return this; } public PersonBuilder firstName(String newFirstName) { this.nestedFirstName = newFirstName; return this; } public PersonBuilder middleName(String newMiddleName) { this.nestedMiddleName = newMiddleName; return this; } public PersonBuilder salutation(String newSalutation) { this.nestedSalutation = newSalutation; return this; } public PersonBuilder suffix(String newSuffix) { this.nestedSuffix = newSuffix; return this; } public PersonBuilder streetAddress(String newStreetAddress) { this.nestedStreetAddress = newStreetAddress; return this; } public PersonBuilder city(String newCity) { this.nestedCity = newCity; return this; } public PersonBuilder state(String newState) { this.nestedState = newState; return this; } public PersonBuilder isFemale(boolean newIsFemale) { this.nestedIsFemale = newIsFemale; return this; } public PersonBuilder isEmployed(boolean newIsEmployed) { this.nestedIsEmployed = newIsEmployed; return this; } public PersonBuilder isHomeOwner(boolean newIsHomeOwner) { this.nestedIsHomeOwner = newIsHomeOwner; return this; } public Person createPerson() { return new Person( nestedLastName, nestedFirstName, nestedMiddleName, nestedSalutation, nestedSuffix, nestedStreetAddress, nestedCity, nestedState, nestedIsFemale, nestedIsEmployed, nestedIsHomeOwner); } } } 

빌더는 "너무 많은 매개 변수"문제에 대한 나의 처음 두 게시물에 설명 된대로 사용자 정의 유형 및 매개 변수 오브젝트를 사용하여 향상 될 때 더 좋을 수 있습니다. 이것은 다음 코드 목록에 나와 있습니다.

중첩 빌더, 사용자 정의 유형 및 매개 변수 오브젝트가있는 Person.java

package dustin.examples; /** * Person class used as part of too many parameters demonstration. * * @author Dustin */ public class Person { private final FullName name; private final Address address; private final Gender gender; private final EmploymentStatus employment; private final HomeownerStatus homeOwnerStatus; /** * Parameterized constructor can be private because only my internal builder * needs to call me to provide an instance to clients. * * @param newName Name of this person. * @param newAddress Address of this person. * @param newGender Gender of this person. * @param newEmployment Employment status of this person. * @param newHomeOwner Home ownership status of this person. */ private Person( final FullName newName, final Address newAddress, final Gender newGender, final EmploymentStatus newEmployment, final HomeownerStatus newHomeOwner) { this.name = newName; this.address = newAddress; this.gender = newGender; this.employment = newEmployment; this.homeOwnerStatus = newHomeOwner; } public FullName getName() { return this.name; } public Address getAddress() { return this.address; } public Gender getGender() { return this.gender; } public EmploymentStatus getEmployment() { return this.employment; } public HomeownerStatus getHomeOwnerStatus() { return this.homeOwnerStatus; } /** * Builder class as outlined in the Second Edition of Joshua Bloch's * Effective Java that is used to build a {@link Person} instance. */ public static class PersonBuilder { private FullName nestedName; private Address nestedAddress; private Gender nestedGender; private EmploymentStatus nestedEmploymentStatus; private HomeownerStatus nestedHomeOwnerStatus; public PersonBuilder( final FullName newFullName, final Address newAddress) { this.nestedName = newFullName; this.nestedAddress = newAddress; } public PersonBuilder name(final FullName newName) { this.nestedName = newName; return this; } public PersonBuilder address(final Address newAddress) { this.nestedAddress = newAddress; return this; } public PersonBuilder gender(final Gender newGender) { this.nestedGender = newGender; return this; } public PersonBuilder employment(final EmploymentStatus newEmploymentStatus) { this.nestedEmploymentStatus = newEmploymentStatus; return this; } public PersonBuilder homeOwner(final HomeownerStatus newHomeOwnerStatus) { this.nestedHomeOwnerStatus = newHomeOwnerStatus; return this; } public Person createPerson() { return new Person( nestedName, nestedAddress, nestedGender, nestedEmploymentStatus, nestedHomeOwnerStatus); } } } 

마지막 두 개의 코드 목록은 빌더가 일반적으로 객체를 생성하는 데 사용되는 방법을 보여줍니다. 실제로 Joshua Bloch의 Second Edition of Effective Java에있는 빌더의 항목 (항목 # 2)은 객체 생성 (및 파괴)에 대한 장에 있습니다. 그러나 빌더는 메소드에 전달되는 매개 변수 오브젝트를 빌드하는 더 쉬운 방법을 허용하여 비 생성자 메소드를 간접적으로 도울 수 있습니다.