享元模式(Flyweight Pattern)

定义

“享元模式(Flyweight Pattern)是池技术的重要实现方式,其定义如下:Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。)”Excerpt From: 秦小波 著. “设计模式之禅(第2版)(华章原创精品).” iBooks.

“如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式” Excerpt From: 程杰. “大话设计模式.” iBooks.

使用共享对象,共享对象的处理是享元模式的关键,把哪些内容作为共享对象,以及对象如何存储在哪儿,怎么存储的问题。
理解享元模式的应用场景,为了减少内存的使用,将对象元素固定的部分提出来,通过hashmap的方式存储,不会为每个对象实例创建这个信息,而是所有实例共享这个数据。
参考Java的UML图示如下:

@startuml
FlyweightFactory *--> Flyweight
Flyweight <|-- ConcreteFlyweight
Flyweight <|-- UnsharedConcreteFlyweight
Client --> FlyweightFactory
Client --> UnsharedConcreteFlyweight
Client --> ConcreteFlyweight
@enduml

代码实现

看似复杂的java UML模型,其实用python来实现还是比较简单的,就是利用python class的类变量,将共享的信息存储在类变量中,通过向类变量的pool中添加key value,来共享这些数据,定义接口从pool中获取数据即可。

'''
享元模式的应用举例,假设我们在游戏开发过程中,需要创建一片森林,而森林中有不同的树种,树种的类型就那么几种,是固定的,而其他的比如位置,树龄是不同的
此时共享的部分各种树种类就可以被提出来作为共享的类变量

'''
import random
from enum import Enum

TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')

class Tree:
    pool = dict() # 定义对象池

    def __new__(cls, tree_type):
        obj = cls.pool.get(tree_type, None)  # 从对象池中获取对象,如果没有填None
        if not obj:
            obj = object.__new__(cls)  # 创建一个新的对象
            cls.pool[tree_type] = obj   # 存入对象池
            obj.tree_type = tree_type
        return obj

    def render(self, age, x, y):
        '''
        一直没理解享元模式这个render的作用是什么,其实render函数表示的是一种操作。举个例子:
        连接数据库,pool表示的是与数据库的连接池,而render表示的是各种数据库交互的动作get、commit等等,此时,age和x、y表示的是一个个动作的内容
        '''
        print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))

def main():
    rnd = random.Random()
    age_min, age_max = 1, 30    # 单位为年
    min_point, 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_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    for _ in range(3):
        t2 = Tree(TreeType.cherry_tree)
        t2.render(rnd.randint(age_min, age_max),
                  rnd.randint(min_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    for _ in range(5):
        t3 = Tree(TreeType.peach_tree)
        t3.render(rnd.randint(age_min, age_max),
                  rnd.randint(min_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    print('trees rendered: {}'.format(tree_counter))
    print('trees actually created: {}'.format(len(Tree.pool)))

    t4 = Tree(TreeType.cherry_tree)
    t4.render(rnd.randint(age_min, age_max),
              rnd.randint(min_point, max_point),
              rnd.randint(min_point, max_point))
    t5 = Tree(TreeType.cherry_tree)
    t5.render(rnd.randint(age_min, age_max),
              rnd.randint(min_point, max_point),
              rnd.randint(min_point, max_point))
    t6 = Tree(TreeType.apple_tree)
    print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
    print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))


if __name__ == "__main__":
    main()

知识点

下集预告 代理模式


neilliu
59 声望9 粉丝

coder is coding code snippet,coder change the world!