Menu
Coddy logo textTech

빌더 패턴

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 93번째.

빌더 패턴(Builder pattern)은 복잡한 객체의 생성 과정을 그 표현 방식과 분리하여, 단계별로 다양한 구성을 만들 수 있게 해줍니다. 이는 객체에 많은 선택적 매개변수가 있거나 특정한 생성 순서가 필요할 때 특히 유용합니다.

많은 매개변수를 가진 생성자(혼란스러울 수 있음) 대신, 빌더(Builder)는 각 구성 옵션에 대해 명확하고 이름이 지정된 메서드를 제공합니다:

#include <iostream>
#include <string>

class Pizza {
public:
    std::string dough;
    std::string sauce;
    std::string topping;
    bool cheese;
    
    void describe() const {
        std::cout << dough << " dough, " << sauce << " sauce, "
                  << topping << (cheese ? ", with cheese" : "") << "\n";
    }
};

class PizzaBuilder {
private:
    Pizza pizza;
    
public:
    PizzaBuilder& setDough(const std::string& d) {
        pizza.dough = d;
        return *this;
    }
    
    PizzaBuilder& setSauce(const std::string& s) {
        pizza.sauce = s;
        return *this;
    }
    
    PizzaBuilder& setTopping(const std::string& t) {
        pizza.topping = t;
        return *this;
    }
    
    PizzaBuilder& addCheese() {
        pizza.cheese = true;
        return *this;
    }
    
    Pizza build() { return pizza; }
};

int main() {
    Pizza margherita = PizzaBuilder()
        .setDough("thin")
        .setSauce("tomato")
        .setTopping("basil")
        .addCheese()
        .build();
    
    margherita.describe();
}

각 세터(setter) 메서드는 빌더에 대한 참조를 반환하며 (return *this), 플루언트 인터페이스(fluent interface)를 위한 메서드 체이닝을 가능하게 합니다. build() 메서드는 생성된 객체를 최종적으로 완성하고 반환합니다.

선택적 구성 요소가 많은 객체를 생성할 때, 가독성 있는 생성 코드를 원할 때, 또는 동일한 생성 프로세스가 서로 다른 표현을 생성해야 할 때 Builder를 사용하세요.

challenge icon

챌린지

쉬움

사용자가 맞춤형 PC를 단계별로 구성할 수 있는 Computer Builder 시스템을 만들어 봅시다. 이는 빌더(Builder) 패턴을 적용하기에 완벽한 시나리오입니다. 컴퓨터는 많은 선택적 구성 요소를 가지고 있으며, 우리는 다양한 구성을 조립하기 위한 깔끔하고 가독성 있는 방법이 필요합니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • Computer.h: 완전히 구성된 컴퓨터를 나타내는 제품 클래스를 정의합니다.

    Computer 클래스는 cpu (string), ram (string), storage (string), gpu (string), hasWifi (boolean)를 private 멤버로 저장해야 합니다. hasWififalse로, 문자열들은 기본적으로 빈 값으로 초기화하세요.

    컴퓨터의 구성을 표시하는 showSpecs()라는 public 메서드를 추가하세요. 설정된 각 구성 요소(비어 있지 않은 문자열)에 대해 각 줄에 하나씩 출력합니다. Wifi 기능은 활성화된 경우에만 출력되어야 합니다.

  • ComputerBuilder.h: 플루언트 인터페이스(fluent interface)를 사용하여 컴퓨터를 구축하는 빌더 클래스를 생성합니다.

    ComputerBuilder 클래스는 구축할 Computer 객체를 private 멤버로 가져야 합니다. 메서드 체이닝이 가능하도록 각각 빌더에 대한 참조를 반환하는 다음 메서드들을 구현하세요:

    • setCPU(const std::string& cpu)
    • setRAM(const std::string& ram)
    • setStorage(const std::string& storage)
    • setGPU(const std::string& gpu)
    • addWifi() — wifi 기능을 활성화합니다.

    또한 완성된 Computer 객체를 반환하고 잠재적인 재사용을 위해 빌더를 초기화하는 build() 메서드를 구현하세요.

  • main.cpp: 모든 것을 결합하여 맞춤형 컴퓨터를 구축합니다.

    네 가지 입력을 읽습니다:

    1. CPU 모델 (string)
    2. RAM 사양 (string)
    3. 저장 장치 사양 (string)
    4. Wifi 포함 여부: yes 또는 no

    ComputerBuilder를 사용하여 제공된 CPU, RAM, 저장 장치로 컴퓨터를 구축하세요. 만약 wifi 입력이 yes라면 addWifi()도 호출하세요. 이 구성에서는 GPU가 지정되지 않았음에 유의하세요. 빌더는 선택적 구성 요소를 매끄럽게 처리해야 합니다.

    구축 후, 결과물인 컴퓨터에서 showSpecs()를 호출하여 구성을 표시하세요.

showSpecs() 메서드는 다음 형식으로 출력해야 합니다 (설정된 구성 요소만 표시):

CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: Enabled

예를 들어, 입력값이 Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD, yes인 경우:

CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: Enabled

입력값이 AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD, no인 경우:

CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSD

빌더 패턴이 구축 과정을 얼마나 가독성 있고 유연하게 만드는지 확인해 보세요. 구성 요소를 쉽게 추가하거나 건너뛸 수 있으며, 메서드 체이닝은 명확하고 자기 문서화된(self-documenting) 코드를 생성합니다. GPU를 설정하지 않았기 때문에 GPU 줄이 나타나지 않으며, 이는 빌더가 선택적 구성 요소를 자연스럽게 처리하는 방식을 보여줍니다.

치트 시트

빌더 패턴(Builder pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 단계별 설정을 가능하게 합니다. 이는 객체에 많은 선택적 매개변수가 있거나 특정 생성 순서가 필요할 때 유용합니다.

매개변수가 많은 생성자 대신, 빌더는 각 설정 옵션에 대해 명확한 이름을 가진 메서드를 제공합니다:

class Pizza {
public:
    std::string dough;
    std::string sauce;
    std::string topping;
    bool cheese;
};

class PizzaBuilder {
private:
    Pizza pizza;
    
public:
    PizzaBuilder& setDough(const std::string& d) {
        pizza.dough = d;
        return *this;  // 메서드 체이닝 가능하게 함
    }
    
    PizzaBuilder& setSauce(const std::string& s) {
        pizza.sauce = s;
        return *this;
    }
    
    PizzaBuilder& setTopping(const std::string& t) {
        pizza.topping = t;
        return *this;
    }
    
    PizzaBuilder& addCheese() {
        pizza.cheese = true;
        return *this;
    }
    
    Pizza build() { return pizza; }
};

// 메서드 체이닝을 사용한 예시
Pizza margherita = PizzaBuilder()
    .setDough("thin")
    .setSauce("tomato")
    .setTopping("basil")
    .addCheese()
    .build();

각 세터(setter) 메서드는 플루언트 인터페이스(fluent interface)를 위한 메서드 체이닝을 가능하게 하도록 return *this를 반환합니다. build() 메서드는 최종적으로 구성된 객체를 완성하여 반환합니다.

많은 선택적 구성 요소가 있는 객체를 생성할 때, 가독성 좋은 생성 코드를 원할 때, 또는 동일한 생성 프로세스로 다양한 표현을 만들어야 할 때 빌더 패턴을 사용하세요.

직접 해보기

#include <iostream>
#include <string>
#include "ComputerBuilder.h"

using namespace std;

int main() {
    // 입력 읽기
    string cpu, ram, storage, wifiChoice;
    getline(cin, cpu);
    getline(cin, ram);
    getline(cin, storage);
    getline(cin, wifiChoice);

    // TODO: ComputerBuilder 인스턴스 생성
    
    // TODO: 메서드 체이닝을 사용하여 CPU, RAM, storage 설정
    
    // TODO: wifiChoice가 "yes"인 경우, addWifi()도 호출
    
    // TODO: build()를 호출하여 Computer 객체 가져오기
    
    // TODO: 빌드된 컴퓨터에서 showSpecs()를 호출하여 구성 표시

    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨