본문 바로가기

디자인 패턴

[디자인 패턴] 빌더 패턴(Builder pattern)

https://ko.wikipedia.org/wiki/%EB%B9%8C%EB%8D%94_%ED%8C%A8%ED%84%B4

문제점

Class를 정의하고 각 상황에 필요한 변수를 넣어 생성자를 생성하게 되거나 기본생성자로 인스턴스 생성 후 setter로 변수에 값을 주게 되면 관리가 힘들어진다. 

public class Person {

    private String name;
    private String nickname;
    private int age;
    private int height;
    private int weight;

    public Person(){}

    public Person(String name, int age, int height, int weight){
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }
    public Person(String nickname, int height, int weight){
        this.name = nickname;
        this.height = height;
        this.weight = weight;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
Person a = new Person("mson", 181, 87);
Person b = new Person();
b.setNickname("mson");
b.setHeight(181);
b.setWeight(87);

여기서 만약 필수로 들어가야하는 변수가 늘어나는 경우 다시 setter로 해당 변수를 설정해주거나 생성자들을 수정해 주어야 한다. 이를 해결하고자 Builder pattern을 사용한다.

구현

구현 부분은 해당 클래스에 @Builder 어노테이션을 붙이고 빌드한 결과 코드를 가져왔다.

public class Person {
    private String name;
    private String nickname;
    private int age;
    private int height;
    private int weight;

    Person(final String name, final String nickname, final int age, final int height, final int weight) {
        this.name = name;
        this.nickname = nickname;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

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

    public static class PersonBuilder {
        private String name;
        private String nickname;
        private int age;
        private int height;
        private int weight;

        PersonBuilder() {
        }

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

        public PersonBuilder nickname(final String nickname) {
            this.nickname = nickname;
            return this;
        }

        public PersonBuilder age(final int age) {
            this.age = age;
            return this;
        }

        public PersonBuilder height(final int height) {
            this.height = height;
            return this;
        }

        public PersonBuilder weight(final int weight) {
            this.weight = weight;
            return this;
        }

        public Person build() {
            return new Person(this.name, this.nickname, this.age, this.height, this.weight);
        }

 

Person a = new Person.builder()
        .nickname("mson")
        .weight(181)
        .height(87)
        .build();

사용할때는 이런식으로 Builder를 통해 기본생성자를 생성후 필요한 속성들을 해당 메소드로 설정해주면 된다. 만약 인스턴스를 생성할 때 필수적으로 필요한 변수들이 있다면 밑에 방법으로 빌더 생성자를 고쳐주면 된다.

public class Person {
    private String name;
    private String nickname;
    private int age;
    private int height;
    private int weight;

    Person(final String name, final String nickname, final int age, final int height, final int weight) {
        this.name = name;
        this.nickname = nickname;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

    public static PersonBuilder builder(String nickname) {
        return new PersonBuilder(String nickname);
    }

    public static class PersonBuilder {
        private String name;
        private String nickname;
        private int age;
        private int height;
        private int weight;

        PersonBuilder(String nickname) {
        	this.nickname = nickname;
        }
		
        ...
   }

 

Person a = new Person.builder("mson")
        .weight(181)
        .height(87)
        .build();
        
        
Person b = new Person.builder() // 기본 생성자가 수정되어 nickname값을 넣지않고 인스턴스를 생성하면 오류가 난다.
		.nickname("mson")
        .weight(181)
        .height(87)
        .build();

스프링에서 @Builder 패턴을 사용할 때는 필수 변수를 따로 설정해 줄 수 있는 방법이 없기 때문에 필수 변수를 사용하려면 위에 코드처럼 build 코드를 가져온 후 수정하여 커스텀하여야 한다.

 

장점

  • 불필요한 생성자를 제거하고 쉽고 직관적으로 인스턴스 생성이 가능하다
  • 클래스 변수를 확장하기 쉽다.
  • 가독성이 높고 코드가 유연해진다.

단점

  • 관리해야 하는 클래스의 수가 늘어난다.
  • 클래스 변수가 적을 경우 효과를 보기 힘들다.
  • 성능에 약간의 영향을 주기 떄문에 성능에 민감한 프로그램일 경우 잘 고려하고 설계해야 한다.