소프트웨어 스터디/디자인패턴

Builder Pattern - 빌더패턴

로 얄 2021. 1. 7. 18:17
반응형

정의

해당 패턴은 생성자를 통해 직접 생성하지 않고, 빌더라는 내부 클래스를 통해 간접적으로 생성하는 패턴입니다.

그렇다면 왜 굳이 이렇게 해야하는가? 팩토리패턴과 무엇이 다른가 의문이 드는 부분이 있습니다.

 

빌더패턴은 객체를 생성할 때 생성자등에 다양한 파라미터가 들어가야하는 경우에 쓰여진다고 볼 수 있습니다.

 

코드

샌드위치 예제로 사용해보도록 하겠습니다.

public class Sandwich {
    enum BreadType {
        Flat, Bun, Roll
    }

    public Sandwich(BreadType breadType) {...}
    public Sandwich(BreadType breadType, boolean meat) {...}
    public Sandwich(BreadType breadType, boolean meat, boolean vegetable) {...}
    public Sandwich(BreadType breadType, boolean meat, boolean vegetable, boolean cheese) {...}
}

일반적으로 사용하는 위와 같은 방식은 생성자를 오버라이딩하여 사용하는 방식입니다. 물론 다른언어의 경우 default paramter(기본값)를 사용하여 좀 더 간단하게 표현이 가능합니다. 하지만 이와 같은 문제는 파라미터가 4개 5개 더 나아가 그 이상으로 늘어나게 된다면 어떤 파라미터의 순서를 기억하기 어려워지며, 상황에 알맞게 사용하는데 한계가 있습니다. 그래서 차선책으로 선택하는 방식은 아래와 같습니다.

Sendwich sendwich = new Sendwich();
sendwich.setBreadType(BreadType.Flat);
sendwich.setMeat(true);
sendwich.setVegetable(false);
sendwich.setCheese(true);

setter를 이용하여 값을 추가하는 방식입니다. 이런 생성방식은 생성과정에 중구난방이기 때문에 일관성이 없고 이런 과정은 유지보수에 있어서도 어려움이 발생합니다. 특히 파라미터가 늘어났을 때 해당 파라미터에 대한 값에 대한 처리, 혹은 생성시 넣어야 하는 값에 빼먹는 실수 등이 나올 수 있습니다. 또한 setter의 접근제한자 변경등에 대한 처리에 더 많은 작업시간이 소요됩니다.

public class Sandwich {
    public enum BreadType {
        Flat, Bun, Roll
    }
    private BreadType breadType;
    private boolean meat;
    private boolean vegetable;
    private boolean cheese;

    public BreadType getBreadType() { return breadType; }
    public boolean isMeat() { return meat; }
    public boolean isVegetable() { return vegetable; }
    public boolean isCheese() { return cheese; }

    private Sandwich(SandwichBuilder builder) {
        this.breadType = builder.breadType;
        this.meat = builder.meat;
        this.vegetable = builder.vegetable;
        this.cheese = builder.cheese;
    }

    public static class SandwichBuilder {
        private BreadType breadType;
        private boolean meat;
        private boolean vegetable;
        private boolean cheese;

        public SandwichBuilder(BreadType breadType) {
            this.breadType = breadType;
        }
        public SandwichBuilder setMeat(boolean meat) {
            this.meat = meat;
            return this;
        }
        public SandwichBuilder setVegetable(boolean vegetable) {
            this.vegetable = vegetable;
            return this;
        }
        public SandwichBuilder setCheese(boolean cheese) {
            this.cheese = cheese;
            return this;
        }
        public Sandwich build() {
            return new Sandwich(this);
        }
    }
}

Sandwich sandwich = new Sandwich.SandwichBuilder(Sandwich.BreadType.Flat)
                        .setMeat(true)
                        .setCheese(false)
                        .setVegetable(true)
                        .build();

Builder class의 생성으로 인해 코드가 길어지고 새로운 파라이터라 생성될 때 빌더까지 처리해줘야하는 번거로움이 생깁니다만, 해당 객체가 생성되는 모든 부분을 따라다니며 처리하는 것 보단 훨씬 편하며, Builder를 통해 생성하는 부분의 가독성이 상당히 좋아진것을 볼 수 있습니다. 또한 build() method를 통해 객체를 생성하기 때문에 생성전 유효성검사가 가능하여 유지보수에 큰 도움이 될 수 있습니다.

반응형

'소프트웨어 스터디 > 디자인패턴' 카테고리의 다른 글

Factory Method Pattern  (0) 2021.01.06
Singleton 패턴  (0) 2021.01.05