Python/Design Patterns

템플릿 메소드(Template Method)

코닝구 2020. 9. 30. 18:02

디자인 패턴 중에서 템플릿 메소드(Template Method) 패턴에 대해 알아보자. 템플릿 메소드 패턴은 GoF가 제시한 행동 패턴(Behavioral Patterns) 중의 하나로 슈퍼 클래스에서 알고리즘에 대한 메소드의 뼈대(skeleton)만 작성하고, 서브 클래스에서 해당 메소드를 상속(inheritance)받아 구체적인 구현을 재 정의(overriding) 하는 패턴이다.

https://refactoring.guru

템플릿 메소드 패턴은 소프트웨어 개발 원칙들 중 DRY(Don't repeat yourself)에 해당한다. DRY는 어떠한 경우든 모든 형태의 데이터 중복을 지양하는 원리이다. 즉, 같은 코드를 중복되어 작성하지 말란 원칙이다. 템플릿 메소드 패턴은 동일한 알고리즘을 슈퍼 클래스에서 작성하기에 코드 중복을 최소화 한다. 또한, 알고리즘을 바닥부터 다시 작성하지 않고, 서브 클래스를 새로 만들고 필요한 메소드만 재 정의하면 됨으로 코드 추가 및 수정이 수월하다. 

템플릿 메소드 패턴은 매우 많이 쓰이는 패턴이라서 많은 프로젝트에서 발견할 수 있다. 자주 쓰이는 이유는 프레임워크를 만드는 데 아주 적합한 디자인 패턴이기 때문이다. Python 에서는 장고(django)의 CBV(Class-based views)가 대표적이다. 


후크(Hook)

후크는 마찬가지로 슈퍼클래스에서 선언되는 메소드이고, 서브 클래스에서 재 정의하거나 하지 않아도 된다. 후크를 사용하면 알고리즘의 특정 포인트에서 서브 클래스가 관여할 수 있게 되고, Python에서는 대부분 pass로 후크를 선언한다.

class A:
    def hook(self) -> None:
        pass

구현

다양한 사이트의 최신 컨텐츠를 수집한다고 가정하자. 이를 위한 알고리즘은 대부분 비슷하겠지만, URL 정보와 파싱을 위한 별도의 로직은 사이트마다 다를 수 있다. 

Template Method UML class diagram

AbstractParser: 추상 클래스(abstract class)로 ConingguParser 클래스와 DolgonetParser 클래스의 슈퍼 클래스이다. 슈퍼 클래스에서는 메소드와 이러한 메소드들을 특정 순서대로 호출하는 템플릿 메소드(execute())가 있다.

ConingguParser, DolgonetParser: AbstractParser의 서브 클래스로 대부분의 메소드들을 재 정의할 수 있지만 템플릿 메소드(execute()) 를 재 정의할 순 없다.

from abc import abstractmethod, ABC


class AbstractParser(ABC):
    url = None

    def get_url(self):
        if self.url is None:
            raise ValueError
        return self.url

    def get_content(self):
        _url = self.get_url()
        print('Send a request to _url to import content.')

    @abstractmethod
    def parsing(self, contents):
        pass

    def hook1(self, data):
        pass

    def execute(self):
        contents = self.get_content()
        self.hook1(self.parsing(contents))


class ConingguParser(AbstractParser):
    url = 'https://www.coningg.com/rss'

    def parsing(self, contents):
        print('parsing!')


class DolgonetParser(AbstractParser):
    url = 'http://dolgo.net/rss'

    def parsing(self, contents):
        print('parsing!')

    def hook1(self, data):
        pass


if __name__ == '__main__':
    coninggu = ConingguParser().execute()
    dolgonet = DolgonetParser().execute()

url: 클래스 속성(class attribute)도 재 정의 할 수 있다.

parsing(): 같은 서브 클래스에서 필수로 구현할 메소드들에 대해선 @abstractmethod를 선언하여 강제하도록 하였다. 

hook1(): 위에서 언급한 후크에 대한 메소드이고 강제하지 않기에 pass하였다. 그리고 hook1()를 재 정의한 DolgonetParser는 parsing() 이후 동작에 관여할 수 있게 된다.

execute(): 템플릿 메소드이며 메소드들의 순서를 정의한다.

'Python > Design Patterns' 카테고리의 다른 글

싱글턴(Singleton)  (0) 2020.09.29
퍼사드(Facade)  (0) 2020.09.27
보그(Borg)  (0) 2020.09.27
데코레이터(Decorator)  (0) 2020.09.22