디자인 패턴의 7가지 기본 원칙
단일 책임 원칙(SRP): 클래스는 하나의 기능만을 담당해야 합니다.
- 장점: 코드 유지보수성 향상 및 재사용 용이
- 단점: 과도한 분할로 인한 클래스 수 증가
인터페이스 분리 원칙(ISP): 인터페이스는 작고 집중적으로 설계되어야 합니다.
- 장점: 불필요한 메서드 제거로 유연성 향상
- 단점: 인터페이스 개수 증가
개방-폐쇄 원칙(OCP): 확장에는 열려있고 수정에는 닫혀 있어야 합니다.
- 장점: 시스템 확장성 향상 및 변경 리스크 감소
- 단점: 지나친 추상화로 복잡도 증가
의존성 역전 원칙(DIP): 구체 클래스보다 추상화에 의존해야 합니다.
- 장점: 모듈 간 결합도 감소 및 구현 교체 용이
- 단점: 추가적인 추상 계층 정의 필요
리스코프 치환 원칙(LSP): 자식 클래스는 부모 클래스를 완전히 대체 가능해야 합니다.
- 장점: 상속 체계의 논리적 일관성 보장
- 단점: 자식 클래스의 유연성 제약
합성 재사용 원칙(CRP): 상속보다는 조합을 우선적으로 사용합니다.
- 장점: 유연한 구조 및 높은 응집도
- 단점: 객체 관계 관리의 복잡성
최소 지식 원칙(LoD): 객체 간 상호작용을 최소화해야 합니다.
- 장점: 결합도 감소 및 모듈 독립성 향상
- 단점: 중간 매개 메서드 증가
생성 패턴(Creational Patterns)
싱글톤 패턴
클래스의 인스턴스가 오직 하나만 존재하도록 보장하는 패턴입니다.
#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <vector>
#include <functional>
class TaskManager {
public:
static TaskManager* getInstance(size_t workerCount);
template<typename Func, typename... Args>
void submitTask(Func&& func, Args&&... args);
~TaskManager();
private:
TaskManager(size_t count);
TaskManager(const TaskManager&) = delete;
TaskManager& operator=(const TaskManager&) = delete;
static TaskManager* instance;
std::vector<std::thread> workers;
std::queue<std::function<void()>> taskQueue;
std::mutex queueMutex;
std::condition_variable conditionVar;
bool shutdownFlag;
};
TaskManager* TaskManager::instance = nullptr;
TaskManager::~TaskManager() {
{
std::lock_guard<std::mutex> lock(queueMutex);
shutdownFlag = true;
}
conditionVar.notify_all();
for(auto& worker : workers) {
if(worker.joinable()) {
worker.join();
}
}
delete instance;
instance = nullptr;
}
TaskManager::TaskManager(size_t count) : shutdownFlag(false) {
for(size_t i = 0; i < count; ++i) {
workers.emplace_back([this]() {
while(true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex);
conditionVar.wait(lock, [this]() {
return !taskQueue.empty() || shutdownFlag;
});
if(shutdownFlag && taskQueue.empty()) {
return;
}
task = std::move(taskQueue.front());
taskQueue.pop();
}
task();
}
});
}
}
template<typename Func, typename... Args>
void TaskManager::submitTask(Func&& func, Args&&... args) {
auto task = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
{
std::lock_guard<std::mutex> lock(queueMutex);
taskQueue.emplace(std::move(task));
}
conditionVar.notify_one();
}
TaskManager* TaskManager::getInstance(size_t workerCount) {
static std::once_flag initFlag;
std::call_once(initFlag, [workerCount]() {
instance = new TaskManager(workerCount);
});
return instance;
}
팩토리 패턴
객체 생성 로직을 캡슐화하여 클라이언트와 구체 클래스 간의 결합도를 낮춥니다.
#include <iostream>
#include <memory>
#include <string>
class Device {
public:
virtual void powerOn() = 0;
virtual void powerOff() = 0;
virtual ~Device() = default;
};
class MobileDevice : public Device {
public:
void powerOn() override {
std::cout << "휴대폰 전원 켜짐\n";
}
void powerOff() override {
std::cout << "휴대폰 전원 꺼짐\n";
}
};
class DesktopDevice : public Device {
public:
void powerOn() override {
std::cout << "데스크톱 전원 켜짐\n";
}
void powerOff() override {
std::cout << "데스크톱 전원 꺼짐\n";
}
};
class DeviceFactory {
public:
virtual std::unique_ptr<Device> createDevice() = 0;
virtual ~DeviceFactory() = default;
};
class MobileFactory : public DeviceFactory {
public:
std::unique_ptr<Device> createDevice() override {
return std::make_unique<MobileDevice>();
}
};
class DesktopFactory : public DeviceFactory {
public:
std::unique_ptr<Device> createDevice() override {
return std::make_unique<DesktopDevice>();
}
};
추상 팩토리 패턴
관련된 객체들의 집합을 생성하기 위한 인터페이스를 제공합니다.
class DatabaseConnection {
public:
virtual void connect() = 0;
virtual ~DatabaseConnection() = default;
};
class DatabaseCommand {
public:
virtual void execute() = 0;
virtual ~DatabaseCommand() = default;
};
class MySQLConnection : public DatabaseConnection {
public:
void connect() override {
std::cout << "MySQL 연결\n";
}
};
class MySQLCommand : public DatabaseCommand {
public:
void execute() override {
std::cout << "MySQL 명령 실행\n";
}
};
class SQLServerConnection : public DatabaseConnection {
public:
void connect() override {
std::cout << "SQL Server 연결\n";
}
};
class SQLServerCommand : public DatabaseCommand {
public:
void execute() override {
std::cout << "SQL Server 명령 실행\n";
}
};
class DatabaseFactory {
public:
virtual std::unique_ptr<DatabaseConnection> createConnection() = 0;
virtual std::unique_ptr<DatabaseCommand> createCommand() = 0;
virtual ~DatabaseFactory() = default;
};
class MySQLFactory : public DatabaseFactory {
public:
std::unique_ptr<DatabaseConnection> createConnection() override {
return std::make_unique<MySQLConnection>();
}
std::unique_ptr<DatabaseCommand> createCommand() override {
return std::make_unique<MySQLCommand>();
}
};
class SQLServerFactory : public DatabaseFactory {
public:
std::unique_ptr<DatabaseConnection> createConnection() override {
return std::make_unique<SQLServerConnection>();
}
std::unique_ptr<DatabaseCommand> createCommand() override {
return std::make_unique<SQLServerCommand>();
}
};
구조 패턴(Structural Patterns)
어댑터 패턴
기존 인터페이스를 새로운 인터페이스로 변환합니다.
#include <iostream>
class ChargerInterface {
public:
virtual void chargeDevice(int voltage) = 0;
virtual ~ChargerInterface() = default;
};
class PhoneCharger {
public:
void charge(int inputA, int inputB) {
std::cout << "휴대폰 충전 중\n";
}
};
template<typename T>
class ChargerAdapter : public ChargerInterface {
public:
explicit ChargerAdapter(T& device) : targetDevice(device) {}
void chargeDevice(int voltage) override {
targetDevice.charge(voltage, voltage + 1);
}
private:
T& targetDevice;
};
프록시 패턴
실제 객체에 대한 접근을 제어하는 대리 객체를 제공합니다.
class PropertyInterface {
public:
virtual void lease() = 0;
virtual ~PropertyInterface() = default;
};
class Landlord : public PropertyInterface {
public:
void lease() override {
std::cout << "부동산 임대\n";
}
};
class RealEstateAgent : public PropertyInterface {
public:
explicit RealEstateAgent(Landlord& owner) : propertyOwner(owner) {}
void lease() override {
advertise();
propertyOwner.lease();
}
private:
void advertise() {
std::cout << "광고 시작\n";
}
Landlord& propertyOwner;
};
퍼사드 패턴
복잡한 서브시스템을 단순한 인터페이스로 제공합니다.
class StockInventory {
public:
bool isAvailable() const { return quantity > 0; }
private:
int quantity = 10;
};
class OrderProcessor {
public:
std::string generateOrderId() { return "ORD-12345"; }
};
class PaymentHandler {
public:
bool processPayment(const std::string& orderId) { return true; }
};
class DeliveryService {
public:
void dispatchPackage() { std::cout << "배송 시작\n"; }
};
class OrderFacade {
public:
void processOrder() {
if(inventory.isAvailable()) {
std::string orderId = processor.generateOrderId();
if(payment.processPayment(orderId)) {
delivery.dispatchPackage();
}
}
}
private:
StockInventory inventory;
OrderProcessor processor;
PaymentHandler payment;
DeliveryService delivery;
};
데코레이터 패턴
객체에 동적으로 새로운 기능을 추가합니다.
class GameCharacter {
public:
virtual int getAttackPower() = 0;
virtual ~GameCharacter() = default;
protected:
int basePower = 20;
};
class Warrior : public GameCharacter {
public:
int getAttackPower() override {
return basePower + 5;
}
};
class Mage : public GameCharacter {
public:
int getAttackPower() override {
return basePower;
}
};
class EquipmentDecorator : public GameCharacter {
public:
explicit EquipmentDecorator(GameCharacter* character) : decoratedCharacter(character) {}
protected:
GameCharacter* decoratedCharacter;
};
class WeaponDecorator : public EquipmentDecorator {
public:
explicit WeaponDecorator(GameCharacter* character) : EquipmentDecorator(character) {}
int getAttackPower() override {
return decoratedCharacter->getAttackPower() + 10;
}
};
class ArmorDecorator : public EquipmentDecorator {
public:
explicit ArmorDecorator(GameCharacter* character) : EquipmentDecorator(character) {}
int getAttackPower() override {
return decoratedCharacter->getAttackPower() + 5;
}
};
브리지 패턴
추상화와 구현을 분리하여 각각 독립적으로 확장할 수 있게 합니다.
class PresentationMethod {
public:
virtual void render() = 0;
virtual ~PresentationMethod() = default;
};
class VideoRenderer : public PresentationMethod {
public:
void render() override {
std::cout << "비디오 형식으로 렌더링\n";
}
};
class DocumentRenderer : public PresentationMethod {
public:
void render() override {
std::cout << "문서 형식으로 렌더링\n";
}
};
class Course {
public:
explicit Course(std::unique_ptr<PresentationMethod> method) : renderingMethod(std::move(method)) {}
virtual void present() = 0;
protected:
std::unique_ptr<PresentationMethod> renderingMethod;
};
class ProgrammingCourse : public Course {
public:
explicit ProgrammingCourse(std::unique_ptr<PresentationMethod> method) : Course(std::move(method)) {}
void present() override {
std::cout << "프로그래밍 강의\n";
renderingMethod->render();
}
};
class DesignCourse : public Course {
public:
explicit DesignCourse(std::unique_ptr<PresentationMethod> method) : Course(std::move(method)) {}
void present() override {
std::cout << "디자인 강의\n";
renderingMethod->render();
}
};
행동 패턴(Behavioral Patterns)
템플릿 메서드 패턴
알고리즘의 구조를 정의하고 하위 클래스에서 특정 단계를 구현하도록 합니다.
class BeveragePreparation {
public:
void prepare() {
boilWater();
addIngredients();
steep();
serve();
}
protected:
void boilWater() {
std::cout << "물 끓이기\n";
}
virtual void addIngredients() = 0;
void steep() {
std::cout << "우려내기\n";
}
void serve() {
std::cout << "컵에 따르기\n";
}
};
class TeaPreparation : public BeveragePreparation {
protected:
void addIngredients() override {
std::cout << "차 잎 추가\n";
}
};
class CoffeePreparation : public BeveragePreparation {
protected:
void addIngredients() override {
std::cout << "커피 가루 추가\n";
}
};
전략 패턴
알고리즘군을 정의하고 캡슐화하여 상호교환이 가능하게 합니다.
class PricingStrategy {
public:
virtual double calculateFinalPrice(double original) = 0;
virtual ~PricingStrategy() = default;
};
class BulkDiscount : public PricingStrategy {
private:
double threshold, discountAmount;
public:
BulkDiscount(double minAmount, double reduction)
: threshold(minAmount), discountAmount(reduction) {}
double calculateFinalPrice(double original) override {
return original >= threshold ? original - discountAmount : original;
}
};
class PercentageDiscount : public PricingStrategy {
private:
double rate;
public:
explicit PercentageDiscount(double discountRate) : rate(discountRate) {}
double calculateFinalPrice(double original) override {
return original * rate;
}
};
class VoucherDiscount : public PricingStrategy {
private:
double voucherValue;
public:
explicit VoucherDiscount(double value) : voucherValue(value) {}
double calculateFinalPrice(double original) override {
return std::max(0.0, original - voucherValue);
}
};
class ShoppingBasket {
public:
explicit ShoppingBasket(std::unique_ptr<PricingStrategy> strategy)
: currentStrategy(std::move(strategy)) {}
void changeStrategy(std::unique_ptr<PricingStrategy> newStrategy) {
currentStrategy = std::move(newStrategy);
}
void checkout(double price) {
double final = currentStrategy->calculateFinalPrice(price);
std::cout << "최종 금액: " << final << "원\n";
}
private:
std::unique_ptr<PricingStrategy> currentStrategy;
};
옵저버 패턴
객체 상태 변화를 다른 객체들에게 알리는 메커니즘을 제공합니다.
#include <vector>
#include <algorithm>
class MarketObserver;
class StockSubject {
protected:
std::vector<MarketObserver*> observers;
double currentPrice;
public:
void addObserver(MarketObserver* observer) {
observers.push_back(observer);
}
void removeObserver(MarketObserver* observer) {
auto it = std::find(observers.begin(), observers.end(), observer);
if(it != observers.end()) {
observers.erase(it);
}
}
void notifyObservers() {
for(auto* observer : observers) {
observer->update(currentPrice);
}
}
void setPrice(double price) {
currentPrice = price;
notifyObservers();
}
};
class MarketObserver {
public:
virtual void update(double price) = 0;
virtual ~MarketObserver() = default;
};
class IndividualInvestor : public MarketObserver {
private:
std::string investorName;
public:
explicit IndividualInvestor(const std::string& name) : investorName(name) {}
void update(double price) override {
std::cout << investorName << ": 주가 " << price << "원 확인\n";
}
};
class InstitutionalInvestor : public MarketObserver {
private:
std::string fundName;
public:
explicit InstitutionalInvestor(const std::string& name) : fundName(name) {}
void update(double price) override {
std::cout << fundName << ": 주가 " << price << "원, 분석 시작...\n";
}
};