
SOLID 원칙 개념
SOLID 원칙은 객체지향 프로그래밍에서 유지보수성과 확장성을 높이기 위해 적용되는 다섯 가지 주요 설계 원칙으로,
로버트 C. 마틴(Robert C. Martin)이 정의하였으며, 마이클 페더스(Michael Feathers)가 이를 기억하기 쉽게 두문자어(SOLID)로 정리한 원칙
목적
- 유지보수성 및 확장성 향상
- 변경에 유연하게 함
단일 책임 원칙(SRP) - Single Responsibility
- 각 클래스는 하나의 책임을 가져야 한다는 원칙
- 클래스의 역할과 책임을 명확히 분리해서 변경이 꼭 필요한 경우에만 필요한 클래스에 수정
//단일 책임 원칙이 제대로 적용되지 않은 코드
#include <iostream>
#include <string>
//Student는 Student의 정보값만을 받는 클래스여야 한다
class Student {
public:
void setName(const std::string& name) {
this->name = name;
}
void displayDetails() {
std::cout << "Student Name: " << name << std::endl;
}
void calculateGrade(int score) {
if (score >= 90) {
std::cout << "Grade: A" << std::endl;
} else if (score >= 80) {
std::cout << "Grade: B" << std::endl;
} else {
std::cout << "Grade: C" << std::endl;
}
}
private:
std::string name;
};
// 단일 책임 원칙이 제대로 적용된 코드
#include <iostream>
#include <string>
// 학생 정보 관리 클래스
class Student {
public:
void setName(const std::string& name) {
this->name = name;
}
std::string getName() const {
return name;
}
private:
std::string name;
};
// 성적 계산 클래스
class GradeCalculator {
public:
void calculateGrade(int score) {
if (score >= 90) {
std::cout << "Grade: A" << std::endl;
} else if (score >= 80) {
std::cout << "Grade: B" << std::endl;
} else {
std::cout << "Grade: C" << std::endl;
}
}
};
// 출력 클래스
class StudentPrinter {
public:
void displayDetails(const Student& student) {
std::cout << "Student Name: " << student.getName() << std::endl;
}
};
개방 폐쇄 원칙(OCP) - Open Closed Principle
- 확장에는 열려 있어야 하고, 수정에는 닫혀있어야 한다는 개념
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있도록 하는게 목적
// 개방 폐쇄 원칙이 제대로 적용되지 않은 코드
class ShapeManager {
public:
void drawShape(int shapeType) {
if (shapeType == 1) {
// 원 그리기
} else if (shapeType == 2) {
// 사각형 그리기
}
}
};
// 개방 폐쇄 원칙이 제대로 적용된 코드
class Shape {
public:
virtual void draw() = 0; // 순수 가상 함수
};
class Circle : public Shape {
public:
void draw() {
// 원 그리기
}
};
class Square : public Shape {
public:
void draw() {
// 사각형 그리기
}
};
class ShapeManager {
public:
void drawShape(Shape& shape) {
shape.draw(); // 다형성 활용
}
};
리스코프 치환 원칙(LSP) - Liskov Subsitution Principle
- 자식 클래스는 부모 클래스에서 기대되는 행동을 보장해야한다는 원칙
// 리스코프 치환이 제대로 적용되지 않은 코드
#include <iostream>
class Rectangle {
public:
virtual void setWidth(int w) { width = w; }
virtual void setHeight(int h) { height = h; }
int getWidth() const { return width; }
int getHeight() const { return height; }
int getArea() const { return width * height; }
private:
int width = 0;
int height = 0;
};
class Square : public Rectangle {
public:
void setWidth(int w) override {
Rectangle::setWidth(w);
Rectangle::setHeight(w); // 정사각형은 너비와 높이가 같아야 함
}
void setHeight(int h) override {
Rectangle::setHeight(h);
Rectangle::setWidth(h); // 정사각형은 너비와 높이가 같아야 함
}
};
void testRectangle(Rectangle& rect) {
rect.setWidth(5);
rect.setHeight(10);
std::cout << "Expected area: 50, Actual area: " << rect.getArea() << std::endl;
}
int main() {
Rectangle rect;
testRectangle(rect); // Expected area: 50
Square square;
testRectangle(square); // Expected area: 50, Actual area: 100 (문제 발생)
return 0;
}
// 리스코프 치환 원칙이 제대로 적용된 코드
#include <iostream>
class Shape {
public:
virtual int getArea() const = 0; // 넓이를 계산하는 순수 가상 함수
};
class Rectangle : public Shape {
public:
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
int getWidth() const { return width; }
int getHeight() const { return height; }
int getArea() const override { return width * height; }
private:
int width = 0;
int height = 0;
};
class Square : public Shape {
public:
void setSide(int s) { side = s; }
int getSide() const { return side; }
int getArea() const override { return side * side; }
private:
int side = 0;
};
void testShape(Shape& shape) {
std::cout << "Area: " << shape.getArea() << std::endl;
}
int main() {
Rectangle rect;
rect.setWidth(5);
rect.setHeight(10);
testShape(rect); // Area: 50
Square square;
square.setSide(7);
testShape(square); // Area: 49
return 0;
}
인터페이스 분리 원칙(ISP) - Interface Segregation Principle
- 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다는 원칙
- 각 클래스에는 불필요한 메서드를 구현하지 말아야한다는 의미
// 인터페이스 분리 원칙이 제대로 적용되지 않은 코드
class Machnine {
private:
public:
Machnine() {}
void print() {
//세부 기능 구현
}
void scan() {
//세부 기능 구현
}
};
// 인터페이스 분리 원칙이 제대로 적용된 코드
class Printer {
public:
virtual void print() = 0;
};
class Scanner {
public:
virtual void scan() = 0;
};
class BasicPrinter : public Printer {
public:
void print() override {
// 문서 출력
}
};
class MultiFunctionDevice {
private:
Printer* printer;
Scanner* scanner;
public:
MultiFunctionDevice(Printer* p, Scanner* s) : printer(p), scanner(s) {}
void print() {
if (printer) printer->print();
}
void scan() {
if (scanner) scanner->scan();
}
};
의존 역전 원칙(DIP) - Dependency Inversion Principle
- 고수준 모듈(인터페이스)은 저수준 모듈(인터페이스를 구현하는 클래스)에 직접 의존하는 것이 아니라, 두 모듈 모두 추상화에 의존해야 한다는 원칙
// 의존 역전 원칙이 제대로 적용되지 않은 코드
class Keyboard {
public:
std::string getInput() {
return "입력 데이터";
}
};
class Monitor {
public:
void display(const std::string& data) {
// 출력
}
};
class Computer {
Keyboard keyboard;
Monitor monitor;
public:
void operate() {
std::string input = keyboard.getInput();
monitor.display(input);
}
};
// 의존 역전 원칙이 제대로 적용된 코드
class InputDevice {
public:
virtual std::string getInput() = 0;
};
class OutputDevice {
public:
virtual void display(const std::string& data) = 0;
};
class Keyboard : public InputDevice {
public:
std::string getInput() override {
return "키보드 입력 데이터";
}
};
class Monitor : public OutputDevice {
public:
void display(const std::string& data) override {
// 화면에 출력
}
};
class Computer {
private:
InputDevice* inputDevice;
OutputDevice* outputDevice;
public:
Computer(InputDevice* input, OutputDevice* output)
: inputDevice(input), outputDevice(output) {}
void operate() {
std::string data = inputDevice->getInput();
outputDevice->display(data);
}
};'컴퓨터지식(CS)' 카테고리의 다른 글
| 게임 플로우차트(FlowChart) 작성 (0) | 2025.06.26 |
|---|---|
| STL Container 시간 복잡도 및 특징비교 (0) | 2021.10.02 |