github地址: https://github.com/cheesezh/python_design_patternspython
設計一個控制檯程序, 模擬商場收銀軟件,根據客戶購買商品的單價和數量,計算總價。git
price = float(input("輸入商品單價:")) number = int(input("輸入商品數量:")) total = (price * number) print("當前總價: %.2f" % total)
輸入商品單價:40 輸入商品數量:9 當前總價: 360.00
上述程序僅僅實現了基本功能,可是當商場有打折活動,例如八折,五折等,就不知足需求了,折扣的方法還可能有滿減活動,例如滿300減100,滿500減200等。假設只有打折和滿減兩種促銷活動,那麼這就很像上一章節的計算器,支持正常收費,打折活動和滿減活動三種計算方法,能夠用簡單工廠方法實現。github
from abc import ABCMeta, abstractmethod class CashBase(): """ 基礎類 """ __metaclass__ = ABCMeta def __init__(self): self.final_price = None @abstractmethod def accept_cash(self): pass class CashNormal(CashBase): """ 正常收費 """ def accept_cash(self, money): self.final_price = money return self.final_price class CashRebate(CashBase): """ 打折活動 """ def __init__(self, rebate): self.rebate = rebate def accept_cash(self, money): self.final_price = money * self.rebate return self.final_price class CashReturn(CashBase): """ 滿減活動 """ def __init__(self, return_condition, return_money): self.return_condition = return_condition self.return_money = return_money def accept_cash(self, money): if money >= self.return_condition: self.final_price = money - self.return_money else: self.final_price = money return self.final_price class CashFactory(): """ 收費方式工廠類 """ # 類的變量,相似靜態變量,經過`類名.變量名`訪問 cash_accepter_map = { "正常收費": CashNormal(), "滿300減100": CashReturn(300, 100), "打8折": CashRebate(0.8) } @staticmethod def createCashAccepter(cash_type): if cash_type in CashFactory.cash_accepter_map: return CashFactory.cash_accepter_map[cash_type] else: return None
price = float(input("輸入商品單價:")) number = int(input("輸入商品數量:")) cash_type_list = ["正常收費", "滿300減100", "打8折"] for i in cash_type_list: print("{}:{}".format(cash_type_list.index(i)+1, i)) cash_type_index = int(input("選擇收費方式(1~3)")) total = price * number cash_accepter = CashFactory.createCashAccepter(cash_type_list[cash_type_index-1]) print("應收: %.2f" % total) total = cash_accepter.accept_cash(total) print("實收: %.2f" % total)
輸入商品單價:10 輸入商品數量:50 1:正常收費 2:滿300減100 3:打8折 選擇收費方式(1~3)3 應收: 500.00 實收: 400.00
該模式定義了算法家族,分別封裝起來,讓它們之間能夠互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。算法
from abc import ABCMeta, abstractmethod class CashBase(): """ 抽象策略:基礎類 """ __metaclass__ = ABCMeta def __init__(self): self.final_price = None @abstractmethod def accept_cash(self): pass class CashNormal(CashBase): """ 具體策略:正常收費 """ def accept_cash(self, money): self.final_price = money return self.final_price class CashRebate(CashBase): """ 具體策略:打折活動 """ def __init__(self, rebate): self.rebate = rebate def accept_cash(self, money): self.final_price = money * self.rebate return self.final_price class CashReturn(CashBase): """ 具體策略:滿減活動 """ def __init__(self, return_condition, return_money): self.return_condition = return_condition self.return_money = return_money def accept_cash(self, money): if money >= self.return_condition: self.final_price = money - self.return_money else: self.final_price = money return self.final_price class CashContext(): """ 策略上下文類(基礎版本),用具體策略類來配置,維護一個具體策略對象的引用 """ def __init__(self, cash_strategy): self.cash_strategy = cash_strategy def get_result(slef, money): return self.cash_strategy.accept_cash(money)
在CashContext類中,咱們須要傳入一個具體策略類來進行配置,在商場收銀軟件這個場景中,那就是不一樣的收費策略,那麼如何生成不一樣的收費策略對象呢?能夠將策略模式和簡單工廠相結合。單元測試
class CashContext(): """ 策略上下文類(改進版本),用具體策略類來配置,維護一個具體策略對象的引用 """ # 類的變量,相似靜態變量,經過`類名.變量名`訪問 cash_accepter_map = { "正常收費": CashNormal(), "滿300減100": CashReturn(300, 100), "打8折": CashRebate(0.8) } def __init__(self, cash_type): self.cash_strategy = CashContext.cash_accepter_map[cash_type] def get_result(self, money): return self.cash_strategy.accept_cash(money)
price = float(input("輸入商品單價:")) number = int(input("輸入商品數量:")) cash_type_list = ["正常收費", "滿300減100", "打8折"] for i in cash_type_list: print("{}:{}".format(cash_type_list.index(i)+1, i)) cash_type_index = int(input("選擇收費方式(1~3)")) total = price * number cash_context = CashContext(cash_type_list[cash_type_index-1]) print("應收: %.2f" % total) total = cash_context.get_result(total) print("實收: %.2f" % total)
輸入商品單價:10 輸入商品數量:10 1:正常收費 2:滿300減100 3:打8折 選擇收費方式(1~3)3 應收: 100.00 實收: 80.00
策略模式+簡單工廠和僅用簡單工廠模式的區別在哪裏呢?測試
簡單工廠 cash_accepter = CashFactory.createCashAccepter(cash_type_list[cash_type_index-1]) ... total = cash_accepter.accept_cash(total) 策略模式+簡單工廠 cash_context = CashContext(cash_type_list[cash_type_index-1]) ... total = cash_context.get_result(total)
CashFactory
和CashBase
CashContext
CashContext
的對象,調用的是CashContext
的get_result
方法,這使得具體的收費策略完全與客戶端分離,甚至連策略的基類CashBase
都不須要客戶端認識。在CashContext中用到了一個dict()型的類的變量cash_accepter_map
保存各類算法策略,若是新增滿200減50
的策略,那麼還要更新cash_accepter_map
,這顯得並不優雅,任何須要的變動都須要成本,可是成本的高低是有差別的
,爲了更加優雅,下降變動成本,可使用反射技術
,這一技術將在抽象工廠模式
中介紹。spa