빌더 (Builder)

빌더 패턴은 복잡한 객체의 생성과 표현을 분리하여 인스턴스를 만드는 생성패턴입니다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식입니다.

 

 

빌더패턴을 사용하는 이유

우리는 데이터를 주입할 때 생성자를 이용하거나 기본생성자로 객체를 생성하고 이후에 Setter를 이용해서 값을 넣었습니다.

 

 

 

생성자를 이용한 주입

public class User {

    private String name;
    private int age;

    private String sex;
    private String phone;
    private int height;

    public User(String name, int age, String sex, String phone, int height) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.phone = phone;
        this.height = height;
    }
}

public class Main {

    public static void main(String[] args) {

        User user = new User("김OO", 20, "남자", "01012345678", 70);

    }
}

 

Setter를 이용한 주입

public class User {

    private String name;
    private int age;

    private String sex;
    private String phone;
    private int height;
    

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

public class Main {

    public static void main(String[] args) {

        User user = new User();
        user.setName("김OO");
        user.setAge(20);
        user.setSex("남자");
        user.setPhone("01012345678");
        user.setHeight(70);
    }
}

 

이러한 방식은 생성자의 인자가 많아질 수록 데이터를 구별하기 어렵고, 필드와 생성자의 위치를 직관적으로 파악하기 어렵다는 것입니다. 그리고 타입이 많아질 수록 메서드 수가 기하급수적으로 늘어나 가독성과 유지보수 측면에서 안좋을 수 밖에 없습니다.

Setter는 객체를 생성하는 시점에 값을 주입하지않아 일관성(Consistency) 불변성(Immutable) 문제가 생길 수 있습니다.

 

생성자와 Setter의 문제점

 

 

일관성 문제

필수 매개변수란 객체가 초기화 될 때 반드시 설정되어야 하는 값입니다. 만약 개발자가 setAge() 메서드를 호출 하지 않고 User 객체를 생성했다면 이 객체는 일관성이 없는 상태가 됩니다. 만약 이 객체를 사용하게 된다면 예외가 발생할 수 있습니다.

 

불변성 문제

Setter의 문제는 객체를 생성한 이후에 값을 주입하는데요 이는 객체를 생성한 이후로도 누구나 외부에서 Setter를 사용해서 값을 주입할 수 있는 문제가 생깁니다. 다시 말해 누구든지 Setter를 호출해 객체를 조작할 수 있다는 뜻입니다. 따라서 Setter는 불변성이 보장되지않는 객체라고 할 수 있습니다.

 

 

 

Builder패턴의 사용

 

빌더 패턴은 이러한 문제들을 해결하기위해 Buillder 클래스를 만들어 생성되는 시점에 모든 값들을 넣어주도록 하는 패턴입니다.

 

        // 생성자 주입
        User user1 = new User("김OO", 20, "남자", "01012345678", 70);

        // Setter 주입
        User user2 = new User();
        user.setName("김OO");
        user.setAge(20);
        user.setSex("남자");
        user.setPhone("01012345678");
        user.setHeight(70);

        // Builder 주입
        User user3 = User.builder()
            .name("김OO")
            .age(20)
            .sex("남자")
            .phone("01012345678")
            .height(70)
            .build();

 

빌더 패턴을 이용하면 생성자를 오버로딩을 하지 않아도되며, 데이터의 순서에 상관없이 데이터를 주입할 수 있게되었습니다.

 

 

 

빌더패턴의 장점

  • 필요한 데이터만 설정할 수 있습니다.
  • 가독성이 좋습니다.
  • 변경 가능성을 최소화 할 수 있습니다.

 

 

빌더패턴의 구조

구현자체는 어렵지 않습니다. 위의 User 객체에 Builder 패턴을 구현해보겠습니다.

 


1. builder()

User 클래스 안에 Builder 클래스를 구현해줍니다.

public class User {

    private String name;
    private int age;

    private String sex;
    private String phone;
    private int height;

    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public static class UserBuilder {

    }

}

 

 

User.builder()로 Builder 패턴을 시작해야하기 때문에 static 을 사용해 클래스를 만들어줍니다.

 

 

 

2. Chaning

    public static class UserBuilder {

        private final User user;

        public UserBuilder() {
            this.user = new User();
        }

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

        public UserBuilder age(int age) {
            user.age = age;
            return this;
        }

        public UserBuilder sex(String sex) {
            user.sex = sex;
            return this;
        }

        public UserBuilder phone(String phone) {
            user.phone = phone;
            return this;
        }

        public UserBuilder height(int height) {
            user.height = height;
            return this;
        }

        public User build() {
            return user;
        }
    }

 

User.builder() 를 입력하면 UserBuilder 기본생성자를 통해 User 기본생성자를 주입시켜줍니다. 여기서 주의할 사항은 User의 기본생성자가 존재해야합니다. 인자를 받는 생성자가 존재한다면 기본생성자를 하나 더 만들어주어야합니다.

 

이후에 반환타입은 UserBuilder, 메서드명은 User의 변수명으로 만들어 메서드를 체이닝(Chaining) 형태로 호출하고 마지막에 build()를 통해 최종적으로 객체를 반환하도록 만들어주면 Builder 패턴을 쉽게 구현할 수 있습니다.

+ Recent posts