python设计模式之享元模式
一、享元模式定義:
享元模式是一種用于解決資源和性能壓力時會使用到的設計模式,它的核心思想是通過引入數據共享來提升性能。
在開發3D游戲時,例如有成千上萬的士兵或者有成千上萬棵樹,如果一個3D地帶的每個對象都單獨創建,不使用數據共享,那么性能是無法接受的。
享元設計模式就是通過為相似對象映入數據共享來最小化內存的使用,提升性能。
既然要創建成千上萬個士兵,那么若他們的數據屬性行為都是一樣的,那豈不是黏一塊去了。這時候就會有:可變數據和不可變數據的概念。
重點在于將不可變(可共享)的屬性與可變的屬性區分開。相同類型的對象共享不可變(可共享)的數據,而每個對象又有其獨立的數據,這部分數據即為:可變的屬性(不可共享數據)。
1.1、實現:
其實享元模式的實現與單例模式的實現方式十分相似,比如:單例模式實現的是一個類對象只允許有一個實例對象,而享元模式則是一個類對象只允許創建不同類型的對象,這樣保證同一類型的對象共享不可變數據。
1.2、優點
相同對象只要保存一份,這降低了系統中對象的數量,從而降低了系統中細粒度對象給內存帶來的壓力。
?
1.3、缺點
1.為了共享對象,需要將不能共享的狀態外部化,會增加程序的復雜性
2.對享元模式的外部狀態會增長運行時間
1.4、享元模式中存在的兩種狀態
1.內部狀態,不會隨著環境的改變而改變,可共享的部分
2.外部狀態,會隨著環境的改變而改變,是不可共享的部分.
享元模式就是要區分這兩種狀態,并將外部狀態外部化。
二、應用實例
1.java中的String,如果有則返回,如果沒有就創建一個字符串保存在字符串緩沖池里面。
2.數據庫的數據池
2.1、應用場景
1.系統中存在大量的相似對象
2.細粒度的對象都具備較接近的外部狀態,而且內部狀態與環境無關,也就是說對象沒有特定身份
3.需要緩沖池的場景
三、享元模式的主要角色
抽象享元角色(Flyweight):是所有的具體享元類的基類,為具體享元規范需要實現的公共接口,非享元的外部狀態以參數的形式通過方法傳入。
具體享元(Concrete Flyweight)角色:實現抽象享元角色中所規定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部狀態,它以參數的形式注入具體享元的相關方法中。
享元工廠(Flyweight Factory)角色:負責創建和管理享元角色。當客戶對象請求一個享元對象時,享元工廠檢査系統中是否存在符合要求的享元對象,如果存在則提供給客戶;如果不存在的話,則創建一個新的享元對象。
3.1、舉例說明什么是具體享元角色和非具體享元角色
舉下圍棋的例子
棋子:是抽象享元角色
白子:具體享元角色
黑子:具體享元角色
圍棋工廠:是享元工廠角色
下棋的位置:非具體享元角色
3.2、代碼舉例:
from enum import EnumTreeType = Enum('TreeType','apple_tree cherry_tree peach_tree')class Tree:pool = dict()def __new__(cls, tree_type, *args,**kwargs):obj = cls.pool.get(tree_type,None)if not obj:obj = super().__new__(cls,*args, **kwargs)cls.pool[tree_type] = objobj.tree_type = tree_typereturn objdef __init(self,size ):self.size = sizedef render(self,age,x,y):print('render a tree of type {} and age {} at ({},{})'.format(self.tree_type,age,x,y))這樣就實現了一個簡單的享元模式:即通過其中的__new__魔法方法來限制類的實例化,只允許實例化不同類型的對象。
通過一個類型池,若需要實例化的類型在該類型池中,則直接返回該類型池中的對象,由于返回的是同一對象,故其共享不可變的屬性(tree_type),而在執行完成__new__()方法之后,變化執行__init__魔法方法,則這時候該對象的屬性便會發生改變,故不共享可變的屬性(size)。
既然我們實現了一個簡單的享元模式,那么怎么去使用它呢?
import random from enum import Enumdef main():rnd = random.Random()age_min, age_max = 1, 30min_piont, max_point = 0, 100tree_counter = 0for _ in range(10):t1 = Tree(TreeType.apple_tree)t1.render(rnd.randint(age_min, age_max),rnd.randint(min_piont, max_point),rnd.randint(min_piont, max_point))tree_counter += 1for _ in range(3):t1 = Tree(TreeType.cherry_tree)t1.render(rnd.randint(age_min, age_max),rnd.randint(min_piont, max_point),rnd.randint(min_piont, max_point))tree_counter += 1for _ in range(5):t1 = Tree(TreeType.peach_tree)t1.render(rnd.randint(age_min, age_max),rnd.randint(min_piont, max_point),rnd.randint(min_piont, max_point))tree_counter += 1print(Tree.pool)if __name__ == '__main__':main()在main()中去創建10棵apple_tree,并且 為每個對象隨機給不同的年齡、位置等,這樣就可以在游戲中的不同的位置中渲染。
輸出結果為:
render a tree of type TreeType.apple_tree and age 17 at (48,57) render a tree of type TreeType.apple_tree and age 30 at (27,9) render a tree of type TreeType.apple_tree and age 4 at (74,92) render a tree of type TreeType.apple_tree and age 16 at (8,95) render a tree of type TreeType.apple_tree and age 26 at (43,95) render a tree of type TreeType.apple_tree and age 1 at (80,20) render a tree of type TreeType.apple_tree and age 26 at (21,88) render a tree of type TreeType.apple_tree and age 22 at (53,57) render a tree of type TreeType.apple_tree and age 17 at (65,47) render a tree of type TreeType.apple_tree and age 24 at (34,77) render a tree of type TreeType.cherry_tree and age 18 at (71,41) render a tree of type TreeType.cherry_tree and age 30 at (63,33) render a tree of type TreeType.cherry_tree and age 13 at (56,53) render a tree of type TreeType.peach_tree and age 27 at (44,80) render a tree of type TreeType.peach_tree and age 21 at (29,60) render a tree of type TreeType.peach_tree and age 14 at (62,52) render a tree of type TreeType.peach_tree and age 20 at (37,63) render a tree of type TreeType.peach_tree and age 7 at (30,8) {<TreeType.apple_tree: 1>: <__main__.Tree object at 0x00000253D1183AC8>,<TreeType.cherry_tree: 2>: <__main__.Tree object at 0x00000253D1187080>, <TreeType.peach_tree: 3>: <__main__.Tree object at 0x00000253D1187978>}其實可以發現同一類型的樹對象,其ID均一樣,而其size屬性卻不一樣,這是由于在執行__init__方法時,返回類型池中的對象后,在進行初始化會size屬性會覆蓋前面返回的對象的size屬性值。
總結:
該示例中,在__new__方法中實現類不可變數據的共享。
在__init__方法中實現了可變數據的獨立,即不共享。
總結
以上是生活随笔為你收集整理的python设计模式之享元模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高效程序员的 7 项技能
- 下一篇: apache mysql php 安装配