
Object Oriented Design 에서
- 유지보수성,
- 확장성,
- 유연성을
높이기 위해 제안된 다섯 가지 핵심 설계 원칙!
Single Responsibility Principle (SRP),
Open/Close Principle (OCP),
Liskov Substitution Principle (LSP),
Interface Segregaton Principle (ISP),
Dependency Inversion Principle (DIP).
참고로 이와 함께 중요한 것으로는 OOP Language가 최소로 가져야 하는 3대요소인
- Inheritance,
- Encapsulation,
- Polymorphism
이 있음: 역시 함께 기억해야하는 주요 부분임.
참고로, Abstraction을 추가해서 OOP 언어의 4대요소라고도 하지만,
OOP에선 Encapsulation을 통해 Abstraction을 달성한다고 보므로
(Abstraction은 Encapsulation의 목표 또는 결과로 봄)
3가지만 언급하는 문헌이 더 많음.
https://dsaint31.me/mkdocs_site/python/oop/oop_0_00_OOP/
BME
Object Oriented Programming (OOP) OOP란? OOP는 Object 에 기반 하여, Object 를 이용 하고 Object 를 만들고(정의 및 구현), Object 를 조합 하여 프로그래밍 하는 Program paradigm의 하나. 을 가르킴. Program Paradigm 의 관
dsaint31.me
1. Single Responsibility Principle (SRP)
“하나의 Class는 하나만 책임진다.”
- 클래스는 가급적 하나의 책임만 가져야 하며, 오직 그 책임에 대해서만 변경되어야 함.
- 때문에 변경되어야 하는 이유도 오직 하나여야 함.
- 예: 사용자 정보를 저장하는 클래스가 화면 UI도 직접 처리하도록 구현시 SRP 을 어기는 것임.
2. Open/Closed Principle (OCP)
“코드는 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.”
- 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있도록 설계되어야 함.
- 기존 코드의 수정은 버그를 발생시키는 위험요소에 해당함.
- 새로운 클래스를 추가하거나 orver-ridding를 해서 기능을 늘려야 한다.
- 예: 조건문 대신 Polymorphism(다형성)을 사용해 새로운 동작을 쉽게 확장할 수 있어야 함.
3. Liskov Substitution Principle (LSP)
“자식 클래스는 부모 클래스의 역할을 완전히 대체할 수 있어야 한다.”
- 부모 클래스를 사용하는 코드에서 부모를 자식 클래스로 대체되어도 정상적으로 작동해야 함을 의미.
- 자신의 기반 클래스가 사용된 코드에서 자신의 기반 클래스 객체를 대체해도 동작해야 함.
- 예:
- Rectangle 클래스 객체를 사용하는 코드에서
- 이를 상속한 Square 클래스 객체를 넣었을 때 동작이 달라진다면,
- 이는 LSP를 위반한 것임.
OOP의 is-a 관계를 기억할 것: Student is a Human! 이므로 Human이 부모, Student가 자식임. 역은 성립하지 않음.
4. Interface Segregation Principle (ISP)
“구현 클래스는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 함.”
- 이를 위해선, 하나의 거대한 만능 인터페이스보다, 작고 명확한 여러 인터페이스들로 분리하여야 함.
- 예:
- 프린터 인터페이스가
scan(),fax()까지 강제하면 - 단순 프린터 구현 클래스는 필요 없는 기능까지 구현하게 됨 (ISP위반사례).
- 프린터 인터페이스가
5. Dependency Inversion Principle (DIP)
“고수준 모듈은 저수준 모듈에 의존 (전통적인 방식)해서는 안 되며, 가급적 둘 다 추상화에 의존해야 한다.”
- 구현이 아닌 추상(인터페이스, 추상 클래스)에 의존하도록 설계하여야, 유연하고 테스트 가능한 구조를 만듬.
- 예:
- 클래스가
Database클래스에 직접 의존하는 대신, DataStorageInterface에 의존하게 하여 유연성 확보.
- 클래스가
다르게 동작해야하는 메서드는 자식 클래스에서 다시 구현하는 것이 추천됨
만약 자식 클래스에서 완전 동일한 코드로 충분한 경우엔 abstract class(추상클래스)에 기본 구현으로 추가하는 방향으로 처리.
과거 Business Logic을 구현하는 고수준 모듈(예: 보고서 생성하는 ReportGenerator 클래스)의 경우, 실제 보고서를 저장하고 위해 특정 구현의 저수준 모듈(예: MySQLDatabase 클래스)들에 의존적인 경우가 많았음.
전통적으로는 고수준 모듈이 저수준 모듈에 지시를 내리거나 사용하는 구조임.
DIP는 이를 뒤집어 저수준 모듈들이
고수준 모듈들이 요구하는 abstract class(or Interface)를 따르도록 함.
전통적인 경우:
class MySQLDatabase:
def save(self, data):
print("Saving to MySQL")
class ReportGenerator:
def __init__(self):
self.db = MySQLDatabase() # 직접 의존
def generate(self):
# ...
self.db.save("data")
DIP를 적용한 경우:
from abc import ABC, abstractmethod
class DataStorage(ABC): # 추상 계층
@abstractmethod
def save(self, data):
pass
class MySQLDatabase(DataStorage):
def save(self, data):
print("Saving to MySQL")
class ReportGenerator:
def __init__(self, storage: DataStorage):
self.storage = storage # 추상 계층에 의존
def generate(self):
# ...
self.storage.save("data")
- @abstractmethod 데코레이터는 특정 method가 반드시 자식 클래스에서 구현되어야 함을 강제함..
- 이 데코레이터는 abc.ABC를 상속하는 클래스에서만 의미를 가짐.
- 자식 클래스에 구현을 안할 경우 TypeError가 발생함.
위의 예에서 DIP 구현을 다이아그램으로 보면 다음과 같음
고수준 모듈 (Business Logic) 저수준 모듈 (구현체)
↘ ↙
공통 추상화 계층 (인터페이스 / 추상 클래스)'Python' 카테고리의 다른 글
| [Ex] scope 이해. (0) | 2025.05.12 |
|---|---|
| [PySide] QtCore.QSettings 사용법 (0) | 2025.05.12 |
| [DL] default collate_fn - PyTorch (0) | 2025.04.26 |
| [Py] importlib.metadata: Package 정보 확인 (0) | 2025.04.23 |
| [Programming] Control Flow 와 Control Structure (1) | 2025.04.23 |