전략 패턴의 리팩토링 (2)

매일 공부(ML)·2022년 12월 11일
0

Fluent Python

목록 보기
38/130

객체로서의 함수

일급 함수 디자인 패턴

전략 패턴의 리팩토링

함수지향 전략

전략 객체는 상태(객체 속성)를 가지고 있지 않고, 일반 함수로 보인다고 생각하면 제대로 본 것이므로 구체적인 전략을 간단히 함수로 변경했고, Promotion 추상 클래스를 제거했다.

from collections import namedtuple

Customer = namedtuple('Customer', 'name fidelity')

class LineItem:

	def __init__(self, product, quantity, price):
    	self.product = product
        self.quantity = quantity
        self.price = price
        
    def total(self):
    	return self.price * self.quantity
        
class Order:

	def __init__(self, customer, cart, promotion=None):
    	self.customer = customer
        self.cart = list(cart)
        self.promotion = promotion
        
    def total(self):
    	if not hasattr(self, '__total'):
        	self.__total = sum(item.total() for item in self.cart)
        return self.__total
        
    def due(self):
    	if self.promotion is None:
        	discount= 0
        else:
        	discount = self.promotion(self)
        return self.total() - discount
        
    def __repr__(self):
    	fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(), self.due())
        
        
def fidelity_promo(order):
	"""충성도 포인트가 1000점 이상인 고객에게 전체 5% 할인 적용"""
    return order.total()*.05 if order.customer.fidelity >= 1000 else 0
    
def bulk_item_promo(order):
	"""20개 이상의 동일 상품을 구입하면 10% 할인 적용"""
    discount = 0
    for item in order.cart:
    	if item.quantity >= 20:
        	discount += item.total() * .1
        return discount
        
def large_order_promo(order):
	"""10종류 이상의 상품을 구입하면 전체 7% 할인 적용"""
    distinct_items = {item.product for item in order.cart}
    if len(distinct_items) >=10:
    	return order.total() * .07
    return 0
#할인 전략 함수로 정의한 ORder 클래스의 사용 예

joe = Customer('John Doe', 0)
ann = Customer('Ann Smith', 1100)
cart = [LineItem('banana', 4, .5),
		LineItem('apple',10,1.5),
        LineItem('watermellon',5,5.0)]
        
Order(joe, cart, fidelity_promo)
Order(ann,cart, fidelity_promo)
banana_cart = [LineItem('banana', 30, .5),
LineItem('apple',10,1.5)]
Order(joe, banana_cart, bulk_item_promo)
long_order = [LineItem(str(item_code),1,1.0) for item_code in range(10)]
Order(joe, long_order, large_order_promo)

저자는 전략 객체는 종종 훌륭한 flyweight가 된다는 재미있는 이야기를 했는데 이는 여러 콘텍스트에서 동시에 사용할 수 있는 공유 객체로 새로운 콘텍스트에서 동일 전략 객체를 반복해서 적용할 때는 새로 생성하는 비용을 줄이기 위해 플라이웨이트를 공유하는 것이 좋다.


왜냐하면, Order 객체를 만들 때 기존 전략 객체가 있으면 재사용할 수 있고, 전략 패턴의 단점은 런타임 비용이 많다는 것인데 플라이웨이트 패턴을 사용하면 비용을 극복할 수 있지만, 소스 코드 행 수와 유지 보수 비용이 눈덩이처럼 불어난다.


구체적인 전략 객체가 내부 상태를 가지고 있어서 더욱 복잡한 경우에는 모든 전략 패턴과 플라이웨이트 패턴을 혼합해서 사용해야 하고, 구체적인 전략 객체가 나부 상태를 가지지 않고 단지 콘텍스트에서 오는 데이터를 처리하는 경우도 많다.
이럴 때, 도 다른 추상 클래스에서 정의된 메서드를 하나만 구현하는 클래스를 만들기보단 밀반 함수를 만드는 것이 좋다.

그러므로, 함수는 사용자 정의 클래스보다 훨씬 가볍고 파이썬이 모듈을 컴파일할 때 단 한 번만 생성되므로 플라이웨이트가 필요하지 않고, 일반 함수도 "여러 콘텍스트에서 동시에 공유할 수 있는 공유 객체"이다.


다음 블로그에선 코드를 더 많이 리팩토링하고 함수와 모듈을 객체로 활용하는 다양한 방법을 이용해서 최선의 전략 찾기

profile
성장을 도울 아카이빙 블로그

0개의 댓글