2024. 4. 10. 02:33ㆍ정보처리,전산/소프트웨어공학
객체지향 프로그래밍에서 SOLID는 소프트웨어 설계 원칙의 다섯 가지 기본 원칙을 의미한다. 이 원칙들은 소프트웨어를 더욱 견고하고 유연하게 만들어준다. 각 원칙은 다음과 같이 설명된다:
1. 단일 책임 원칙 (Single Responsibility Principle, SRP): 하나의 클래스는 단 하나의 책임만 가져야 한다. 클래스가 여러 책임을 가지게 되면 변경이 발생했을 때 다른 책임에 영향을 미치게 될 수 있으며, 이는 유지보수성을 저하시킨다.
- SRP 위배 코드
하나의 메서드가 주문 항목들의 총 가격을 계산하고 동시에 송장을 생성하려고 시도하고 있다.
class Order:
def calculate_total_and_generate_invoice(self, order_items):
# 주문 항목들의 총 가격 계산 및 송장 생성
pass
- 두 작업을 별도의 메서드로 분리하는 것이 더 나은 방법
class Order:
def calculate_total(self, order_items):
# 주문 항목들의 총 가격 계산
pass
def generate_invoice(self, order_items):
# 주문에 대한 송장 생성
pass
2. 개방-폐쇄 원칙 (Open/Closed Principle, OCP): 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다. 이는 기존의 코드를 변경하지 않고도 새로운 기능을 추가할 수 있도록 해준다.
원래 기능은 그대로 둔 상태로 확장해서 씀
- OCP 위배 코드
코드는 새로운 도형을 추가할 때 기존 코드를 변경해야 한다
class Shape:
def __init__(self, type):
self.type = type
def area(self):
if self.type == "rectangle":
return self.width * self.height
elif self.type == "circle":
return 3.14 * self.radius ** 2
- 도형 클래스를 확장하여 새로운 도형을 추가할 수 있도록 설계해야 한다.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
3. 리스코프 치환 원칙 (Liskov Substitution Principle, LSP): 하위 타입은 상위 타입으로 교체 가능해야 한다. 이는 파생 클래스가 상위 클래스의 기능을 보장해야 함을 의미한다. 즉, 클라이언트 코드에서는 기본 클래스나 인터페이스를 사용하여 파생 클래스를 사용할 수 있어야 한다.
자식클래스는 언제나 부모클래스로 대체할 수 있어야 한다.
- Ostrich 클래스가 Bird 클래스의 메서드를 오버라이드하여 부모 클래스의 동작을 변경하고 있다.
class Bird:
def fly(self):
print("Bird is flying")
class Ostrich(Bird):
def fly(self):
raise Exception("Ostrich cannot fly")
- 파생 클래스는 부모 클래스의 동작을 변경하지 않고 확장해야 한다.
class Bird:
def fly(self):
pass
class Sparrow(Bird):
def fly(self):
print("Sparrow is flying")
class Ostrich(Bird):
def fly(self):
raise Exception("Ostrich cannot fly")
4. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP): 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다. 즉, 인터페이스는 클라이언트가 필요로 하는 작은 단위로 분리되어야 한다.
필요한 인터페이스만 만들어 써야함
class Worker:
def work(self):
pass
class Manager:
def manage(self):
pass
class WorkerManager(Worker, Manager):
def work(self):
print("Worker is working")
def manage(self):
print("Manager is managing")
하나의 WorkerManager 클래스가 두 가지 작업을 수행하고 있다.
-각 인터페이스에 해당하는 클래스를 만들어야 한다.
class WorkerManager:
def work(self):
print("Worker is working")
def manage(self):
print("Manager is managing")
5. 의존성 역전 원칙 (Dependency Inversion Principle, DIP): 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 모두 추상화에 의존해야 한다. 이는 상위 수준의 모듈이 하위 수준의 모듈에 의존하지 않고, 추상화된 것에 의존함으로써 시스템의 유연성과 확장성을 높인다.
자주 바뀌지 않는 인터페이스에 의존해야함
class LightSwitch:
def __init__(self):
self.bulb = LightBulb()
def toggle(self):
self.bulb.turn_on()
스위치 클래스가 전구 클래스의 구체적인 구현에 직접 의존하고 있다
- 스위치 클래스는 전구 클래스의 추상화된 인터페이스에만 의존해야 한다.
class LightBulb:
def turn_on(self):
print("Light bulb is on")
class Switch:
def __init__(self, device):
self.device = device
def control(self):
self.device.turn_on()
# High-level 모듈
class LightSwitch:
def __init__(self):
self.switch = Switch(LightBulb())
def toggle(self):
self.switch.control()
'정보처리,전산 > 소프트웨어공학' 카테고리의 다른 글
결합도와 응집도 (0) | 2025.01.22 |
---|---|
소프트웨어 생명 주기 (Software Life Cycle) (1) | 2024.06.03 |