언어/Effective Java

이펙티브 자바 Item2 - 생성자에 매개변수가 많다면 빌더를 고려하라

now0204 2024. 5. 27. 11:44

 

정적 팩토리와 생성자는 "선택적 매개변수"가 많을 때 적절하게 대응하기 어렵다는 단점이 있다.

 

만약 있을 수도 없을 수도 있는 필드가 많은 객체가 있다고 한다면..

 


 

1. 점층적 생성자 패턴 

  • 필수 매개변수를 받는 생성자 1개, 그리고 선택 매개변수를 하나씩 늘려가며 생성자를 만드는 패턴이다. 
public class Javabom {
    private Long id;
    private List<String> members;
    private String email;
    private LocalDateTime createDateTime;
    private boolean isOpen;

    public Javabom(Long id, List<String> members, String email, LocalDateTime createDateTime, boolean isOpen) {
        this.id = id;
        this.members = members;
        this.email = email;
        this.createDateTime = createDateTime;
        this.isOpen = isOpen;
    }

    public Javabom(Long id, LocalDateTime createDateTime) {
        this.id = id;
        this.createDateTime = createDateTime;
    }

    public Javabom(Long id, List<String> members, LocalDateTime createDateTime) {
        this.id = id;
        this.members = members;
        this.createDateTime = createDateTime;
    }
}

 

위 객체는 생성시 특정 필드를 받을수도 안받을 수도 있다.

 

  • 클라이언트가 초기화하고 싶은 필드만 포함한 생성자가 없으면, 원치 않는 필드까지 불가피하게 초기화해야한다.
  • 복잡하고 읽기 어렵다.

 2. 자바빈즈 패턴(Setter 패턴)

 

public class JavaBeans {
    private Long id;
    private List<String> members;
    private String email;
    private LocalDateTime createDateTime;
    private boolean isOpen;

    public JavaBeans(Long id, LocalDateTime createDateTime) {
    	this.id = id;
        this.createDateTime = createDateTime;
    }

    public void setMembers(List<String> members) {
        this.members = members;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setOpen(boolean open) {
        isOpen = open;
    }
}
  • 필수 속성값은 매개변수로 두고, 선택적으로 setter를 열어 초기화 하고 싶은 필드만 골라서 초기화할 수 있다.

하지만, 

  1. 객체 하나를 만들려면 메서드를 계속해서 호출해야 하는 점 
  2. 그 사이 일관성 유지가 어렵다는 점 
  3. 객체를 불변으로 만들 수 없다는 점이 단점이다. 

이에 대안으로 freezing 기법이 언급된다고 한다. boolean으로 상태검사하는 코드를 넣어, setting된 속성은 다시 셋팅 못하도록 막는 기법이다. 


 

3. 빌더패턴 

 

필수 매개변수로 객체를 생성하고 일종의 "Setter"를 사용해서 필수 매개변수를 초기화한 뒤 build() 메서드를 호출하여 완전한 객체를 생성하는 패턴이다. 

 

//생성하려는 타겟 클래스 
class Student{
	private int id;
    private String name;
    private String grade;
    private String phoneNumber;
    
    
}

//동일한 필드를 가지는 클래스 마지막에 build를 호출해서 target을 만들도록 한다.
class StudentBuilder{
	private int id;
    private String name;
    private String grade;
    private String phoneNumber;
    
    public StudentBuilder id(int id) {
        this.id = id;
        return this;
    }

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

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

    public StudentBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }
    public Student build() {
        return new Student(id, name, grade, phoneNumber); // Student 생성자 호출
    }
}
  • 필수 매개변수는 빌더에 생성자로 사용하면 된다!! 
  • target class에  static class로 넣을 수도 있다.

빌더 패턴의 장점은 다음과 같다.

  • 객체 생성 과정을 일관된 프로세스로 표현 
  • 디폴트 매개변수 생략을 간접적으로 지원
  • 필수 멤버와 선택적 멤버 분리 가능 
  • 객체 생성 단계를 지연할 수 있다. -> 빌더만 생성해놨다가, 나중에 build()할 수 있다. 
  • 초기화 검증을 분리할 수 있다. -> 빌더 메서드로 받기 때문에 각 매개변수별로 검증 가능

빌더 패턴의 단점은 다음과 같다.

  • 코드 복잡성 증가
  • 생성자 보다 성능이 떨어진다.
  • 지나친 빌더는 금지 (필드 개수가 4개보다 적고, 필드 변경 가능성이 없다면 차라리 생성자나 정적 팩토리 메서드를 사용하는 것이 더 나을 수 있다.)
    • *다만, API는 시간이 지날수록 많은 매개변수를 갖는 경향이 있음으로 애초에 빌더 패턴으로 시작하는 편이 나을 때가 많다고 말하는 경향도 있다.

참고자료 

https://javabom.tistory.com/67

 

[ 아이템 2 ] 생성자에 매개변수가 많다면 빌더를 고려하라

빌더패턴 전에 ,, 정적팩터리와 생성자는 "선택적 매개변수"가 많을 떄 적절하게 대응하기 어렵다는 단점이 있다. 만약 있을 수도 없을 수도 있는 필드가 많은 객체가 있다고 가정하자. public class

javabom.tistory.com

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC#%EB%B9%8C%EB%8D%94builder_%ED%8C%A8%ED%84%B4

 

💠 빌더(Builder) 패턴 - 완벽 마스터하기

Builder Pattern 빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아

inpa.tistory.com