【python 类】【部分转发】设计模式 -- 享元模式

今天看https://www.cnblogs.com/alex3714/p/5760582.html

 

看到享元模式时,真的感觉长见识了~~~

最好的例子就是:绘制有1W个星星,如果把从生成1W个实例,变成20个实例。 (此时享元模式就是最佳的解决方案)。

 

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 # Flyweight模式,顾名思义,就是共享元数据
 6 # 在我们面向对象设计过程中,我们常常会面临着对象实例过多的问题,如果对象实例过多这将是我们系统性能提高的一个瓶颈。
 7 # 假设我们要设计一个星空场景,现在我们需要实例星星对象,我们可以实例每一颗星星,但随着我们实例星星对象增多整个场景就越来越慢了,
 8 # 如果你实例了1000+颗星星要你去维护,这可是一个吃力不讨好的工作。我们必须找到一个合适的方法解决以上问题,这就是今天要介绍的享元模式(Flyweight)。
 9 
10 # 享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象。
11 #
12 # 抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
13 #
14 # 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
15 #
16 # 复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
17 #
18 # 享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
19 #
20 # 客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。
21 #
22 # 内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。
23 
24 
25 class FlyweightBase(object):
26     _instances = dict()
27 
28     def __init__(self, *args, **kwargs):
29         # 继承的子类必须初始化
30         raise NotImplementedError
31 
32     def __new__(cls, *args, **kwargs):
33         # print(cls._instances,type(cls))
34         return cls._instances.setdefault(
35             (cls, args, tuple(kwargs.items())),
36 
37             super(FlyweightBase, cls).__new__(cls)
38         )
39 
40     def test_data(self):
41         pass
42 
43 
44 class Spam(FlyweightBase):
45     '''精子类'''
46 
47     def __init__(self, a, b):
48         self.a = a
49         self.b = b
50 
51     def test_data(self):
52         print("精子准备好了", self.a, self.b)
53 
54 
55 class Egg(FlyweightBase):
56     '''卵类'''
57 
58     def __init__(self, x, y):
59         self.x = x
60         self.y = y
61 
62     def test_data(self):
63         print("卵子准备好了", self.x, self.y)
64 
65 
66 spam1 = Spam(1, 'abc')
67 spam2 = Spam(1, 'abc')
68 spam3 = Spam(3, 'DEF')
69 
70 print(id(spam1), id(spam2), id(spam3))
71 # 4417487928 4417487928 4417487984
72 
73 egg1 = Egg(1, 'abc')
74 egg2 = Egg(4,'abc')
75 print(id(egg1), id(egg2))
76 # 4417488096 4417488152
77 
78 assert spam1 is spam2
79 # 断言正确
80 
81 assert egg1 is not spam1
82 # 断言正确
83 
84 spam2.test_data()
85 egg1.test_data()
86 print(egg1._instances)
87 # {(<class '__main__.Spam'>, (1, 'abc'), ()): <__main__.Spam object at 0x1074d8438>, 
88 #  (<class '__main__.Spam'>, (3, 'DEF'), ()): <__main__.Spam object at 0x1074d8470>, 
89 #  (<class '__main__.Egg'>, (1, 'abc'), ()): <__main__.Egg object at 0x1074d84e0>, 
90 #  (<class '__main__.Egg'>, (4, 'abc'), ()): <__main__.Egg object at 0x1074d8518>}
91 
92 
93 print(egg1._instances.keys())
94 # [(<class '__main__.Spam'>, (1, 'abc'), ()), 
95 #  (<class '__main__.Spam'>, (3, 'DEF'), ()), 
96 #  (<class '__main__.Egg'>, (1, 'abc'), ()), 
97 #  (<class '__main__.Egg'>, (4, 'abc'), ())]
享元模式代码

 

实现的关键是利用了字典的key可以通过tuple 带很多的信息~~~ 

 

收获:平时使用时,也应该考虑使用tuple作为key~~

 

posted @ 2019-02-25 18:12  小小Guido妹  阅读(151)  评论(0编辑  收藏  举报