python策略模式包含角色_詳解Python設計模式之策略模式

雖然設計模式與語言無關,但這并不意味著每一個模式都能在每一門語言中使用。《設計模式:可復用面向對象軟件的基礎》一書中有 23 個模式,其中有 16 個在動態語言中“不見了,或者簡化了”。

1、策略模式概述

策略模式:定義一系列算法,把它們一一封裝起來,并且使它們之間可以相互替換。此模式讓算法的變化不會影響到使用算法的客戶。

電商領域有個使用“策略”模式的經典案例,即根據客戶的屬性或訂單中的商品計算折扣。

假如一個網店制定了下述折扣規則。

有 1000 或以上積分的顧客,每個訂單享 5% 折扣。

同一訂單中,單個商品的數量達到 20 個或以上,享 10% 折扣。

訂單中的不同商品達到 10 個或以上,享 7% 折扣。

簡單起見,我們假定一個訂單一次只能享用一個折扣。

UML類圖如下:

Promotion 抽象類提供了不同算法的公共接口,fidelityPromo、BulkPromo 和 LargeOrderPromo 三個子類實現具體的“策略”,具體策略由上下文類的客戶選擇。

在這個示例中,實例化訂單(Order 類)之前,系統會以某種方式選擇一種促銷折扣策略,然后把它傳給 Order 構造方法。具體怎么選擇策略,不在這個模式的職責范圍內。(選擇策略可以使用工廠模式。)

2、傳統方法實現策略模式:

from abc import ABC, abstractmethod

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.discount(self)

return self.total() - discount

def __repr__(self):

fmt = ''

return fmt.format(self.total(), self.due())

class Promotion(ABC): # 策略:抽象基類

@abstractmethod

def discount(self, order):

"""返回折扣金額(正值)"""

class FidelityPromo(Promotion): # 第一個具體策略

"""為積分為1000或以上的顧客提供5%折扣"""

def discount(self, order):

return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0

class BulkItemPromo(Promotion): # 第二個具體策略

"""單個商品為20個或以上時提供10%折扣"""

def discount(self, order):

discount = 0

for item in order.cart:

if item.quantity >= 20:

discount += item.total() * 0.1

return discount

class LargeOrderPromo(Promotion): # 第三個具體策略

"""訂單中的不同商品達到10個或以上時提供7%折扣"""

def discount(self, order):

distinct_items = {item.product for item in order.cart}

if len(distinct_items) >= 10:

return order.total() * 0.07

return 0

joe = Customer('John Doe', 0)

ann = Customer('Ann Smith', 1100)

cart = [LineItem('banana', 4, 0.5),

LineItem('apple', 10, 1.5),

LineItem('watermellon', 5, 5.0)]

print('策略一:為積分為1000或以上的顧客提供5%折扣')

print(Order(joe, cart, FidelityPromo()))

print(Order(ann, cart, FidelityPromo()))

banana_cart = [LineItem('banana', 30, 0.5),

LineItem('apple', 10, 1.5)]

print('策略二:單個商品為20個或以上時提供10%折扣')

print(Order(joe, banana_cart, BulkItemPromo()))

long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]

print('策略三:訂單中的不同商品達到10個或以上時提供7%折扣')

print(Order(joe, long_order, LargeOrderPromo()))

print(Order(joe, cart, LargeOrderPromo()))

輸出:

策略一:為積分為1000或以上的顧客提供5%折扣

策略二:單個商品為20個或以上時提供10%折扣

策略三:訂單中的不同商品達到10個或以上時提供7%折扣

3、使用函數實現策略模式

在傳統策略模式中,每個具體策略都是一個類,而且都只定義了一個方法,除此之外沒有其他任何實例屬性。它們看起來像是普通的函數一樣。的確如此,在 Python 中,我們可以把具體策略換成了簡單的函數,并且去掉策略的抽象類。

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 = ''

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

joe = Customer('John Doe', 0)

ann = Customer('Ann Smith', 1100)

cart = [LineItem('banana', 4, 0.5),

LineItem('apple', 10, 1.5),

LineItem('watermellon', 5, 5.0)]

print('策略一:為積分為1000或以上的顧客提供5%折扣')

print(Order(joe, cart, fidelity_promo))

print(Order(ann, cart, fidelity_promo))

banana_cart = [LineItem('banana', 30, 0.5),

LineItem('apple', 10, 1.5)]

print('策略二:單個商品為20個或以上時提供10%折扣')

print(Order(joe, banana_cart, bulk_item_promo))

long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]

print('策略三:訂單中的不同商品達到10個或以上時提供7%折扣')

print(Order(joe, long_order, large_order_promo))

print(Order(joe, cart, large_order_promo))

其實只要是支持高階函數的語言,就可以如此實現,例如 C# 中,可以用委托實現。只是如此實現反而使代碼變得復雜不易懂。而 Python 中,函數天然就可以當做參數來傳遞。

值得注意的是,《設計模式:可復用面向對象軟件的基礎》一書的作者指出:“策略對象通常是很好的享元。” 享元是可共享的對象,可以同時在多個上下文中使用。共享是推薦的做法,這樣不必在每個新的上下文(這里是 Order 實例)中使用相同的策略時不斷新建具體策略對象,從而減少消耗。因此,為了避免 [策略模式] 的運行時消耗,可以配合 [享元模式] 一起使用,但這樣,代碼行數和維護成本會不斷攀升。

在復雜的情況下,需要具體策略維護內部狀態時,可能需要把“策略”和“享元”模式結合起來。但是,具體策略一般沒有內部狀態,只是處理上下文中的數據。此時,一定要使用普通的函數,別去編寫只有一個方法的類,再去實現另一個類聲明的單函數接口。函數比用戶定義的類的實例輕量,而且無需使用“享元”模式,因為各個策略函數在 Python 編譯模塊時只會創建一次。普通的函數也是“可共享的對象,可以同時在多個上下文中使用”。

以上就是詳解Python設計模式之策略模式的詳細內容,更多關于Python 策略模式的資料請關注我們其它相關文章!

本文標題: 詳解Python設計模式之策略模式

本文地址: http://www.cppcns.com/jiaoben/python/319771.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457428.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457428.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457428.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql 日期

數據類型 數據類型格式date YYYY-MM-DD datetime YYYY-MM-DD HH:MM:SS timestamp YYYY-MM-DD HH:MM:SS year YYYY 或 YY 具體實現的函數 1、now() 返回當前的日期和時間 SELECT NOW(); 2、curdate() 返回當前的日期 SELECT CURdate(); 3、curtime()返回當…

【Go】panic: reflect: call of reflect.Value.FieldByName on ptr Value

產生原因 調用 FieldByName()方法時,調用者與預期類型不相符。 // 錯誤代碼 func setNewArticleInfoToCache(article *Article) {fields : []string{"Title", "Abstract", "ID", "AuthorID", "CreateTime",}im…

超完整的 Chrome 瀏覽器客戶端調試大全

2019獨角獸企業重金招聘Python工程師標準>>> 引言 “工欲善其事,必先利其器” 沒錯,這句話個人覺得說的特別有道理,舉個例子來說吧,厲害的化妝師都有一套非常專業的刷子,散粉刷負責定妝,眼影刷負…

PHP 獲取服務器詳細信息【轉】

碰到此問題,做下記錄 獲取系統類型及版本號: php_uname() (例:Windows NT COMPUTER 5.1 build 2600)只獲取系統類型: php_uname(s) (或&#xff1…

HIVE攻略 JFK_Hive安裝及使用攻略

目錄Hive的安裝Hive的基本使用:CRUDHive交互式模式數據導入數據導出Hive查詢HiveQLHive視圖Hive分區表1. Hive的安裝系統環境裝好hadoop的環境后,我們可以把Hive裝在namenode機器上(c1)。hadoop的環境,請參考:讓Hadoop跑在云端系列文章&#…

MySQL 為什么用索引,為什么是 B+樹,怎么用索引

MySQL 索引 A database index is a data structure that improves the speed of operations in a table. Indexes can be created using one or more columns, providing the basis for both rapid random lookups and efficient ordering of access to records. 為什么需要索…

頁面加載完畢執行多個JS函數

通常我們需要在打開頁面時加載腳本,這些腳本必須在頁面加載完畢后才可以執行,因為這時候DOM才完整,可以利用window.onload確保這一點,如:window.οnlοadfirstFunction;這腳本的意思是在頁面完畢后執行firstFunction函…

Servlet 生命周期、工作原理

Servlet 生命周期:Servlet 加載--->實例化--->服務--->銷毀。init():在Servlet的生命周期中,僅執行一次init()方法。它是在服務器裝入Servlet時執行的,負責初始化Servlet對象。可以配置服務器&…

【Go 并發控制】上下文 context 源碼

Context 在 Go 服務中,往往由一個獨立的 goroutine 去處理一次請求,但在這個 goroutine 中,可能會開啟別的 goroutine 去執行一些具體的事務,如數據庫,RPC 等,同時,這一組 goroutine 可能還需要…

js設置全局變量ajax中賦值

js設置全局變量,在ajax中給予賦值賦值不上問題解決方案 方案一、 //在全局或某個需要的函數內設置Ajax異步為false,也就是同步. $.ajaxSetup({async : false}); //然后再進行你的Ajax操作 $.post(地址, 參數, function(data, status) { if (status &q…

iOS開發UI篇—模仿ipad版QQ空間登錄界面

一、實現和步驟 1.一般ipad項目在命名的時候可以加一個HD,標明為高清版 2.設置項目的文件結構,分為home和login兩個部分 3.登陸界面的設置 (1)設置第一個控制器和自定義的控制器類(登陸)關聯 (2&#xff09…

click傳值vue_對vue下點擊事件傳參和不傳參的區別詳解

如下所示:{{btn_text1}}{{btn_text2}}var _vm new Vue({data : {btn_text1 : 點擊1 ,btn_text2 : 點擊2},methods : {test_click1 : function (e) {console.log(test_click1--------------------------) ;console.log(e) ;// 輸出結果:MouseEvent {isTr…

【Golang 源碼】sync.Map 源碼詳解

sync.Map 不安全的 map go 中原生的 map 不是并發安全的&#xff0c;多個 goroutine 并發地去操作一個 map 會拋出一個 panic package main import "fmt" func main() {m : map[string]int {"1": 1, "2": 2,}// 并發寫for i : 0; i < 100;…

oracle中scn(系統改變號)

系統scn&#xff1a; select checkpoint_change# from v$database; 文件scn&#xff1a; select name,checkpoint_change# from v$datafile; 結束scn&#xff1a; select name,last_change# from v$datafile; 數據文件頭部scn…

sicktim571操作手冊_SICK激光傳感器TIM310操作說明書

SICK激光傳感器TIM310操作說明書最近更新時間&#xff1a;2015/1/23 13:31:29提 供 商&#xff1a;資料大小&#xff1a;1.2MB文件類型&#xff1a;PDF 文件下載次數&#xff1a;709次資料類型&#xff1a;瀏覽次數&#xff1a;5192次相關產品&#xff1a;詳細介紹&#xff1a;…

Tengine 安裝配置全過程

在先前的文章中介紹過Tengine&#xff0c;先前只是使用了運維人員配置好的內容&#xff0c;未自己進行過安裝配置。周末閑來無事&#xff0c;對于Tengine進行了嘗試性的安裝。記錄下面方便以后再做改進。Tengine官網上有個非常簡單的教程&#xff0c;中間并未涉及到一些常用的設…

【Go】sync.WaitGroup 源碼分析

WaitGroup sync.WaitGroup 用于等待一組 goroutine 返回&#xff0c;如&#xff1a; var wg sync.WaitGroup{}func do() {time.Sleep(time.Second)fmt.Println("done")wg.Done() }func main() {go do()go do()wg.Add(2)wg.Wait()fmt.Println("main done"…

什么是響應式設計?為什么要做響應式設計?響應式設計的基本原理是什么?...

頁面的設計和開發應當根據用戶行為以及設備環境&#xff08;系統平臺、屏幕尺寸、屏幕定向等&#xff09;進行相應的響應和調整。具體的實踐方式由多方面組成&#xff0c;包括彈性網格和布局、圖片、css media query的使用等。無論用戶正在使用筆記本還是iPad&#xff0c;我們的…

三個數相減的平方公式_快收好這份小學數學公式大全!孩子遇到數學難題時肯定用得上...

必背定義、定理公式1.三角形的面積&#xff1d;底高2 公式 S&#xff1d; ah22.正方形的面積&#xff1d;邊長邊長公式 S&#xff1d; aa3.長方形的面積&#xff1d;長寬公式 S&#xff1d; ab4.平行四邊形的面積&#xff1d;底高公式 S&#xff1d; ah5.梯形的面積&#xff1d…

Eclipse 控制console

http://blog.csdn.net/leidengyan/article/details/5686691