객체지향 SOLID

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()




반응형