설계패턴 4. SOLID 원칙 - Open-Closed, Dependency Inversion Principle

LSDrug·2024년 6월 8일

설계패턴(完)

목록 보기
4/26

지난번까지 SOLID principle의 SRP까지 알아보았다.
이번에는 Open-Closed, Dependency Inversion Principle에 관해 알아볼 차례이다.


Open-Closed

개방-폐쇄 원칙(Open-Closed Principle)소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다는 프로그래밍 원칙이다.

쉽게 말하자면 다음을 충족한다고 볼 수 있다.

  • 확장(추가)을 할 때는 쉽게 넣을 수 있어야 한다.
  • 확장을 할 때, 수정을 할 필요가 없어야 한다.

즉, 쉽게 추가할 수 있도록 하되, 수정하지 않고 추가만 할 수 있도록 하는 원칙이라고 할 수 있다.

예시를 통해 알아보자.

# 기능이 추가될 때마다 수정이 필요한 코드
class Animal: # 동물에 관한 클래스
    def __init__(self, a_type): # 생성자
      self.a_type = a_type

def hey(animal:Animal): # 동물의 울음소리 -> 클래스 변수를 지정.
  if animal.a_type == 'Cat':
    print('meow')
  elif animal.a_type == 'Dog':
    print('bark')
  else: # 에러가 날 경우
    raise Error('wrong a_type')

kitty = Animal('Cat')
bingo = Animal('Dog')
cow = Animal('Cow')
sheep = Animal('Sheep')

hey(cow) # 에러
hey(sheep) # 에러 -> 출력이 되기 위해서는 hey 메서드를 수정해야 한다.

해당 코드는 hey 메서드를 계속해서 추가해 주어야 한다.

# 기능이 추가되어도 수정이 필요하지 않은 코드
class Animal:
  def speak(self):
    pass

class Cat(Animal):
  def speak(self):
    print('meow')

class Dog(Animal):
  def speak(self):
    print('bark')

class Sheep(Animal):
  def speak(self):
    print('meh')

class Cow(Animal):
  def speak(self):
    print('moo')

def hey(animal:Animal):
  animal.speak()

cow = Cow()
sheep = Sheep()

hey(cow)
hey(sheep)

그러나 해당 코드는 각자의 울음소리를 각각의 클래스로 만들어서 추가할 경우, 수정 필요없이 클래스를 추가하면 될 수 있도록 만들었다.


Dependency Inversion Principle

의존관계 역전 원칙은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭한다.

이 원칙을 따르면, 상위 계층이 하위 계층에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다.

이에 대한 코드 예시를 통해 알아보자.

# 기본 코드
class Cat:
  def speak(self):
    print('meow')

class Dog:
  def speak(self):
    print('bark')

class Zoo:
  def __init__(self):
    self.cat = Cat()
    self.dog = Dog()

# 추가된 코드
class Sheep:
  def speak(self):
    pass

class Cow:
  def speak(self):
    pass

class Zoo:
  def __init__(self):
    self.cat = Cat()
    self.dog = Dog()
    self.sheep = Sheep()
    self.cow = Cow()

# 의존관계가 있도록 지정한다.
# 해당 관계를 도식화하면 다음과 같다.
'''
  -------> Sheep
 l
Zoo -----> Cat
 l
  -------> Dog
'''

여기서 Zoo 클래스는 Cat, Dog 클래스들에 의존관계가 있다.

동물들이 늘어나면 Zoo 클래스를 계속 수정해야 한다.

class Animal:
  def speak(self):
    pass

class Cat(Animal):
  def speak(self):
    print('meow')

class Dog(Animal):
  def speak(self):
    print('bark')

class Zoo:
  def __init__(self):
    self.animals = []

  def addAnimal(self, animal:Animal):
    self.animals.append(animal)

  def speakAll(self):
    for animal in self.animals:
      animal.speak()

'''
Cat
    -------> Animal <---------- Zoo
Dog
'''

그러나 해당 코드는 Animal 추상클래스에 의해 역전되었고, Zoo 클래스는 각 Cat, Dog 등의 클래스로부터 독립되었다.

이는, 추가를 할 때마다 수정을 할 필요가 없어졌다는 것을 의미한다.

예시 하나를 더 들어보자.

# DIP 따르지 않음 -> 수정이 필요

# 저수준 모듈: 전등 클래스
class Light:
  def turn_on(self):
    print("Light turned on")

  def turn_off(self):
    print("Light turned off")

# 저수준 모듈: 팬 클래스
class Fan:
  def turn_on(self):
    print("Fan turned on")

  def turn_off(self):
    print("Fan turned off")

# 고수준 모듈: 스위치 클래스
class Switch:
  def __init__(self): 
    self.light = Lihgt()
    self.fan = Fan()

    def turn_light_on(self):
      self.light.turn_on()

    def turn_light_off(self):
      self.light.turn_off()

    def turn_fan_on(self):
      self.fan.turn_on()

    def turn_fan_off(self):
      self.fan.turn_off()

  # 예시의 사용
  switch = Switch()
  switch.turn_light_on()
  switch.turn_light_off()
  switch.turn_fan_on()
  switch.turn_fan_off()
# DIP를 따름
from abc import ABC, abstractmethod # 추상 클래스와 추상 메서드를 사용하기 위한 모듈

# 추상 인터페이스 정의
class Switchable(ABC):
  @abstractmethod
  def turn_on(self):
    pass

  @abstractmethod
  def turn_off(self):
    pass

# 저수준 모듈: 전등 클래스
class Light(Switchable):
  def turn_on(self):
    print("Light turned on")

  def turn_off(self):
    print("Light turned off")

# 저수준 모듈: 팬 클래스
class Fan(Switchable):
  def turn_on(self):
    print("Fan turned on")

  def turn_off(self):
    print("Fan turned off")

class Switch:
  def __init__(self, device):
    self.device = device

  def turn_on(self):
    self.device.turn_on()

  def turn_off(self):
    self.device.turn_off()

# 예시의 사용
light = Light()
fan = Fan()

switch1 = Switch(light)
switch1.turn_on()
switch1.turn_off()

switch2 = Switch(fan)
switch2.turn_on()
switch2.turn_off()

바로 위의 코드는 추상 클래스와 추상 메서드를 이용해서 의존관계를 역전시킨 것을 볼 수 있다.


profile
마약같은 코딩, 마약같은 코딩러

0개의 댓글