정의
해당 패턴은 생성자를 통해 직접 생성하지 않고, 빌더라는 내부 클래스를 통해 간접적으로 생성하는 패턴입니다.
그렇다면 왜 굳이 이렇게 해야하는가? 팩토리패턴과 무엇이 다른가 의문이 드는 부분이 있습니다.
빌더패턴은 객체를 생성할 때 생성자등에 다양한 파라미터가 들어가야하는 경우에 쓰여진다고 볼 수 있습니다.
코드
샌드위치 예제로 사용해보도록 하겠습니다.
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 |