Builder Pattern

Yongs12 ㅣ 2024. 2. 3. 14:01

 

Builder 패턴 예시 (Computer)

#pragma once

/*=========================================
             Builder Pattern
=========================================*/

/*
복잡한 객체를 단계별로 구성할 수 있는 방법을 제공하여 객체의 구성 과정과 표현을 분리하여 
동일한 구성 과정으로 다양한 표현을 생성할 수 있게 한다.

장점
ㄴ 가독성 향상 : 객체 생성의 과정이 명확해져 가독성이 높아진다.
ㄴ 불변 객체 생성 : 불변 객체를 쉽게 만들 수 있어, 객체의 상태 변경을 방지할 수 있다.
ㄴ 유연성 : 다양한 구성 요소를 조합하여 객체를 생성할 수 있어, 다양한 상황에 맞게 객체를 쉽게 조정할 수 있다.

단점
ㄴ 복잡성 증가 : Builder 클래스를 추가함으로써 전체 코드 구조가 복잡해질 수 있다.
ㄴ 성능 문제 : Builder 패턴이 객체를 생성하는 과정에서 성능 저하를 초래할 수 있다.
*/


/*
Parts: 부품들의 기본 클래스
*/
class Parts
{
public:
    virtual ~Parts() {}

    virtual string getModelName() const = 0;
    virtual string getDescription() const = 0;
protected:
    string _model;
    string _description;
};

/*
CPU: CPU 부품 클래스
ECPUModel: CPU Model
*/

enum class ECPUModel
{
    AMD_RYZEN_5_5600X,
    AMD_RYZEN_7_5800X,
    AMD_RYZEN_9_5900X
};

class CPU : public Parts
{
public:
    CPU() {};
    CPU(ECPUModel model)
    {
        setModel(model);
    }
    CPU(const CPU& cpu)
    {
        this->_model = cpu._model;
        this->_description = cpu._description;
    }

public:
    void setModel(ECPUModel type)
    {
        switch (type)
        {
        case ECPUModel::AMD_RYZEN_5_5600X:
            _model = "AMD_RYZEN_5_5600X";
            _description = "6 Core 12 Threads";
            break;
        case ECPUModel::AMD_RYZEN_7_5800X:
            _model = "AMD_RYZEN_7_5800X";
            _description = "8 Core 16 Threads";
            break;
        case ECPUModel::AMD_RYZEN_9_5900X:
            _model = "AMD_RYZEN_9_5900X";
            _description = "12 Core 24 Threads";
            break;
        default:
            _model = "Unknown";
            _description = "해당하는 모델이 없습니다.";
            cerr << "Warning: " << _description << "\n";
            break;
        }
    }

    string getModelName() const override
    {
        return _model;
    }
    string getDescription() const override
    {
        return _description;
    }
};

/*
MainBoard: MainBoard 부품 클래스
EMainBoardModel: MainBoard Model
*/

enum class EMainBoardModel
{
    ASUS_A450,
    ASUS_B550,
    ASUS_X570
};

class MainBoard : public Parts
{
public:
    MainBoard() {};
    MainBoard(const MainBoard& mainBoard)
    {
        this->_model = mainBoard._model;
        this->_description = mainBoard._description;
    }
    MainBoard(EMainBoardModel model)
    {
        setModel(model);
    }


public:
    void setModel(EMainBoardModel type)
    {
        switch (type)
        {
        case EMainBoardModel::ASUS_A450:
            _model = "ASUS_A450";
            _description = "ASUS_A450 저가형";
            break;
        case EMainBoardModel::ASUS_B550:
            _model = "ASUS_B550";
            _description = "ASUS_B550 중급형";
            break;
        case EMainBoardModel::ASUS_X570:
            _model = "ASUS_X570";
            _description = "ASUS_X570 고급형";
            break;
        default:
            _model = "Unknown";
            _description = "해당하는 모델이 없습니다.";
            cerr << "Warning: " << _description << "\n";
            break;
        }
    }

    string getModelName() const override
    {
        return _model;
    }
    string getDescription() const override
    {
        return _description;
    }
};

 

Computer에서 Parts를 담을 포인터 변수를 가지고 있는다.

#pragma once

#include "Parts.h"

/*
완성 클래스
*/
class Computer
{
public:
    Computer() {}
    Computer(const Computer& computer)
    {
        _cpu = computer._cpu;
        _mainBoard = computer._mainBoard;
    }

    ~Computer() {}

public:
    void setCPU(CPU* cpu)
    {
        _cpu = cpu;
    }

    CPU* getCPU() const
    {
        return _cpu;
    }

    void setMainBoard(MainBoard* mainboard)
    {
        _mainBoard = mainboard;
    }

    MainBoard* getMainBoard() const
    {
        return _mainBoard;
    }

    void printConfig() const
    {
        cout << "Computer Configuration:\n";
        cout << "CPU: " << _cpu->getModelName() << " - " << _cpu->getDescription() << "\n";
        cout << "MainBoard: " << _mainBoard->getModelName() << " - " << _mainBoard->getDescription() << "\n";
    }

private:
    CPU* _cpu = nullptr;
    MainBoard* _mainBoard = nullptr;
};

 

ComputerBuilder에서 객체 생성

class IComputerBuilder
{
public:
    virtual ~IComputerBuilder() {}
    virtual unique_ptr<Computer> build() = 0;
};

class ComputerBuilder : public IComputerBuilder
{
public:
    ComputerBuilder() {}
    ComputerBuilder(Computer computer)
        : _computer(computer)
    {}

    ComputerBuilder(const ComputerBuilder& computerBuilder)
    {
        _computer = computerBuilder._computer;
    }

    ComputerBuilder& setCPU(ECPUModel cpuModel)
    {
        _computer.setCPU(new CPU(cpuModel));
        return *this;
    }

    ComputerBuilder& setMainBoard(EMainBoardModel mainBoardModel)
    {
        _computer.setMainBoard(new MainBoard(mainBoardModel));
        return *this;
    }

    unique_ptr<Computer> build()
    {
        try
        {
#if _DEBUG
            // 디버그중 잘못 생성 시 위치 확인
            assert(_computer.getCPU() != nullptr);
            assert(_computer.getMainBoard() != nullptr);
#else
            if (_computer.getCPU() == nullptr || _computer.getMainBoard() == nullptr)
            {
                // 런타임중 잘못된 빌더 사용 시 에러를 던진다.
                throw std::runtime_error("Error");
            }
#endif
            return make_unique<Computer>(_computer);
        }
        catch (...)
        {
            // 생성 에러가 발생하면 기본값으로 대체
            _computer.setCPU(new CPU(ECPUModel::AMD_RYZEN_5_5600X));
            _computer.setMainBoard(new MainBoard(EMainBoardModel::ASUS_A450));
            return make_unique<Computer>(_computer);
        }
    }

private:
    Computer _computer;
};

 

 

Builder 사용

#include "pch.h"
#include "Computer.h"

int main()
{
    ComputerBuilder builder;

    unique_ptr<Computer> myComputer = builder
        .setCPU(ECPUModel::AMD_RYZEN_9_5900X)
        .setMainBoard(EMainBoardModel::ASUS_X570)
        .build();

    myComputer->printConfig();

	return 0;
}

 

 

'Design Pattern' 카테고리의 다른 글

State Pattern  (0) 2024.02.06
Observer Pattern  (0) 2024.02.05
Prototype Pattern  (0) 2024.02.04
Factory Pattern  (0) 2024.02.02
Singleton Pattern  (0) 2024.02.01