본문 바로가기
개발/Java

[Java] 생성자 대신 빌더 사용하기

by devhooney 2023. 1. 5.
728x90

1. 점층적 생성자 패턴

- 정적 팩토리와 생성자는 매개변수가 많을 경우 적절히 대응하기 어렵다.

- 과거에는 점층적 생성자 패턴 사용

public class Member {
    private String name;
    private String email;
    private int age;
    private String address;
    private String phone;
    
    public Member (String name, String email) {
    	this(name, email);
    }
    
    public Member (String name, String email, int age) {
    	this(name, email, age);
    }
    
    public Member (String name, String email, int age, String address) {
    	this(name, email, age, address);
    }
    
    public Member (String name, String email, int age, String address, Strubg phone) {
    	this(name, email, age, address, phone);
    }
    
    public Member (String name, String email, int age, String address, Strubg phone) {
        this.name = name;
        this.email = email;
        this.age = age;
        this.address = address;
        this.phone = phone;
    }
}

- 이 클래스의 인스턴스를 만들려면 원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출하면 된다.

 

 

Member member1 = new Member("test", "test@email.com", "50", "한국", "01012341234);

 

 

- 위와 같은 생성자는 사용자가 설정하길 원하지 않는 매개변수까지 포함하기 쉬운데, 어쩔 수 없이 그런 매개변수에도 값을 지정해줘야 한다.

- 매개변수가 늘아날 수록 클라이언트 코드를 작성하거나 읽기 어렵다.

- 코드를 읽을 때 각 값의 의미가 무엇인지 헷갈릴 것이고, 매개변수가 몇 개 인지도 주의해서 세어 보아야 한다.

- 타입이 같은 매개변수가 연달아 늘어서 있으면 찾기 어려운 버그로 이어질 수 있다.

- 클라이언트가 실수로 매개변수의 순서를 바꿔 건네줘도 컴파일러는 알아채지 못하고, 결국 런타임에 엉뚱한 동작을 하게 된다.

 

 

2. 자바빈즈 패턴

- 매개변수가 없는 생성자로 객체를 만든 후, setter 메소드를 호출해 원하는 매개변수의 값을 설정하는 방식

public class Member {
    // 매개변수들은 기본값으로 초기화된다.(기본값이 있을 경우)
    private String name = "";
    private String email = "";
    private int age = 0;
    private String address = "";
    private String phone = "";
    
    public Member {
    	// setter메소드들
        public void setName(String name) {
        	this.name = name;
        }
        public void setEmail(String email) {
        	this.email = email;
        }
        public void setAge(String age) {
        	this.age = age;
        }
        public void setAddress(String address) {
        	this.address = address;
        }
        public void setPhone(String phone) {
        	this.phone = phone;
        }
        
    }
}

 

Member member2 = new Member();
member2.setName("test");
member2.setEmail("test@email.com");
member2.setAge(50);
member2.setAddress("서울");
member2.setPhone("01020203030);

 

 

- 인스턴스를 만들기 쉽고, 읽기 쉬운 코드로 변경되었다.

- 하지만 자바 빈즈 패턴에서는 객체 하나를 만들려면 메소드 여러 개를 호출해야 하고, 객체가 완전히 생성되기 전까지 일관성이 무너진 상태가 된다.

- 자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없다.

 

 

3. 빌더 패턴

이런 두 가지 패턴에서 문제가 발생하는데, 이를 해결한 것이 빌더 패턴이다.

 

public class Member {
    private String name;
    private String email;
    private int age;
    private String address;
    private String phone;
    
    public static class Builder {
    	private final String name;
        private final String email;
        
        private int age;
        private String address;
        private String phone;
        
        public builder(String name, String email) {
        	this.name = name;
            this.email = email;
        }
        
        public Builder age(int val) {
			age = val;
            return this;
        }
        ... 반복 ...
        
    }
    
    private Member(Builder builder) {
    	name = builder.name;
        ... 반복 ...
        phone = builder.phone;
    }
    
}

 

- Member 클래스는 불변, 모든 매개변수의 기본값들을 한곳에 모아 뒀다.

- 빌더의 세터 메소드들은 빌더 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다.

-> 이런 방식을 Fluent API 혹은 Methos Chaining이라고 한다.

 

 

Member member3 = new Member.Builder("test3", "test3@email.com")
.age(50)
.address("제주도")
.phone("01034344545").build();

 

- 이 클라이언트 코드는 쓰기 쉽고, 읽기 쉽다.

 

 

4. 정리

- 생성자나 정적 팩토리가 처리해야할 매개변수가 많다면 빌더 패턴을 선택하는게 낫다.

- 매개변수 중 다 수가 필수가 아니거나 같은 타입이면 더 쓰는게 맞다.

- 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 쉽다.

- 빌더는 자바 빈즈보다 훨씬 안전하다.

 

 

 

- 참고

http://www.yes24.com/Product/Goods/65551284

728x90