1. 设计模式基本原则
- 单一职责: 一个类或者模块只负责完成一个职责(或者功能)
- 开闭原则: 对扩展开放、对修改关闭
- 里式替换: 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏
- 接口隔离原则: 客户端不应该被强迫依赖它不需要的接口
- 依赖反转原则: 高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。
- 迪米特法则: 每个模块只应该了解那些与它关系密切的模块的有限知识
2. 单例设计模式
- 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 主要解决:一个全局使用的类频繁地创建与销毁
- 何时使用:当您想控制实例数目,节省系统资源的时候
2.1 饿汉式单例
- 定义:类加载时就创建好实例
- 方法:类加载前就初始化变量,如java的静态变量和python的类变量
class IdMaker:
# python 的类变量会被多个类,实例共享
__instance = None
# __id 也是类变量,多个实例或类共享
__id = -1
# python 在类加载阶段,通过父类的 __new__ 创建实例,如果我们重写 __new__
# 就不会调用父类的 __new__ ,就会调用我们写的 __new__ 创建实例
# __new__ 需要返回一个实例,如果不返回,就不会实例化
def __new__(cls):
if cls.__instance is None:
# 父类的 __new__ ,参数接收一个类名,会返回类的实例
cls.__instance = super().__new__(cls)
return cls.__instance
# 计数器,在获取前,进行 + 1
def get_id(self):
self.__id += 1
return self.__id
def test_id_maker():
# IdMaker 是单例类,只允许有一个实例
id1 = IdMaker().get_id()
id2 = IdMaker().get_id()
id3 = IdMaker().get_id()
print(id1, id2, id3)
if __name__ == "__main__":
test_id_maker()
# 0 1 2
2.2 懒汉式单例
- 定义:类使用阶段才会创建实例,由于在使用实例时创建,避免初始化卡慢
from threading import Lock
class IdMaker:
# 初始化类变量
__instance = None
__id = -1
# 初始化线程锁
__instance_lock = Lock()
# 如果 __new__ 抛出异常,就不允许调用者进行实例化
def __new__(cls):
raise ValueError('不允许IdMaker类实例化')
# 通过类方法,调用父类的__new__方法,对IdMaker进行实例化
@classmethod
def get_instance(cls):
# with 会帮我们自动的上锁和释放,不用我们操心
with cls.__instance_lock:
if cls.__instance is None:
# 因为我们的 __new__ 代码不允许进行实例化,所以可以借用父类的 __new__ 进行实例化
cls.__instance = super().__new__(cls)
return cls.__instance
def get_id(self):
self.__id += 1
return self.__id
if __name__ == '__main__':
id1 = IdMaker.get_instance().get_id()
id2 = IdMaker.get_instance().get_id()
id3 = IdMaker.get_instance().get_id()
print(id1, id2, id3)
3. 工厂设计模式
- 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
- 主要解决:主要解决接口选择的问题
- 何时使用:我们明确地计划不同条件下创建不同实例时
3.1 简单工厂
- 把创建大量实例的代码放到工厂类中
# Demo 用于加载不同的文件,对不同的文件作不同的处理
# 问题:如果创建对象的代码比较多,可能还会创建 text ,md,yml 等等
# 简单工厂解决:把对象的创建移动到其它类中, load 方法就会很简洁
class Demo:
def load(self, rule):
parse = ParseRuleFactory().create_parse(rule)
# 调用对象的方法进行操作
parse.parse()
# 简单工厂类:用于实例的创建,根据 rule 创建不同的实例。本质就是把 Demo 中原来创建实例的代码,给迁移过来
class ParseRuleFactory:
def create_parse(self, rule):
parse = None
# 根据不同的 rule ,创建不同的对象
if "xml" == rule:
parse = XmlParse()
elif "json" == rule:
parse = JsonParse()
else:
parse = OtherParse()
return parse
# 相当于接口,用于规范各个解析类
# 每个解析类都要实现 parse 方法,否则在调用的时候就会报错
class IParse:
def parse(self):
raise ValueError()
class XmlParse(IParse):
def parse(self):
print("XmlParse")
class JsonParse(IParse):
def parse(self):
print("JsonParse")
class OtherParse(IParse):
def parse(self):
print("OtherParse")
if __name__ == "__main__":
Demo().load("json")
3.2 工厂方法
- 如果创建实例的代码非常复杂,就可以把创建实例的代码单独放入一个类
# 问题:简单工厂不能解决创建实例的代码可能很复杂,即使迁移到了简单工厂中,复杂的创建过程依旧存在
# 解决:使用工厂方法,把创建过程封装到工厂类
class Demo:
def load(self, rule):
parse = None
if "xml" == rule:
# 省略 1000 行代码
parse = XmlParse()
elif "json" == rule:
parse = JsonParseRuleFactory().create_parse()
else:
# 省略 1000 行代码
parse = OtherParse()
# 调用对象的方法进行操作
parse.parse()
# 相当于接口,用于规范各个工厂类
class IParseRuleFactory:
def create_parse(self):
raise ValueError()
# 工厂:把 Json 的解析放到此工厂下面
class JsonParseRuleFactory(IParseRuleFactory):
def create_parse(self):
# 省略 1000 行代码
return JsonParse()
# 相当于接口,用于规范各个解析类
# 每个解析类都要实现 parse 方法,否则在调用的时候就会报错
class IParse:
def parse(self):
raise ValueError()
class XmlParse(IParse):
def parse(self):
print("XmlParse")
class JsonParse(IParse):
def parse(self):
print("JsonParse")
class OtherParse(IParse):
def parse(self):
print("OtherParse")
if __name__ == "__main__":
Demo().load("json")
3.3 抽象工厂
- 若每个工厂要创建多个实例,可使用抽象工厂
# 问题:如果多个公司都要封装工厂,比如 A, B, C ...公司都要封装自己的工厂,就要封装 n 个工厂类
# 解决:可以使用抽象工厂解决问题,每个工厂类可以创建多个实例,比如 JsonParseRuleFactory ,可以创建 A, B, C 公司的实例
# 一个工厂类,可以生成多个公司的解析方法
class IParseRuleFactory:
def a_create_parse(self):
raise ValueError()
def b_create_parse(self):
raise ValueError()
def c_create_parse(self):
raise ValueError()
# 实现时候,一个工厂类就可以生成多个公司的实例
class JsonParseRuleFactory(IParseRuleFactory):
def a_create_parse(self):
pass
def b_create_parse(self):
pass
def c_create_parse(self):
pass
4. 建造者模式
- 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
- 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
- 何时使用:一些基本部件不会变,而其组合经常变化的时候
- 应用实例: 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"
实例:建造者模式
5. 代理模式
- 定义:创建具有现有对象的对象,以便向外界提供功能接口
- 意图:为其他对象提供一种代理以控制对这个对象的访问。
- 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层
- 何时使用:想在访问一个类时做一些控制
-
应用实例:
- 远程代理:在代码中加入 Web 引用
- 虚拟代理:存放实体化需要很久的对象,比如网页先看到文字,后看到图片
- 安全代理:控制真实对象的访问权限
- 智能指引:调用真实对象时,处理另外一些事,比如计算真实对象的引用次数