Dependency Inversion
객체지향 설계 5원칙 중 하나인 SOLID 원칙의 D가 Dependency Inversion을 일컫는다.
Dependency Inversion을 포함해 모든 디자인 패턴의 핵심은 abstraction(추상화)이다.
이상적으로는 프로그래밍 언어가 추상화 메커니즘과 type을 모두 제공해야 한다.
파이썬에는 이 두 가지 모두 없다..!
그러나 여전히 Dependency Inversion을 구현할 수 있다.
Abstract base class(ABC)라는 모듈을 사용할 수 있고, type hint도 제공한다.
(⚠️ type hint는 언어 레벨에서 실질적으로 어떠한 제약 사항도 강요하지 않는다. 즉, type hint에 맞게 코드를 쓰지 않아도 에러를 발생시키지 않는다.)
Example
# BEFORE
class LightBulb:
def turn_on(self):
print("LightBulb: turned on...")
def turn_off(self):
print("LightBulb: turned off...")
class ElectricPowerSwitch:
def __init__(self, l: LightBulb):
self.lightBulb = l
self.on = False
def press(self):
if self.on:
self.lightBulb.turn_off()
self.on = False
else:
self.lightBulb.turn_on()
self.on = True
l = LightBulb()
switch = ElectricPowerSwitch(l)
switch.press()
switch.press()
첫째로 ABC를 상속받는 Switchable 클래스를 만든다.
abstract class의 인스턴스는 만들 수 없다.
from abc import ABC, abstractmethod
class Switchable(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
s = Switchable() # 에러
LightBulb는 Switchable이라는 interface*를 implement*하는 클래스로 수정해준다.
* interface, implement : 인터페이스는 자신과 이를 구현하는 모든 클래스 간의 계약(contract)이다. 인터페이스에는 메서드, 프로퍼티 또는 이벤트가 있을 수 있다. 멤버 선언(declaration)만 포함하고 멤버 구현(implementation)은 인터페이스를 구현하는 클래스에서 제공한다.
class LightBulb(Switchable):
def turn_on(self):
print("LightBulb: turned on...")
def turn_off(self):
print("LightBulb: turned off...")
다음으로 ElectricPowerSwitch 클래스에서 전달받는 LightBulb의 타입을 Switchable로 변경해준다. (lightbulb → client)
class ElectricPowerSwitch:
def __init__(self, c: Switchable):
self.client = c
self.on = False
def press(self):
if self.on:
self.client.turn_off()
self.on = False
else:
self.client.turn_on()
self.on = True
이렇게 함으로써 LightBulb와 ElectricPowerSwitch 사이의 의존을 없앨 수 있다.
→ LightBulb 말고도 다른 Switchable을 구현한 클래스를 ElectricPowerSwitch에 연결할 수 있게 된다. (ex. Fan)
class Fan(Switchable):
def turn_on(self):
print("Fan: turned on...")
def turn_off(self):
print("Fan: turned off...")
수정 후 전체 코드는 다음과 같다.
# AFTER
from abc import ABC, abstractmethod
class Switchable(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
class LightBulb(Switchable):
def turn_on(self):
print("LightBulb: turned on...")
def turn_off(self):
print("LightBulb: turned off...")
class Fan(Switchable):
def turn_on(self):
print("Fan: turned on...")
def turn_off(self):
print("Fan: turned off...")
class ElectricPowerSwitch:
def __init__(self, c: Switchable):
self.client = c
self.on = False
def press(self):
if self.on:
self.client.turn_off()
self.on = False
else:
self.client.turn_on()
self.on = True
l = LightBulb()
f = Fan()
switch = ElectricPowerSwitch(f)
switch.press()
switch.press()
Summary
직접적으로 coupled된 두 클래스를 Dependency Inversion을 통해 (예시의 경우 interface를 통해) decouple 할 수 있다.
loose coupling을 지향해야 하는 이유는 포스팅으로 작성한 바 있다.
🌟 내용에 오류가 있다면 댓글 달아주시면 감사하겠습니다.
References
https://www.youtube.com/watch?v=Kv5jhbSkqLE&list=PLC0nd42SBTaNuP4iB4L6SJlMaHE71FG6N&index=2
https://medium.com/@ubale.vikas9/interface-in-oops-6eae3731c242
'프로그래밍 > CS' 카테고리의 다른 글
[디자인패턴] Observer Pattern (옵저버 패턴) (0) | 2022.08.05 |
---|---|
[디자인패턴] Strategy Pattern (전략 패턴) (0) | 2022.07.29 |
[디자인패턴] Cohesion (응집도), Coupling (결합도) (0) | 2022.07.26 |
[네트워크] OSI 7계층 모델 (0) | 2022.07.25 |
[네트워크] 프로토콜, TCP, UDP (0) | 2022.07.12 |