简单工厂模式
-
简单工厂模式(Factory Pattern) 提供了一种创建对象的最佳方式。我们希望有一个工厂可以创造不同类型的对象,调用者在创建对象时,只需要指定其名字即可获取一个相应的对象,从而隐藏调用者无需关注的对象创建逻辑
-
优点:将对象的创建交给专门的工厂类负责,实现了对象的创建和对象的使用分离,简单工厂的特点就是“简单粗暴”,通过一个含参的工厂方法,我们可以实例化任何产品类,上至飞机火箭,下至土豆面条,无所不能。所以简单工厂有一个别名:上帝类。
-
缺点:不符合开闭原则(对拓展开放,对修改关闭),增加新的具体产品需要修改工厂类的判断逻辑代码,当所要生产产品种类非常多时,工厂方法的if 判断也会非常多,不容易维护。
-
代码类图
-
代码示例:
from abc import ABC, abstractmethod # 手机抽象类 class BasePhone(ABC): @abstractmethod def call(self): ... @abstractmethod def send_sms(self): ... # 华为手机实现类 class HuaweiPhone(BasePhone): def call(self): print("华为打电话") def send_sms(self): print("华为发短信") # 小米手机实现类 class XiaomiPhone(BasePhone): def call(self): print("小米打电话") def send_sms(self): print("小米发短信") # 手机工厂类 class PhoneFactory: def get_pen(self, brand): """ 工厂方法 """ if brand == "huawei": return HuaweiPhone() elif brand == 'xiaomi': return XiaomiPhone() else: return None if __name__ == '__main__': phone_factory = PhoneFactory() # 创建手机工厂 phone_factory.get_pen('huawei').call() # 创建华为手机打电话 phone_factory.get_pen('xiaomi').send_sms() # 创建小米手机发短信
工厂方法模式
工厂方法模式就很好的减轻了工厂类的负担,我们为将简单工厂中的每一个if分支的产品都创建一个专属的工厂;同时增加某一类产品由简单工厂的增加一个if分支改为增加一个工厂类即可,使得工厂类符合开放-封闭原则。
-
优点:符合开闭原则,“简单工厂模式" + “开闭原则” = 工厂方法
-
缺点:对于某些可以形成产品族(一组产品)的情况处理比较复杂。
-
结构:
-
抽象工厂(Creator)角色:工厂方法模式的核心,任何工厂类都必须实现这个接口。
-
具体工厂( Concrete Creator)角色:具体工厂类是抽象工厂的一个实现,负责实例化产品对象
-
抽象(Product)角色:工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口
-
具体产品(Concrete Product)角色:工厂方法模式所创建的具体实例对象
-
-
代码类图
-
代码示例
from abc import ABC, abstractmethod # 手机抽象类 class BasePhone(ABC): @abstractmethod def call(self): ... @abstractmethod def send_sms(self): ... class BaseRouter(ABC): @abstractmethod def wifi(self): ... # 华为路由器实现类 class HuaweiRouter(BaseRouter): def wifi(self): print("华为路由器") # 小米路由器实现类 class XiaomiRouter(BaseRouter): def wifi(self): print("小米路由器") # 华为手机实现类 class HuaweiPhone(BasePhone): def call(self): print("华为打电话") def send_sms(self): print("华为发短信") # 小米手机实现类 class XiaomiPhone(BasePhone): def call(self): print("小米打电话") def send_sms(self): print("小米发短信") # 工厂抽象类 class BaseFactory(ABC): @abstractmethod def create_product(self): ... # 华为工厂实现类 class HuaweiPhoneFactory(BaseFactory): def create_product(self): return HuaweiPhone() # 小米手机工厂实现类 class XiaomiPhoneFactory(BaseFactory): def create_product(self): return XiaomiPhone() # 华为路由器工厂实现类 class HuaweiRouterFactory(BaseFactory): def create_product(self): return HuaweiRouter() # 小米路由器工厂实现类 class XiaomiRouterFactory(BaseFactory): def create_product(self): return XiaomiRouter() if __name__ == '__main__': HuaweiPhoneFactory().create_product().call() # 使用华为手机工厂创建手机打电话 HuaweiRouterFactory().create_product().wifi() # 使用华为路由器工厂创建路由器发射wifi XiaomiPhoneFactory().create_product().send_sms() # 使用小米手机工厂创建手机发信息 XiaomiRouterFactory().create_product().wifi() # 使用华为手机工厂创建手机打电话
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
- 类继承关系图
- 代码示例
from abc import ABC, abstractmethod # 手机抽象类 class BasePhone(ABC): @abstractmethod def call(self): ... @abstractmethod def send_sms(self): ... # 路由器抽象类 class BaseRouter(ABC): @abstractmethod def wifi(self): ... # 华为手机实现类 class HuaweiRouter(BaseRouter): def wifi(self): print("华为路由器") # 小米路由器实现类 class XiaomiRouter(BaseRouter): def wifi(self): print("小米路由器") # 华为手机实现类 class HuaweiPhone(BasePhone): def call(self): print("华为打电话") def send_sms(self): print("华为发短信") # 小米手机实现类 class XiaomiPhone(BasePhone): def call(self): print("小米打电话") def send_sms(self): print("小米发短信") # 工厂抽象类 class BaseFactory(ABC): @abstractmethod def create_phone(self): ... @abstractmethod def create_router(self): ... # 华为工厂实现类 class HuaweiFactory(BaseFactory): def create_phone(self): return HuaweiPhone() def create_router(self): return HuaweiRouter() # 小米工厂实现类 class XiaomiFactory(BaseFactory): def create_phone(self): return XiaomiPhone() def create_router(self): return XiaomiRouter() # 工厂生成器类 class FactoryProducer: @staticmethod def get_huawei_factory(): return HuaweiFactory() @staticmethod def get_xiaomi_factory(): return XiaomiFactory() if __name__ == '__main__': FactoryProducer.get_huawei_factory().create_phone().call() # 获取华为工厂,创建华为手机,打电话 FactoryProducer.get_huawei_factory().create_router().wifi() # 获取华为工厂,创建华为路由器,发射wifi FactoryProducer.get_xiaomi_factory().create_phone().send_sms() # 获取小米工厂,创建小米手机,打电话 FactoryProducer.get_xiaomi_factory().create_router().wifi() # 获取小米工厂,创建小米路由器,发射wifi
单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如:数据库链接、Socket创建链接
-
基于模块实现
Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。mysingleton.py
class Singleton(object): def foo(self): pass singleton = Singleton()
main.py
from mysingleton import singleton
-
使用装饰器实现
def singleton(cls): # 单下划线的作用是这个变量只能在当前模块里访问,仅仅是一种提示作用 # 创建一个字典用来保存类的实例对象 _instance = {} def _singleton(*args, **kwargs): # 先判断这个类有没有对象 if cls not in _instance: _instance[cls] = cls(*args, **kwargs) # 创建一个对象,并保存到字典当中 # 将实例对象返回 return _instance[cls] return _singleton @singleton class A(object): a = 1 def __init__(self, x=0): self.x = x print('这是A的类的初始化方法') a1 = A(2) a2 = A(3) print(id(a1), id(a2))
-
使用类实现
这种方式创建的单例,必须使用Singleton_get_instance()方法,如果使用Singleton()
的话,得到的并不是单例.所以我们推荐使用__new__()方法来创建单例,这样创建的单例可以使用类名()的方法进行实例化对象class Singleton(object): def __init__(self, *args, **kwargs): pass @classmethod def get_instance(cls, *args, **kwargs): # 利用反射,看看这个类有没有_instance属性 if not hasattr(Singleton, '_instance'): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance s1 = Singleton() # 使用这种方式创建实例的时候,并不能保证单例 s2 = Singleton.get_instance() # 只有使用这种方式创建的时候才可以实现单例 s3 = Singleton() s4 = Singleton.get_instance() print(id(s1), id(s2), id(s3), id(s4))
注意,这样的单例模式在单线程下是安全的,但是如果遇到多线程,就会出现问题。因为在一个对象创建的过程中,另外一个对象也创建了.当它判断的时候,会先去获取_instance属性,因为这个时候还没有,它就会调用**
init**()
方法.结果就是调用了10次,然后就创建了10个对象。
对于这种问题我们可以通过加锁的方式解决,在获取对象属性_instance的时候加锁,如果已经有人在获取对象了,其他的人如果要获取这个对象,就要等一哈.因为前面的那个人,可能在第一次创建对象.。
import time
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self, *args, **kwargs):
time.sleep(1)
@classmethod
def get_instance(cls, *args, **kwargs):
if not hasattr(Singleton, '_instance'):
with Singleton._instance_lock:
if not hasattr(Singleton, '_instance'):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
obj = Singleton.get_instance(arg)
print(obj)
for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()
obj = Singleton.get_instance()
print(obj)
-
使用
__new__()
方法实现**(推荐方式)**当我们实例化一个对象时,是先执行了类的
__new__()
方法(我们没写时,默认调用object.__new__()
),
实例化对象;然后再执行类的__init__方法,对这个对象进行初始化, 所有我们可以基于这个,实现单例模式import threading class Singleton(object): _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 obj1 = Singleton() obj2 = Singleton() print(obj1, obj2) def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task, args=[i, ]) t.start()
-
使用metaclass(元类)实现
import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self, name): self.name = name obj1 = Foo('name') obj2 = Foo('name') print(obj1, obj2)
-
单例应用示例(数据库连接池)
import pymysql import threading from DBUtils.PooledDB import PooledDB class SingletonDBPool(object): _instance_lock = threading.Lock() def __init__(self): self.pool = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] ping=0, # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always host='127.0.0.1', port=3306, user='root', password='123', database='pooldb', charset='utf8' ) def __new__(cls, *args, **kwargs): if not hasattr(SingletonDBPool, "_instance"): with SingletonDBPool._instance_lock: if not hasattr(SingletonDBPool, "_instance"): SingletonDBPool._instance = object.__new__(cls, *args, **kwargs) return SingletonDBPool._instance def connect(self): return self.pool.connection()
原型模式
原型模式(Prototype Pattern)使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的典型应用
-
优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
- 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
- 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
-
缺点
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
-
适用场景
- 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
-
示例代码
from abc import ABC, abstractmethod from copy import copy, deepcopy # 原型抽象类 class Prototype(ABC): @abstractmethod def copy(self): ... # 工作经历类 class WorkExperience(object): def __init__(self): self.timearea = '' self.company = '' def set_workexperience(self, timearea, company): self.timearea = timearea self.company = company # 简历类 class Resume(Prototype): def __init__(self, name): self.name = name self.workexperience = WorkExperience() def set_personinfo(self, sex, age): self.sex = sex self.age = age pass def set_workexperience(self, timearea, company): self.workexperience.set_workexperience(timearea, company) def display(self): print(self.name, self.sex, self.age, '工作经历:', self.workexperience.timearea, self.workexperience.company) def copy(self): return deepcopy(self) if __name__ == '__main__': resume = Resume('zs') resume_copy = resume.copy() # 深拷贝对象 resume_copy.set_personinfo('男', 28) resume_copy.set_workexperience('2016-2017', 'AA') # 修改深拷贝的对象的工作经历 resume_copy.display()