본문 바로가기

Design/Design Pattern

[Design Pattern] Builder Pattern

이번 장에서는 빌더(Builder) 패턴에 대해서 알아보도록 한다.
샘플 코드는 여기 (링크) 프로젝트의 테스트 코드로 정리해두었다.


이번 디자인 패턴은 데이터베이스에 접속하는 클라이언트를 생성하는 과정을 접목시켜서 알아도록한다.
필자는 빌더 패턴은 "사전적 정의의 빌더 패턴"과 "현업에서 사용하는 빌더 패턴" 두 가지가 있다고 생각한다.
이번 장에서는 두 가지의 패턴을 모두 다뤄보도록 한다.

(필자는 Spring으로 개발을 해서 Lombok 어노테이션을 사용해서인지 "현업에서 사용하는 빌더 패턴"만 사용하였고 "사전적 정의의 빌더 패턴은 사용해본 적이 없다.")

"현업에서 사용하는 빌더 패턴"이 궁금하다면 글의 하단부로 이동한다.

빌더 패턴이란?
복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다.

사실 빌더 패턴을 사용하는 가장 큰 이유는 멤버 변수가 많은 객체를 생성자를 통해서 생성하면 휴먼에러가 발생할 수 있고
불필요한 값들에 null을 입력해주어야 한다는 단점을 보완하기 위해서 사용한다.

GoF Design Patternsdㅔ 따르면 아래와 같은 Class Diagram이 그려진다.

추상 클래스인 Builder는 구현체에서 인스턴스를 생성할 BuildPart 메서드를 명시하고 있다.
Builder의 구현체인 ConcreteBuilder는 인스턴스를 생성하는 BuildPart 메서드를 구현하고 있다.
Director는 멤버로 Builder를 가지게 되며 사용자는 Director를 통해서 실제 인스턴스를 생성하게 된다.

데이터베이스에 접속하는 클라이언트를 생성한다고 가정하면 아래와 같은 Class Diagram이 그려진다.

DB Client를 생성하는 User는 Creator를 생성하고 자신이 원하는 Builder를 set시킨다.
이후 Creator의 constructDBClient를 통해서 자신이 선택한 Builder 인스턴스가 생성시킨 DB Client를 사용하게 된다.

코드로 보면서 하나씩 살펴보도록 한다.


사전적 정의의 빌더 패턴

DBClient Class
DB에 접속하는 인스턴스를 생성할 클래스로서 DB 접속 정보를 가지고 있다.

DBClientBuilder Abstract Class
DBClient 인스턴스를 생성하기 위한 기능을 명시하고 있다.

DefaultDBClientBuilder Class
DBClientBuilder을 확장(상속)한 클래스로 실제로 DBClient를 생성하고 필요한 정보를 set하는 역할을 한다.

Creator Class
Builder를 가지고 있으며 사용자가 선택한 Builder를 통하여 DBClient를 생성하는 역할을 한다.

User Class
사용자는 Creator를 생성하고 원하는 Builder를 주입하고 Creator를 통해서 인스턴스를 생성하고 사용한다.

코드의 작동 결과는 아래와 같다.


현업에서 사용하는 빌더 패턴

실제로 많이 사용되는 빌더 패턴은 위에서 설명한 방식과는 다르다.

UsedDBClient Class
DB에 접근하는 클라이언트인 UsedDBClient는 inner class로 Builder를 가지고 있다.
Builder 클래스는 UsedDBClient와 동일한 필드를 가지고 있다.
Builder 클래스의 필드를 set하는 메소드의 경우 필드를 set하고 자기자신을 return하는 체이닝 구조로 되어있다.
Builder 클래스의 build 메서드는 이전 단계에서 set시킨 자신의 필드로 새로운 UsedDBClient를 생성하는 역할을 한다.

// toString 코드는 생략하였음.
public class UsedDBClient {
    public String address;
    public int port;
    public String id;
    public String password;
    public String database;

    private UsedDBClient() {};

    private UsedDBClient(String address, int port, String id, String password, String database) {
        this.address = address;
        this.port = port;
        this.id = id;
        this.password = password;
        this.database = database;
    }

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

    public static class Builder {
        private String address;
        private int port;
        private String id;
        private String password;
        private String database;

        private Builder() {}

        public Builder address(String address) {
            this.address = address;
            return this;
        }
        public Builder port(int port) {
            this.port = port;
            return this;
        }
        public Builder id(String id) {
            this.id = id;
            return this;
        }
        public Builder password(String password) {
            this.password = password;
            return this;
        }
        public Builder database(String database) {
            this.database = database;
            return this;
        }
        public UsedDBClient build() {
            return new UsedDBClient(this.address, this.port, this.id, this.password, this.database);
        }
    }

}

이를 사용하는 사용하는 User 클래스의 코드는 아래와 같다.

코드를 실행시킨 결과는 아래와 같다.


지금까지 데이터베이스에 접속하는 클라이언트를 생성하는 과정에 빌더 패턴을 적용시켜보았다.