淺談Python設計模式 - 享元模式


 

  聲明:本系列文章主要參考《精通Python設計模式》一書,並且參考一些資料,結合自己的一些看法來總結而來。

  享元模式:

  享元模式是一種用於解決資源和性能壓力時會使用到的設計模式,它的核心思想是通過引入數據共享來提升性能

  在開發3D游戲時,例如有成千上萬的士兵或者有成千上萬棵樹,如果一個3D地帶的每個對象都單獨創建,不使用數據共享,那么性能是無法接受的。

  故享元設計模式就是通過為相似對象映入數據共享來最小化內存的使用,提升性能。

  既然要創建成千上萬個士兵,那么若他們的數據屬性行為都是一樣的,那豈不是黏一塊去了。這時候就會有:可變數據和不可變數據的概念。

  即 -- 重點在於將不可變(可共享)的屬性與可變的屬性區分開。相同類型的對象共享不可變(可共享)的數據,而每個對象又有其獨立的數據,這部分數據即為:可變的屬性(不可共享數據)。

  實現:

  其實享元模式的實現與單例模式的實現方式十分相似,比如:單例模式實現的是一個類對象只允許有一個實例對象,而享元模式則是一個類對象只允許創建不同類型的對象,這樣保證同一類型的對象共享不可變數據。

  直接看書中代碼吧:

from enum import Enum

TreeType = 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] = obj
            obj.tree_type = tree_type
        return obj

    def __init(self,size ):
        self.size = size
    
    def 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 Enum

def main():
    rnd = random.Random()
    age_min, age_max = 1, 30
    min_piont, max_point = 0, 100
    tree_counter = 0

    for _ 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 += 1
    
    for _ 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 += 1
    
    for _ 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 += 1
    
    print(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__方法中實現了可變數據的獨立,即不共享。

 

  over~~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM