개발자 yeah의 Developer Story - 디자인 패턴
오늘은 저번 포스팅에 이어서 빌더 패턴 을 이야기해보려 합니다.
먼저 빌더 패턴 을 설명하기 전에 복합 객체에 대해 설명을 하도록 하겠습니다.
복합 객체란 하나의 객체가 다른 객체를 포함하는 관계 구조를 말합니다.
즉 내부적으로 다른 클래스의 객체를 포함하는 것이죠.
따라서 복합 객체는 객체를 생성하는 과정이 단일 객체보다 복잡 할 수 있습니다.
코드로 예를 들어 보겠습니다.
/* Cpu 클래스 */
public class Cpu {
String cpu;
Cpu(String cpu) { this.cpu = cpu; }
String getInfo() { return cpu; }
}
/* Ram 클래스 */
public class Ram {
String ram;
Ram(String ram) { this.ram = ram; }
public String getInfo() { return ram; }
}
/* Graphics 클래스 */
public class Graphics {
String graphics;
Graphics(String graphics) { this.graphics = graphics; }
String getInfo() { return graphics; }
}
Computer 를 만드는 데 있어 필요한 부품 Cpu, Ram, Graphics 클래스가 있습니다.
public class Computer {
private Cpu cpu;
private Ram ram;
private Graphics graphics;
/* Computer 생성자 */
Computer(Cpu cpu, Ram ram, Graphics graphics) {
this.cpu = cpu;
this.ram = ram;
this.graphics = graphics;
}
/* Computer 정보 출력 */
public void computerInfo() {
String cpuInfo = cpu.getInfo();
String ramInfo = ram.getInfo();
String graphicsInfo = graphics == null ? "내장형 그래픽" : graphics.getInfo();
System.out.println("컴퓨터 정보 출력 - "
+ "Cpu : " + cpuInfo
+ ", Ram : " + ramInfo
+ ", Graphics : " + graphicsInfo);
}
}
Computer 클래스는 Cpu, Ram, Graphics 를 포함하는 복합 객체입니다.
생성자 를 통한 의존성 주입 으로 객체를 전달받아 set 하고 있습니다.
public class BuilderPattern {
public static void main(String[] args) {
/* 프리미엄 컴퓨터 */
Cpu cpu = new Cpu("Intel core i7");
Ram ram = new Ram("16GB");
Graphics graphics = new Graphics("RTX-3090");
Computer premiumPC = new Computer(cpu, ram, graphics);
premiumPC.computerInfo();
/* 저가 컴퓨터 - 그래픽카드를 포함하지 않음 */
Cpu cpu = new Cpu("Intel core i7");
Ram ram = new Ram("8GB");
Computer lowCostPC = new Computer(cpu, ram, null);
premiumPC.computerInfo();
}
}
다음은 main() 에서 Computer 를 생성하는 코드입니다.
Computer 객체 생성 시 new키워드를 통한 생성자에 각각의 Cpu, Ram, Graphics 객체를 전달하는 것을 알 수 있습니다.
다음 코드의 문제점은 무엇일까요?
Computer 를 만들 때 부품의 구성여부에 따라 종류가 달라질 수 있습니다.
위 코드에서는 그래픽카드 유무에 따라 프리미엄 / 저가형 으로 구분을 하였습니다.
하지만 생성자를 통해 생성하기 때문에 저가형 같은 경우 그래픽카드가 필요 없지만 null을 넘기는 것을 확인할 수 있습니다.
이는 Computer 객체(복합객체) 내 불필요한 객체 생성을 야기합니다.
그렇다면 복합 객체 내 원하는 객체만 생성하여 인스턴스화 하려면 어떻게 해야 할까요?
첫번째, 우리는 여러 개의 생성자 를 오버로딩 하여 해결하는 방법을 생각할 수 있습니다.
하지만 이 방법은 구성하는 부품의 개수가 많아지면 그때마다 생성자를 추가해줘야 하는 번거로움이 있습니다.
두번째, Computer 클래스 내부에 각각 부품을 set 할 수 있는 함수를 정의하여 해결할 수도 있습니다.
하지만 이 방법 또한 Computer 객체를 생성한 후에 추가로 set 함수들을 호출해야 하기 때문에 누락할 잠재적인 문제가 있습니다.
※따라서 복잡한 구조를 가진 복합 객체를 우리가 원하는 구조로 생성하기 위해서는 구조에 맞게 생성할 수 있는 로직이 필요합니다.
따라서 이러한 문제를 해결할 수 있는 빌더 패턴 을 적용해보도록 하겠습니다.
빌더 패턴 은 복합 객체 생성 과정을 별도의 독립된 클래스(Builder) 로 관리합니다.
/* 빌더 패턴을 위한 빌더 */
public class Builder {
private Computer computer; //Computer 객체
/* 생성자를 통해 Computer 객체를 먼저 생성 */
Builder() { computer = new Computer(); }
/* cpu 정보를 set */
public Builder setCpu(Cpu cpu) {
computer.setCpu(cpu);
return this;
}
/* Ram 정보를 set */
public Builder setRam(Ram ram) {
computer.setRam(ram);
return this;
}
/* Graphics 정보를 set */
public Builder setGraphics(Graphics graphics) {
computer.setGraphics(graphics);
return this;
}
/* 생성한 computer 객체를 반환 */
public Computer build() { return computer; }
}
Builder 클래스 내부를 먼저 살펴보면, 생성자를 통해 생성된 Computer 가 멤버 변수로 존재합니다.
그리고 각각의 구성요소인 Cpu, Ram, Graphics 를 set 할 수 있는 함수들이 정의되어 있는데,
이 함수들에서 return되는 반환형이 Builder 인 것이 핵심입니다.
마지막으로 Computer 객체를 반환하는 build()로 구성되어있습니다.
Builder 를 이용하여 어떻게 위 문제들을 해결 했는지 main() 코드를 살펴보겠습니다.
public class BuilderPattern {
public static void main(String[] args) {
/* 프리미엄 컴퓨터 */
Cpu cpu = new Cpu("Intel core i7");
Ram ram = new Ram("16GB");
Graphics graphics = new Graphics("RTX-3090");
/* Builder를 이용하여 원하는 구성으로 set하여 객체를 생성 */
Builder builder = new Builder();
Computer premiumCP = builder.setCpu(cpu)
.setRam(ram)
.setGraphics(graphics)
.build();
premiumCP.computerInfo();
/* 저가형 컴퓨터 */
cpu = new Cpu("Intel core i5");
ram = new Ram("8GB");
/* Builder를 이용하여 원하는 구성으로 set하여 객체를 생성 */
builder = new Builder();
Computer lowCostPc = builder.setCpu(cpu)
.setRam(ram)
.build();
lowCostPc.computerInfo();
}
}
먼저 Builder 를 생성하고 Computer 객체 생성을 Builder 를 통해 생성한 것을 알 수 있습니다.
main() 을 보시면 Builder 의 set함수 를 연속해서 호출하는 것을 확인할 수 있습니다.
그건 바로 해당 set 함수들의 return 이 Builder 였기 때문에 가능한 것이죠!
따라서 해당 Builder를 통해 원하는 구성요소로 set을 하고 최종적으로는 build() 를 호출하여 원하는 객체를 생성 할 수 있습니다.
이렇게 오늘은 빌더 패턴 을 이야기해봤는데요.
궁금하시거나 질문이 있으시면 댓글로 달아주세요 !!
감사합니다!
'Software Development > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴의 정석] 구조패턴 - 어댑터 패턴 (Adapter) (21) | 2020.12.27 |
---|---|
[디자인 패턴의 정석] 생성패턴 - 프로토 타입 패턴 (Prototype) (15) | 2020.12.19 |
[디자인 패턴의 정석] 생성패턴 - 싱글턴 패턴 (Singleton) (17) | 2020.12.15 |
[디자인 패턴의 정석] 생성패턴 - 추상 팩토리 패턴 (Abstract Factory) (23) | 2020.12.13 |
[디자인 패턴의 정석] 생성패턴 - 팩토리 메서드 패턴 (Factory Method) (25) | 2020.12.10 |