스프링

Builder Pattern (빌더 패턴)

쿠쿠s 2022. 6. 28. 23:15

빌더 패턴의 정의


빌더 패턴은 "복잡한 객체의 구성을 해당 표현과 분리하여 동일한 구성 프로세스에서 여러 개의 다른 표현을 생성할 수 있도록 하는 것"을 목표로 합니다.

 

 

빌더 패턴은 fluent interface와 비슷해야 합니다. fluent interface는 일반적으로 람다식에서 볼 수 있듯이 method cascading(또는 method chaining)을 사용하여 구현됩니다.

 

*메서드 체인은 객체 지향 프로그래밍 언어에서 여러 메서드를 호출하기 위한 일반적인 구문입니다.

 

 

method chainig example) 

public class PersonMethodChaining {
    private String name;
    private int age;

    //해당 속성을 설정하는 부작용 외에도 setter 는 추가로 연결된 메서드 호출을 허용하기 위해 "this"(현재 Person 객체)를 반환
    public PersonMethodChaining setName(String name) {
        this.name = name;
        return this;
    }

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

    public void introduce() {
        System.out.println("내 이름은=" + name + ", 나이는 =" + age);
    }

    public static void main(String[] args) {
        PersonMethodChaining person = new PersonMethodChaining();
        person.setName("띵구").setAge(29).introduce();
    }
}

 

 

No method chaining example )

public class Person {
    private String name = ""; //필수
    private int age = 0;
    
    public void setName(String name) {
        this.name = name;
    }

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

    public void introduce() {
        System.out.println("내 이름은=" + name + ", 나이는 =" + age);
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.setName("띵구");
        person.setAge(29);
        person.introduce();
    }
}

 

 

 

 

그렇다면 빌더 패턴이 필요한 곳은 어디일까?


위의 체이닝 메소드를 적용하지 않은 person 클래스를 보면 객체 하나를 만드려고 메서드를 여러 개 호출하고, 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓이게 된다. 생성자를 사용하여 미리 초기화할 값을 셋팅해도 이름만 입력하고 싶다던가 나이만 입력하고 싶을 수 있다. 또한 setter를 사용하기 때문에 final 키워드도 사용할 수 없어 불변으로 만들 수 없다.

 

지금 같은 경우는 2개의 값만 입력하면 되지만 셋팅해야할 값이 5개가 넘어간다면..? 그중에서도 몇 개는 사용할 때도 있고 사용 안 할 때도 있을 것이다. 이런 경우 모두 생성자를 만들게 된다면 코드 작성이 힘이 들고 가독성도 떨어진다.

 

그래서 이럴 때 빌더패턴을 사용하면 된다.

1. 클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개 변수만으로 생성자를 호출해 빌더 객체를 얻는다.

2. 빌더 객체가 제공하는 일종의 setter 메서드들로 원하는 선택 매개변수들을 설정한다.

3. 매개변수가 없는 build 메서드를 호출하여 필요한 객체(보통은 불변)를 얻는다.

 

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

    public static class Builder {
        //필수 매개변수
        private final String name;
        private final int age;

        //선택 매개변수
        private int phoneNum = 0;
        private int height = 0;
        private int weight = 0;

        public Builder(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public Builder phoneNum(int phoneNum) {
            this.phoneNum = phoneNum;
            return this;
        }

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

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

        public Person build() {
            return new Person(this);
        }
    }

    private Person(Builder builder) {
        name = builder.name;
        age = builder.age;
        phoneNum = builder.phoneNum;
        weight = builder.weight;
        height = builder.height;
    }


    public static void main(String[] args) {
        Person person = new Builder("띵구", 29)
		.phoneNum(010101010)
		.height(180)
                .weight(70)
                .build();
    }
}

위와 같이 만듬으로..

 1. 코드 라인의 수는 빌더 패턴에서 적어도 두 배로 증가하지만 디자인 유연성과 훨씬 더 읽기 쉬운 코드 면에서 장점

 2. 생성자에 대한 매개변수가 줄어들고 가독성이 높은 연결 메서드 호출로 제공

 3. build()를 사용해야 객체를 생성시키기 때문에 완전한 상태로 인스턴스화가 된다.

 

단점은

- 빌더부터 만들어야 하기 때문에 코드가 장황해진다는 단점이 있습니다.

 

 

 

하지만  lombok의 @Builder라는 엄청난 어노테이션이 있습니다. @Builder라는 어노테이션을 사용하면 위에서 장황하게 만든 빌더를 대신 만들어 준다..!

 

import lombok.Builder;

@Builder
public class Person {
    private final String name;
    private final int age;
    private int phoneNum;
    private int height;
    private int weight;

    public static void main(String[] args) {
        Person person = Person.builder()
                .name("띵구")
                .age(29)
                .phoneNum(0121231)
                .height(180)
                .weight(70)
                .build();
                
    }
}

@Builder 만 붙이면 끝!

 

 

 

이펙티브 자바 중.. 

- 생성자나 정적 팩토리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다. 매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다. 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.

 

 

 

 

 

 

 

 

 

 

참고

https://howtodoinjava.com/design-patterns/creational/builder-pattern-in-java/

https://stackoverflow.com/questions/2872222/how-to-do-method-chaining-in-java-o-m1-m2-m3-m4

이펙티브자바 3/E

'스프링' 카테고리의 다른 글

[HTTP] 로그아웃은 "GET" or "POST" ??  (0) 2022.07.12
assertJ - 공식문서 기반 간단 정리  (0) 2022.07.08
XSS 와 CSRF  (0) 2022.06.05
서블릿(Servlet)  (0) 2022.05.31
싱글톤 패턴(Singleton)  (0) 2022.05.26