객체지향프로그래밍의 3요소 5원칙 #2 - 5원칙
개요
객체지향 프로그래밍(OOP)의 SOLID 원칙은 객체지향 프로그래밍에서 설계의 유연성과 확장성을 높이기 위한 다섯 가지 중요한 원칙을 뜻한다.
- SRP (Single responsibility principle) 단일 책임 원칙
- OCP (Open-closed principle) 개방 폐쇄 원칙
- LSP (Liskov substitution principle) 리스코브 치환 원칙
- ISP (Interface segregation principle) 인터페이스 분리 원칙
- DIP (Dependency inversion principle) 의존 역전 원칙
이번 글에서는 이 다섯 가지 원칙을 간단한 설명과 코드 예제로 알아보자.
1. 단일 책임 원칙
(SRP, Single Responsibility Principle)
클래스는 하나의 기능만 가져야 한다.
예제
리포트 생성과 저장의 책임을 별도 클래스로 분리.
class ReportGenerator {
public:
void generate() { /* 리포트 생성 로직 */ }
};
class ReportSaver {
public:
void save() { /* 리포트 저장 로직 */ }
};
2. 개방-폐쇄 원칙
(OCP, Open-closed principle)
클래스는 확장에 열려 있고, 수정에는 닫혀 있어야 한다.
예제
새로운 도형을 추가할 때 기존 코드를 수정할 필요가 없다.
class Shape {
public:
virtual double getArea() const = 0; // 인터페이스
};
class Circle : public Shape {
public:
double radius;
Circle(double r) : radius(r) {}
double getArea() const override { return 3.14 * radius * radius; }
};
class Rectangle : public Shape {
public:
double width, height;
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const override { return width * height; }
};
3. 리스코프 치환 원칙
(LSP, Liskov substitution principle)
자식 클래스는 부모 클래스를 대체할 수 있어야 한다.
예제
Sparrow
는 Bird
처럼 동작하며, 부모 클래스의 인터페이스를 그대로 사용할 수 있다.
class Bird {
public:
virtual void fly() { cout << "Flying!" << endl; }
};
class Sparrow : public Bird {};
4. 인터페이스 분리 원칙
(ISP, Interface segregation principle)
클래스가 사용하지 않는 인터페이스는 구현하지 않아야 하며 의존하지 않아야 한다. 또한, 클래스가 다른 클래스에 종속적일 때에는 가능한최소한의 인터페이스만을 사용해야 한다. 하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.
예제
프린터와 스캐너 기능을 분리하여 필요에 따라 인터페이스를 구현할 수 있다.
class Printer {
public:
virtual void print() = 0;
};
class Scanner {
public:
virtual void scan() = 0;
};
5. 의존 역전 원칙
(DIP, Dependency Inversion Principle)
모듈 간의 직접적인 의존을 끊고 상위 레벨 모듈에서 정의한 추상을 하위레벨 모듈이 구현하게 하는 원칙. 결과적으로 상위 레벨 모듈과 하위 레벨 모두 추상클래스에 의존하게 되며 변화에 쉽게 영향받지 않게 된다.
즉, 외부에서 의존성을 주입함으로써 의존성을 줄이는 것이며 모듈 간의 관계를 최대한 느슨하게 만드는 것이 원칙이다.
예제
Car
는 구체적인 엔진(GasEngine
)에 의존하지 않고, 추상화된 Engine
인터페이스에 의존합니다.
class Engine {
public:
virtual void start() = 0;
};
class GasEngine : public Engine {
public:
void start() override { cout << "Starting gas engine" << endl; }
};
class Car {
Engine* engine;
public:
Car(Engine* eng) : engine(eng) {}
void startCar() { engine->start(); }
};
결론
SOLID 원칙을 준수하면 객체지향 설계의 품질이 크게 향상된다. 유연하고 확장 가능한 소프트웨어를 설계하기 위해 이 원칙들을 기억하고 적용해야 한다.