设计模式创造者模式--python
I.创建者模式
1.享元模式
-
享元模式采用一个共享来避免大量拥有相同内容对象的开销,这种开销最常见,直观就是内存损耗,享元模式以共享的方式高效支持大量的细粒度的对象。(享元模式介于单例和不加控制多例模式之间,非常灵活使用性和使用场景大于单例模式)。
-
优点:节省空间。
-
缺点:如果需要维护享元项很多,查找时间耗费,该模式时间换空间。
-
应用
不希望建立多个连接,但又要在同一解释器下操作好多台机器的数据库,当传参的机器的ip端口不同时候,那肯定要创建一个新的连接了,这种使用享元模式适合
1.创建型模式-元类方式
class FlyweightMetaClass(type):
def __init__(cls,name,bases,dict):
super(FlyweightMetaClass, cls).__init__(name,bases,dict)
cls._instance_map = {}
@staticmethod
def _make_arguments_to_key(args,kwds):
key = args
if kwds:
sorted_items = sorted(kwds.items())
for item in sorted_items:
key += item
return key
def __call__(cls, *args, **kw):
# 通过 _make_arguments_to_key 让参数构建成key
cache_key = "{}__{}".format(cls, cls._make_arguments_to_key(args,kw))
# 判断key如果不存在添加在_instance_map,存在直接返回
if cache_key not in cls._instance_map:
cls._instance_map[cache_key] = super().__call__(*args,**kw)
return cls._instance_map[cache_key]
class Auth(metaclass=FlyweightMetaClass):
# 首先调用其元类的__call__方法
def __init__(self,x1,x2):
print("初始化:{},{}".format(x1,x2))
if __name__ == '__main__':
Auth(5, 10)
Auth(5, 10)
Auth(10, 20)
# 打印结果:
# 初始化:5,10
# 初始化:10,20
# 区别于单例模式,整体规划_instance_map有则直接返回,没有则创建
2.享元模式-通过装饰器实现
from functools import wraps
def flyweight(cls):
_instance = {}
def _make_arguments_to_key(args,kwds):
key = args
if kwds:
sorted_items = sorted(kwds.items())
for item in sorted_items:
key += item
return key
@wraps(cls)
def _flyweight(*args,**kwargs):
cache_key = "{}_{}".format(cls,_make_arguments_to_key(args,kwargs))
if cache_key not in _instance:
_instance[cache_key] = cls(*args,**kwargs)
return _instance[cache_key]
return _flyweight
@flyweight
class A:
def __init__(self,identity):
self.idenntity = identity
def eat(self):
print("{}吃饭".format(self.idenntity))
if __name__ == '__main__':
# a1与a2是同一个对象
a1 = A("001")
a2 = A("001")
print(a1 == a2)
a1.eat()
a2.eat()
a3 = A("003")
print(a1 == a3)
a3.eat()
"""
True
001吃饭
001吃饭
False
003吃饭
"""
3.单例使用不当,造成问题
from redis import Redis
class MyRedis:
_inst = None
def __new__(cls, *args, **kwargs):
if not cls._inst:
self = super().__new__(cls)
self.__my_init__(*args, **kwargs)
cls._inst = self
return cls._inst
def __my_init__(self,redis_db):
print(f'传入的redis db是 {redis_db}')
self.r = Redis(host='127.0.0.1',port=6379,db=redis_db)
def set(self,key,value):
self.r.set(key,value)
if __name__ == '__main__':
"""
此时单例模式一直在db5,按理能放到db6中,应该使用享元模式,单错误的使用了单例模式。
"""
MyRedis(5).set('a',1)
MyRedis(6).set('b', 2)
MyRedis(5).set('c', 3)
MyRedis(6).set('d', 4)
2.原型模式
-
原型模式用于创建重复的对象,同时可以保证性能。它实现了一个原型接口,该接口用于创建当前对象的克隆,当直接创建对象的代价比较大时,则采用这种模式。比如一个对象需要一个高代价的数据库操作之后被创建,我们可以缓存该对象,在下一个请求时返回它的克隆,在需要时候更新数据库,从而减少数据库调用。
-
通过克隆方式创建全新对象,它们内存中拥有新的地址,通常对克隆所产生对象进行修改原型对象不会造成任何影响,每一个克隆对象相互独立,通过不同方式修改可以得到一系列相似但不完全相同对象。
-
应用场景:
1.类初始化需要消化非常多的资源,使用原型模式为易,因为原型模式是在内存中对这个对象进行拷贝,要比直接new这个对象性能要好很多,在这种情况下,需要的对象越多,原型模式体现出优点越明显。 2.如果一个对象初始化需要很多其他对象数据准备或其他资源繁琐计算,那么可以使用原型模式。 3.当需要一个对象大量公共信息,少量字段进行个性化设置时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。
-
优点:性能提高。逃避构造函数约束
-
缺点:配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
-
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
-
代码示例:
import copy
from collections import OrderedDict
class Book:
def __init__(self,name,author,price,**rest):
self.name = name
self.authors = author
self.price = price
self.__dict__.update(rest)
def __str__(self):
mylist = []
ordered = OrderedDict(sorted(self.__dict__.items()))
for i in ordered.keys():
mylist.append("{}:{}".format(i,ordered[i]))
if i == "price":
mylist.append("$")
mylist.append("\n")
return "".join(mylist)
class Prototype:
def __init__(self):
self.objects = dict()
def register(self,identifier,obj):
# 原型列表中注册原型对象
self.objects[identifier] = obj
def unregister(self,identifier):
# 从原型列表中删除原型对象
del self.objects[identifier]
def clone(self,identifier,**attr):
# 根据 identifier 在原型列表中查找原型对象并克隆
found = self.objects.get(identifier)
if not found:
raise ValueError('Incorrect object identifier: {}'.format(identifier))
obj = copy.deepcopy(found)
obj.__dict__.update(attr) # 用新的属性值替换原型对象中的对应属性
return obj
if __name__ == '__main__':
b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'),
price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22',
tags=('C', 'programming', 'algorithms', 'data structures'))
prototype = Prototype()
cid = "k&r-first"
prototype.register(cid,b1)
b2 = prototype.clone(cid,name="The C Programming Language(ANSI)",price=48.99,length=274,publication_date='1988-04-01', edition=2)
for i in (b1,b2):
print(i)
print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))# ID b1 : 2659817871400 != ID b2 : 2659936098400
3.对象池模式
-
此模式可以创造数据连接池,浏览器池等。实现原理是:使用时借,使用完归还。
-
最好使用with语法完成对象借和还,减少调用处代码。
-
资源受限的, 不需要可伸缩性的环境(cpu\内存等物理资源有限):cpu性能不够强劲, 内存比较紧张, 垃圾收集, 内存抖动会造成比较大的影响,需要提高内存管理效率, 响应性比吞吐量更为重要;数量受限的, 比如数据库连接;(创建成本高的对象, 可以考虑是否池化, 比较常见的有线程池(ThreadPoolExecutor), 字节数组池等。)
from queue import Queue
class QueueObject():
def __init__(self,queue,auto_get=False):
self._queue = queue
self.object = self._queue.get() if auto_get else None
def __enter__(self):
"""队列中拿取"""
if self.object is None:
self.object = self._queue.get()
return self.object
def __exit__(self, exc_type, exc_val, exc_tb):
"""从队列中放入"""
if self.object is not None:
self._queue.put(self.object)
self.object = None
def __del__(self):
if self.object is not None:
self._queue.put(self.object)
self.object = None
def main():
sample_queue = Queue()
sample_queue.put('yam')
with QueueObject(sample_queue) as obj:
print('Inside with: {}'.format(obj))
print('Outside with: {}'.format(sample_queue.get()))
sample_queue.put('sam')
queue_object = QueueObject(sample_queue, True)
print('内部 func: {}'.format(queue_object.object))
# print('外部 func: {}'.format(sample_queue.get()))# 队列中为空此处会阻塞
if not sample_queue.empty():
print(sample_queue.get())
"""
Inside with: yam
Outside with: yam
内部 func: sam
"""
if __name__ == '__main__':
main()
4.建造者模式
- 使用多个简单的对象一步一步构造成复杂对象,这种类型设计模式属于创建型模式,它提供一种创建对象最佳方式。
- 主要解决再软件系统中,面临着一个复杂对象创建工作,比如:你去肯德基,汉堡,可乐,薯条,炸鸡翅等是不变的,而组合经常变化的生成了所谓的套餐。
- 优点:建造者独立,易扩展,使用控制细节风险。
- 缺点:产品必须有共同点,范围有限制。如内部变化复杂,会有很多建造类。
- 需要生成的对象具有复杂的内部结构,需要生成的对象内部树形本身相互依赖。
- 注意:与工厂模式的区别是,建造者模式更加关注,零件装配顺序。
import abc
# 1.创建对应的产品抽象类/产品类
class Building(object):
def __init__(self):
self.floor = None
self.size = None
def __repr__(self):
return "Floor:{0.floor}|size:{0.size}".format(self)
# 3.创建构造者抽象类,主要定义构建者通用属性/方法,以及继承者必须实现功能抽象。
class AbsBuilder(object):
def __init__(self):
self.building = None
def new_building(self):
self.building = Building()
@abc.abstractmethod
def build_floor(self):
pass
@abc.abstractmethod
def build_size(self):
pass
# 4.具体构建者类实现
class HouseBuilder(AbsBuilder):
def build_floor(self):
self.building.floor = 'one'
def build_size(self):
self.building.size = '220 squre'
class FlatBuilder(AbsBuilder):
def build_floor(self):
self.building.floor = 'seven'
def build_size(self):
self.building.size = '140 squre'
# 2.创建产品指挥类,最终提供给客户端产品实例对象
class Director(object):
def __init__(self):
self.builder = None
def construct_building(self):
"""
仅在需要时客户端代码才显式地请求指挥者返回最终的对象
:return:
"""
self.builder.new_building()
self.builder.build_floor()
self.builder.build_size()
def get_building(self):
return self.builder.building
class Client(object):
def build(self, build_type):
if build_type == "House":
director = Director()
builder = HouseBuilder()
director.builder = builder
director.construct_building()
building = director.get_building()
print(building)
else:
director = Director()
builder = FlatBuilder()
director.builder = builder
director.construct_building()
building = director.get_building()
print(building)
if __name__ == '__main__':
build_type= "Flat"
client = Client()
client.build(build_type)# Floor:seven|size:140 squre
5.工厂模式
1.简单工厂模式
- 将创建实例工作与使用实例分开,把初始化实例工作放到工厂里进行,使代码更容易维护,使修改代码不会引起太大变动,良好扩展性。
class Xiaomi5:
def __init__(self):
self.phone_name = "小米5"
def send_msg(self):
print("用{}发送短信".format(self.phone_name))
class Xiaomi6:
def __init__(self):
self.phone_name = "小米6"
def send_msg(self):
print("用{}发送短信".format(self.phone_name))
def get_xiaomi_phone(phone_type):
if phone_type == "5":
return Xiaomi5()
if phone_type == "6":
return Xiaomi6()
if __name__ == '__main__':
phone5 = get_xiaomi_phone("5")
phone5.send_msg()# 用小米5发送短信
phone6 = get_xiaomi_phone("6")
phone6.send_msg()# 用小米6发送短信
2.抽象工厂模式
- 当有多个抽象角色时,使用一种工厂模式。它可以向客户端提供一个接口,使客户端再不必指定产品具体情况下创建多个产品对象。
class Xiaomi5:
def __init__(self):
self.phone_name = "小米5"
def send_msg(self):
print("用{}发短信".format(self.phone_name))
class Xiaomi6:
def __init__(self):
self.phone_name = "小米6"
def send_msg(self):
print("用{}发短信".format(self.phone_name))
class XiaomiFactory:
@staticmethod
def get_phone(phone_type):
if phone_type == '5':
return Xiaomi5()
elif phone_type == '6':
return Xiaomi6()
class Apple5:
def __init__(self):
self.phone_name = '苹果5'
def send_msg(self):
print(f'用 {self.phone_name} 发短信')
class Apple6:
def __init__(self):
self.phone_name = '苹果6'
def send_msg(self):
print(f'用 {self.phone_name} 发短信')
class AppleFactory:
@staticmethod
def get_phone(phone_type):
if phone_type == '5':
return Apple5()
elif phone_type == '6':
return Apple6()
class FactoryProducer:
@staticmethod
def get_factory(factory_name):
if factory_name == "xiaomi":
return XiaomiFactory()
else:
return AppleFactory()
if __name__ == '__main__':
factory = FactoryProducer.get_factory("xiaomi")
xiaomi5 = factory.get_phone("5")
xiaomi5.send_msg()# 用小米5发短信
6.单例模式
1.单例模式(元类)
- 它不是最常用设计模式,因面向对象(oop)根本目的要多例,使用oop来实现单例模式。python模块天然单例。
- 优点:延迟初始化(只有在生成对象调用
__init__
里才进行初始化),动态传参初始化。
class Singleton(type):
def __init__(cls,name,bases,dict):
super(Singleton, cls).__init__(name,bases,dict)
cls.instance = None
def __call__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls.instance
class A(metaclass=Singleton):
def __init__(self, identity):
print("执行init")
self.identity = identity
def eat(self):
print("{}吃饭.".format(self.identity))
if __name__ == '__main__':
a1 = A("001")
a2 = A("002")
print(a1 == a2)#True
a1.eat()# 001吃饭.
a2.eat()# 001吃饭.
a3 = A("003")
print(a1 == a3)#True
a3.eat()# 001吃饭.
2.单例模式(装饰器)
import threading
from functools import wraps
def singleton(cls):
"""
单例模式装饰器,新加入线程锁,
更牢固的单例模式,主要解决多线程
如100线程同时实例化情况下可能会
出现三例四例的情况
:param cls:
:return:
"""
_instance = {}
# 这里直接演示了线程安全版单例模式
singleton.__lock = threading.Lock()
@wraps(cls)
def _singleton(*args,**kwargs):
with singleton.__lock:
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]
return _singleton
@singleton
class A:
def __init__(self,identity):
self.identity = identity
def eat(self):
print("{}在吃饭".format(self.identity))
if __name__ == '__main__':
a1 = A('001')
a2 = A('001')
print(a1 == a2)# True
a1.eat()# 001在吃饭
a2.eat()# 001在吃饭
a3 = A('003')
print(a1 == a3)# True
a3.eat()# 001在吃饭
3.单例模式重写new
- 通过new实现单例模式,需要注意,虽然生成对象是同一个,但init会每次都被自动调用,py2这种写法实现的单例模式,init不会自动被调用,python3会被自动调用。当init成本很大,不希望被自动调用,可以改写另外的方式。
- 下面单例模式,每次调用都会执行init
class A:
_inst = None
def __new__(cls, identity):
if not cls._inst:
cls._inst = object.__new__(cls)
return cls._inst
def __init__(self, identity):
print('执行init')
self.identity = identity
def eat(self):
print(f'{self.identity} 吃饭')
if __name__ == '__main__':
a1 = A('001')
a2 = A('001')
print(a1 == a2)
a1.eat()
a2.eat()
a3 = A('003')
print(a1 == a3)
a3.eat()
"""
执行init
执行init
True
001 吃饭
001 吃饭
执行init
True
003 吃饭
"""
- 我们不希望,每次调用对象都要执行init,通过改写new:
class A:
_inst = None
def __new__(cls, *args, **kwargs):
if not cls._inst:
cls._inst = object.__new__(cls)
cls._inst.__custom_init__(*args,**kwargs)
return cls._inst
def __custom_init__(self,identity):## 这行也是是重点。去掉了__init__方法,init会被自动调用,改成在new里面主动调用。
print("执行custom init")
self.identity = identity
def eat(self):
print("{}吃饭".format(self.identity))
if __name__ == '__main__':
a1 = A('001')
a2 = A('002')
print(a1 == a2)
a1.eat()
a2.eat()
a3 = A('003')
print(a1 == a3)
a3.eat()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库