设计模式八:享元模式和单例模式
什么是享元模式
享元模式是一个用于优化的设计模式,通过为相似对象引入数据共存来最小化内存使用,提升性能。
一个享元就是一个包含状态独立的不可变数据的对象,依赖状态的可变数据不是享元的一部分,每个对象的这种信息不同,无法共享。
享元是一种特定于面向对象编程优化的设计模式,关注的是共享对象数据。
使用场景
应用需要使用大量的对象。
对象太多,存储/渲染的代价太大。
对象ID对于应用不重要。
典型案例
FPS游戏,玩家共享一些状态,比如外在表现和行为,比如跳起低头等行为可以创建一个享元包含所有共同数据,当然也有可变数据,但这些数据不能是享元的一部分,比如当前位置 枪支等。
补充知识
一个对象的实例化过程是先执行类的__new__方法,再调调用__init__方法。
享元模式和单例模式都是在__new__方法中进行实现。
实例代码
from enum import Enum
PeopleType = Enum('People','Chinese American')
class People:
#对象池,所有实例共享的一个变量
pool = dict()
def __new__(cls,types):
#当客户端创建一个实例时,以类型传入
#检查是否创建过相同种类的人类,
#如果有,则返回之前创建的对象,反之将新的种类添加到池中,并返回相应的新对象
obj = cls.pool.get(types,None)
if not obj:
obj = object.__new__(cls)
cls.pool[types] = obj
obj.types = types
return obj
#渲染方法,需要用户端代码显示传递
def render(self,name,age):
print('render a people of {} and name is {} age {}'.format(self.types,name,age))
def Flyweightmain():
people1 = People(PeopleType.Chinese)
people1.render('zhang san',18)
people2 = People(PeopleType.Chinese)
people2.render('li si',20)
people3 = People(PeopleType.American)
people3.render('Arthur',28)
#由于人类1和人类2是同人种,所以应该id是一样的
print('people1 id {} == people2 id {}? {}'.format(id(people1),id(people2),id(people1)==id(people2)))
#人类1和人类3是不同人种,所以id是不一样的
print('people1 id {} == people3 id {}? {}'.format(id(people1),id(people3),id(people1)==id(people3)))
if __name__ == "__main__":
Flyweightmain()
单例模式
确保某一个类只有一个实例存在,希望整个系统中,某个类只出现一个实施。
同样有最小化内存使用,提升性能的效果。
典型案例
读取配置文件的类。一个大型的系统中,在读取配置文件时,如果过多的创建读取配置文件的类的话,资源会被浪费。
单例模式和享元模式的区别
享元模式可以再次创建对象,也可以取缓存对象。
单例模式则是严格控制单个进程中的一个实例对象。
实例代码
#单例模式需要threading模块
import threading
class Singleton:
#加锁,防止多线程获取对象时创建过多实例
_instance_lock = threading.Lock()
def __init__(self,*args,**kwargs):
pass
def __new__(cls,*args,**kwargs):
#判断是否存在实例,如果存在,返回实例,如果不存在就创建
if not hasattr(cls,'_instance'):
with Singleton._instance_lock:
if not hasattr(cls,'_instance'):
Singleton._instance = super().__new__(cls)
return Singleton._instance
def Singletonmain():
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
if __name__ == "__main__":
Singletonmain()