Flyweight
动机(Motivation)
- 在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。
- 如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
模式定义
运行共享技术有效地支持大量细粒度的对象。
——《设计模式》GoF
要点总结
- 面向对象很好地解决了抽象性的问题,但是作为yield运行机器中的程序实体,我们需要考虑对象的代价问题,
Flyweight主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。 - Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的压力。在具体实现方面,要注意对象状态的处理。
- 对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
例子
# -*- coding:utf-8 -*-
import random
class TreeType(object):
Apple = 0
Cherry = 1
Peach = 2
class Tree(object):
tree_pool = {}
def __new__(cls, tree_type):
obj = cls.tree_pool.get(tree_type)
if not obj:
obj = object.__new__(cls)
obj.tree_type = tree_type
cls.tree_pool[tree_type] = obj
return obj
def render(self, x, y):
print('render a tree of type {} at ({}, {})'.format(self.tree_type, x, y))
def main():
rnd = random.Random()
min_point, max_point = 0, 100
tree_counter = 0
for _ in range(10):
t1 = Tree(TreeType.Apple)
t1.render(rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(3):
t2 = Tree(TreeType.Cherry)
t2.render(rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(5):
t3 = Tree(TreeType.Peach)
t3.render(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.tree_pool)))
t4 = Tree(TreeType.Cherry)
t5 = Tree(TreeType.Cherry)
print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
t6 = Tree(TreeType.Apple)
print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))
if __name__ == '__main__':
main()
输出:
render a tree of type 0 at (25, 60)
render a tree of type 0 at (43, 41)
render a tree of type 0 at (67, 78)
render a tree of type 0 at (73, 77)
render a tree of type 0 at (96, 59)
render a tree of type 0 at (98, 16)
render a tree of type 0 at (59, 72)
render a tree of type 0 at (12, 40)
render a tree of type 0 at (10, 60)
render a tree of type 0 at (52, 44)
render a tree of type 1 at (100, 26)
render a tree of type 1 at (37, 12)
render a tree of type 1 at (11, 46)
render a tree of type 2 at (23, 22)
render a tree of type 2 at (74, 32)
render a tree of type 2 at (36, 15)
render a tree of type 2 at (49, 4)
render a tree of type 2 at (33, 27)
trees rendered: 18
trees actually created: 3
4427929552 == 4427929552? True
4427929552 == 4427929488? False
输出结果可知,虽然渲染了18棵树,但仅分配了3棵树的内存。
因为同一类树只分配了一次内存,因此id(t4)==id(t5)
.
即当使用享元时,我们不能依赖对象的ID来判断。
object.__new__
用法可以参考Python中的new(cls) 以及 单例模式
网友评论