프로그래밍/CS

[디자인패턴] Template Method Pattern (템플릿 메소드 패턴)

choar 2022. 8. 8. 10:33
반응형

Template Method Pattern

템플릿 메소드 패턴을 사용하면 프로세스를 표준화할 수 있다.

이 패턴은 프로세스는 고정되어 있는데 프로세스 내의 단계는 다를 때 적용하기 좋다.

예를 들어 온라인 쇼핑몰을 생각해보면, 사용자가 물건을 구매하는 큰 프로세스는 표준화되어있다. (항상 동일하다.)

그러나 상품의 종류에 따라 다른 물류 시스템을 통해 보내지는 등, 상황에 따라 프로세스 내의 단계는 달라질 수 있다.

템플릿 메소드 패턴을 사용하면 중간 단계를 너무 많은 boilerplate code*를 더하지 않고도 바꿀 수 있다.

* boilerplate code : 거의 또는 전혀 변형 없이 여러 위치에서 반복되는 코드

 

Example

트레이딩 봇 코드를 예시로 살펴보자.

트레이딩 봇에서는 트레이딩 전략을 어떻게 설정하는지에 따라 매도/매수 알고리즘을 달리 한다.

 

# BEFORE

from typing import List

class Application:

    def __init__(self, trading_strategy = "average"):
        self.trading_strategy = trading_strategy

    def connect(self):
        print(f"Connecting to Crypto exchange...")

    def get_market_data(self, coin: str) -> List[float]:
        return [10, 12, 18, 14]

    def list_average(self, l: List[float]) -> float:
        return sum(l) / len(l)

    def should_buy(self, prices: List[float]) -> bool:
        if self.trading_strategy == "minmax":
            return prices[-1] == min(prices)
        else:
            return prices[-1] < self.list_average(prices)

    def should_sell(self, prices: List[float]) -> bool:
        if self.trading_strategy == "minmax":
            return prices[-1] == max(prices)
        else:
            return prices[-1] > self.list_average(prices)

    def check_prices(self, coin: str):
        self.connect()
        prices = self.get_market_data(coin)
        should_buy = self.should_buy(prices)
        should_sell = self.should_sell(prices)
        if should_buy:
            print(f"You should buy {coin}!")
        elif should_sell:
            print(f"You should sell {coin}!")
        else:
            print(f"No action needed for {coin}.")

application = Application("average")
application.check_prices("BTC/USD")

should_buy, should_sell 메소드를 살펴보자.

트레이딩 전략이 minmax인 경우, 현재 코인의 가격이 지금까지의 가격에서 최솟값이어야 매수하고, 최댓값이어야 매도한다.

트레이딩 전략이 average인 경우, 현재 코인의 가격이 지금까지의 가격의 평균보다 작으면 매수하고, 크면 매도한다.

현재는 각 메소드에서 if문으로 트레이딩 전략을 확인해 다른 기능을 하도록 구현된 것을 확인할 수 있다.

모든 메소드가 Application 클래스 내에 구현돼 있어 한 클래스가 너무 많은 responsibility를 갖고 있다. 즉, cohesion이 낮다.

 

# AFTER

from abc import ABC, abstractmethod
from typing import List

class TradingBot(ABC):
    
    def connect(self):
        print(f"Connecting to Crypto exchange...")

    def get_market_data(self, coin: str) -> List[float]:
        return [10, 12, 18, 14]

    def check_prices(self, coin: str):
        self.connect()
        prices = self.get_market_data(coin)
        should_buy = self.should_buy(prices)
        should_sell = self.should_sell(prices)
        if should_buy:
            print(f"You should buy {coin}!")
        elif should_sell:
            print(f"You should sell {coin}!")
        else:
            print(f"No action needed for {coin}.")

    @abstractmethod
    def should_buy(self, prices: List[float]) -> bool:
        pass

    @abstractmethod
    def should_sell(self, prices: List[float]) -> bool:
        pass

class AverageTrader(TradingBot):

    def list_average(self, l: List[float]) -> float:
        return sum(l) / len(l)

    def should_buy(self, prices: List[float]) -> bool:
        return prices[-1] < self.list_average(prices)

    def should_sell(self, prices: List[float]) -> bool:
        return prices[-1] > self.list_average(prices)

class MinMaxTrader(TradingBot):

    def should_buy(self, prices: List[float]) -> bool:
        return prices[-1] == min(prices)

    def should_sell(self, prices: List[float]) -> bool:
        return prices[-1] == max(prices)

application = MinMaxTrader()
application.check_prices("BTC/USD")

수정 후 코드는 위와 같다.

일단 TradingBot이라는 abstract base class를 생성해준다.

트레이딩 전략에 따라 달라지는 should_buy, should_sell은 abstract method로 지정해준다.

TradingBot을 상속해 각 트레이딩 전략마다 Trader 클래스를 만들어주고, 거기서 should_buy, should_sell을 구현해준다.

다른 트레이딩 전략을 추가할 때도 원래 코드를 수정할 필요 없이 클래스를 하나 더 만들어주면 된다.

 

🌟 내용에 오류가 있다면 댓글 달아주시면 감사하겠습니다.


References

https://www.youtube.com/watch?v=t0mCrXHsLbI&list=PLC0nd42SBTaNuP4iB4L6SJlMaHE71FG6N&index=5&t=3s

https://github.com/ArjanCodes/betterpython/tree/main/6%20-%20template%20method%20%26%20bridge

https://en.wikipedia.org/wiki/Boilerplate_code

반응형