개발을 하다보면 의존성 주입이 필요한 경우가 생긴다. 예를 들어 아래와 같은 코드에서 우리는 어떻게 의존성 주입을 적용해야 할까?
class PngConversion:
def converse(data: Data):
# png conversion algorithm
return data
class JPEGConversion:
def converse(data: Data):
# jpeg conversion algorithm
return data
class Transformer:
def __init__(self, data: Data):
self.converser = PngConversion()
self.data = data
def converse(self):
self.converser.converse(self.data)
코드를 이런식으로 구성하면 나중에 다른 Conversion 객체의 메서드를 사용하고 싶을 경우에 Transformer 클래스안에 있는 코드를 수정해야 하는 불편함이 생긴다.
현재 코드에서는 상위 클래스(Transformer) 와 하위 클래스(PngConversion) 사이의 강한 결합이 생긴다. 상위 클래스의 동작이 하위 클래스의 구현에 영향을 받는 것이다.
의존 관계 역전 원칙 (DIP) 를 지키기 위해 상위 클래스를 추상 클래스에 의존하게 만들어야 한다.
의존 관계 역전 원칙(Dependency inversion Principle)은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭한다. 이 원칙을 따르면, 상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다
첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
둘째, 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
-위키백과-
from abc import ABCMeta, abstractmethod
class Conversion(metaclass=ABCMeta):
@abstractmethod
def converse(data: Data):
pass
class PngConversion(Conversion):
def converse(data: Data):
# png conversion algorithm
return data
class JPEGConversion(Conversion):
def converse(data: Data):
# jpeg conversion algorithm
return data
class Transformer:
def __init__(self, data: Data, converser: Conversion):
self.converser = converser
self.data = data
def converse(self):
self.converser.converse(self.data)
Transformer(data, PngConversion()).converse()
Transformer(data, JPEGConversion()).converse()
위의 코드처럼 Transformer의 생성자 인자로 추상클래스를 상속받은 클래스를 넘겨줌으로써 의존성 주입(DI)
을 구현할 수 있다.
인자를 넘겨주는 방식말고도 파이썬 데코레이터를 활용하여 구현 할 수 있는 방법도 있다.
import inject
from abc import ABCMeta, abstractmethod
class Conversion(metaclass=ABCMeta):
@abstractmethod
def converse(data: Data):
pass
class PngConversion(Conversion):
def converse(data: Data):
# png conversion algorithm
return data
class JPEGConversion(Conversion):
def converse(data: Data):
# jpeg conversion algorithm
return data
class Transformer:
@inject.autoparams()
def __init__(self, data: Data, converser: Conversion):
self.converser = converser
self.data = data
def converse(self):
self.converser.converse(self.data)
transformer = Transformer(data)
transformer.converse()
@inject.autoparams() 을 활용하면 거기에 png, jpg 어떤 컨버터가 들어갈지 어떻게 정해지는지 알수있을까요..??