Python 测开27期 - 柒柒 - 设计模式

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 引用
    • 虚拟代理:存放实体化需要很久的对象,比如网页先看到文字,后看到图片
    • 安全代理:控制真实对象的访问权限
    • 智能指引:调用真实对象时,处理另外一些事,比如计算真实对象的引用次数