SOLID 원칙은 객체 지향 설계(OOP)의 다섯 가지 주요 원칙을 의미한다.
이 원칙들은 코드의 유연성, 확장성, 유지보수성을 높이는데 도움을 준다.
SOLID 원칙을 사용하여 좋은 코드로 구현해보자.
Single Responsibility Principle (단일 책임 원칙)
클래스는 하나의 책임만 가져야 한다.
즉, 하나의 클래스는 오직 하나의 변경 이유만 가져야 한다.
클래스의 역할을 명확히 하고, 변경에 따른 영향을 최소화한다.
class ReportGenerator {
public:
void generateReport() {
// 리포트를 생성하는 로직
}
};
class ReportPrinter {
public:
void printReport() {
// 리포트를 출력하는 로직
}
};
잘못된 사례에서는 리포트를 생성하고 출력하는 작업을 하나의 클래스에 넣을 수 있지만,
이를 분리하면 책임이 명확해지고 코드 관리가 쉬워진다.
Open/Closed Principle (개방-폐쇄 원칙)
클래스는 확장에는 열려 있어야 하지만, 수정에는 닫혀 있어야 한다.
즉, 기존 코드를 변경하지 않고 기능을 확장할 수 있어야 한다.
기존 코드를 건드리지 않고 새로운 기능을 추가하도록 한다.
class Shape {
public:
virtual void draw() = 0; // 추상 메서드
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() override {
// 원을 그리는 로직
}
};
class Rectangle : public Shape {
public:
void draw() override {
// 사각형을 그리는 로직
}
};
void renderShape(Shape& shape) {
shape.draw();
}
새로운 도형을 추가할 때 기존 코드(예: renderShape)를 수정하지 않고 확장할 수 있어야한다.
Liskov Substitution Principle (리스코프 치환 원칙)
서브클래스는 언제나 부모 클래스의 행위를 대체할 수 있어야 한다.
즉, 부모 클래스 객체가 사용되는 모든 곳에 자식 클래스 객체를 사용할 수 있어야 한다.
다형성을 보장하고 코드의 신뢰성을 유지한다.
class Bird {
public:
virtual void fly() = 0;
virtual ~Bird() = default;
};
class Sparrow : public Bird {
public:
void fly() override {
// 참새가 나는 로직
}
};
class Penguin : public Bird {
public:
void fly() override {
throw std::logic_error("Penguins cannot fly");
}
};
Interface Segregation Principle (인터페이스 분리 원칙)
인터페이스는 특정 클라이언트에 맞게 최소한으로 분리해야 한다.
클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
불필요한 의존성을 제거하고 유연성을 높인다.
class Printer {
public:
virtual void print() = 0;
virtual ~Printer() = default;
};
class Scanner {
public:
virtual void scan() = 0;
virtual ~Scanner() = default;
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void print() override {
// 출력 로직
}
void scan() override {
// 스캔 로직
}
};
하나의 인터페이스에 print와 scan을 모두 포함시키게 된다면,
프린터만 필요한 클래스도 불필요하게 scan 메서드를 구현해야한다.
Dependency Inversion Principle (의존성 역전 원칙)
고수준 모듈은 저수준 모듈에 의존해서는 안 된다.
둘 다 추상화에 의존해야 한다. 또한 추상화는 세부사항에 의존하지 않아야 한다.
구현 세부사항이 아닌 추상화에 의존하도록 하여 유연성과 테스트 용이성을 높인다.
class IMessageSender {
public:
virtual void sendMessage(const std::string& message) = 0;
virtual ~IMessageSender() = default;
};
class EmailSender : public IMessageSender {
public:
void sendMessage(const std::string& message) override {
// 이메일 발송 로직
}
};
class Notification {
IMessageSender& sender;
public:
Notification(IMessageSender& sender) : sender(sender) {}
void notify(const std::string& message) {
sender.sendMessage(message);
}
};
Notification 클래스는 특정 구현(EmailSender)이 아니라 추상화(IMessageSender)에 의존하기 때문에,
쉽게 다른 메시지 발송 방법으로 교체할 수 있다.
'C++' 카테고리의 다른 글
CS) 싱글톤 패턴 (1) | 2025.01.08 |
---|---|
CS) 가상함수, 순수가상함수 (0) | 2025.01.07 |
CS) 스마트포인터 (0) | 2025.01.07 |
CS) Include Guards (0) | 2025.01.06 |
CS) const와 참조 (const and Reference ) (0) | 2025.01.03 |