设计模式及创建型模式-python版
1 架构模式与设计模式
架构模式搞层次的设计模式, 描述系统整体结构和组织方式,设计模式是针对某个问题的解决方案,是一种解决问题的思路。
2 设计模式的分类
2.1 创建型模式
单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式
2.2 结构型模式
适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
2.3 行为型模式
策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式
3 设计模式的六大原则
- 单一职责原则:一个类只负责一个功能领域中的相应职责
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:任何基类可以出现的地方,子类一定可以出现
- 依赖倒转原则:针对接口编程,依赖于抽象而不依赖于具体
- 接口隔离原则:使用多个隔离的接口,比使用单个接口要好
- 迪米特法则:一个实体应当尽量少的与其他实体发生相互作用
- 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承
4 设计模式举例
4.1 单例模式
单例模式(Singleton)用在软件运行过程中仅需要一个实例的场景,如软件的主界面, 系统全局统一管理(win 下的task manager)等。
- 举例
用单例模式来模拟计算机的总线地址, 处理器中的多个线程可以调用总线地址(单例)来发送消息。
import threading
import time
class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kw)
return cls._instance
class Bus(Singleton):
lock = threading.RLock()
def sendData(self, data):
self.lock.acquire()
time.sleep(3)
print("Sending Signal Data...", data)
self.lock.release()
class VisitEntity(threading.Thread):
my_bus = ""
name = ""
def getName(self):
return self.name
def setName(self, name):
self.name = name
def run(self):
self.my_bus = Bus()
self.my_bus.sendData(self.name)
if __name__ == "__main__":
for i in range(3):
print("Entity %d begin to run..." % i)
my_entity = VisitEntity()
my_entity.setName("Entity_"+str(i))
my_entity.start()
上述代码中 Bus 继承了Singleton, 每创建一个Bus 对象, 实际上返回的都是同一个对象。
- 优缺点
单例模式要求在全局只有一个实例, 可以节省内存空间;全局只有一个接入点, 可以更高的进行数据控制, 避免过多的占用;单例可以常驻内存, 减少系统开销。
单例模式的扩展是比较困难的;赋予单例太多的职责, 某种程度上违反单一职责原则;单例模式在某种情况下会导致资源瓶颈
4.2 工厂方法模式
工厂方法模式(Factory Method Pattern)是在简单工厂模式(定义一个工厂类,根据传入的参数决定创建哪一种产品类的实例)基础上的递进。它通过定义一个创建对象的接口, 由子类决定实例化哪一个类。工厂方法模式使一个类的实例化延迟到其子类, 简言之, 就是父类提供一个接口, 子类来决定实例化哪个具体的类。
- 举例
from abc import ABC, abstractmethod
# 定义Shape接口
class Shape(ABC):
@abstractmethod
def draw(self):
pass
# 实现Circle类
class Circle(Shape):
def draw(self):
print("Drawing a Circle")
# 实现Square类
class Square(Shape):
def draw(self):
print("Drawing a Square")
# 定义ShapeFactory接口
class ShapeFactory(ABC):
@abstractmethod
def create_shape(self):
pass
# 实现CircleFactory类
class CircleFactory(ShapeFactory):
def create_shape(self):
return Circle()
# 实现SquareFactory类
class SquareFactory(ShapeFactory):
def create_shape(self):
return Square()
# 使用示例
if __name__ == "__main__":
circle_factory = CircleFactory()
shape1 = circle_factory.create_shape()
shape1.draw() # 输出: Drawing a Circle
square_factory = SquareFactory()
shape2 = square_factory.create_shape()
shape2.draw() # 输出: Drawing a Square
上述例子定义了形状类产品的接口及其两个具体的实现类, 工厂接口及其两个具体的实现, 每个工厂负责生产一种形状产品。
- 优缺点
解耦:将对象的创建过程与使用过程分离,降低了代码的耦合度;
灵活性:通过子类来决定具体实例化哪个类,增加了代码的灵活性;
扩展性:增加新的产品类时,只需添加相应的工厂类即可,不需要修改现有代码。
类的数量增加:每增加一个产品类,都需要增加一个相应的工厂类,导致类的数量增多;
代码复杂度提高:增加了系统的复杂性,理解起来可能会有些困难。
4.3 抽象工厂模式
在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
抽象工厂模式(Abstract Factory)与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
- 举例
from abc import ABCMeta, abstractmethod
class Refrigerator(metaclass=ABCMeta):
@abstractmethod
def use(self):
pass
class Refrigerator1(Refrigerator):
def use(self):
print("Haier refrigerator")
class Refrigerator2(Refrigerator):
def use(self):
print("Meidi refrigerator")
class Airconditioner(metaclass=ABCMeta):
@abstractmethod
def go(self):
pass
class Airconditioner1(Airconditioner):
def go(self):
print("Haier Airconditioner")
class Airconditioner2(Airconditioner):
def go(self):
print("Meidi Airconditioner")
class Factory(metaclass=ABCMeta):
@abstractmethod
def create_refrigerator(self):
pass
@abstractmethod
def create_airconditioner(self):
pass
class Factory1(Factory):
def create_refrigerator(self):
return Refrigerator1()
def create_airconditioner(self):
return Airconditioner1()
class Factory2(Factory):
def create_refrigerator(self):
return Refrigerator2()
def create_airconditioner(self):
return Airconditioner2()
if __name__ == '__main__':
f = Factory1()
r1 = f.create_refrigerator()
r1.use()
r2 = f.create_airconditioner()
r2.go()
f = Factory2()
r1 = f.create_refrigerator()
r1.use()
r2 = f.create_airconditioner()
r2.go()
上述例子定义了两个系列的产品:冰箱和空调, 每种产品又要区分不同的品牌。 定义了两个工厂实现类,分别负责生产不同品牌的产品。
- 优缺点
优点
“抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。”
“当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。”
“增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。”
缺点
“在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。”
“开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。”
4.4 建造者模式
建造者模式(Build)是把复杂对象的创建拆分成多个简单的步骤, 并将这些步骤封装在一个独立的建造者类中, 然后,我们可以使用一个指挥者类来控制建造者的调用顺序,以便在每个步骤完成后正确地构建复杂对象。
将对象的构建与其表示分离开来。这意味着我们可以使用相同的构建过程来创建不同的表示形式,而不必改变建造者的代码。
建造者模式是一种创建型设计模式,它允许您逐步构造复杂对象。与直接实例化复杂对象不同,建造者模式将对象的构造过程分解为一系列步骤,使您可以逐个处理这些步骤。
建造者模式的核心思想是将一个复杂对象的构造过程和它的表示分离开来,使得同样的构造过程可以创建不同的表示。通常情况下,一个建造者模式包括以下四个角色:
Product(产品):表示被建造的复杂对象,包含多个部件。
Builder(抽象建造者):定义构造过程的接口,包括创建各个部件的抽象方法。
ConcreteBuilder(具体建造者):实现 Builder 接口,构造和装配各个部件,同时提供一个返回产品的方法。
Director(指挥者):负责安排各个部件的构造顺序,告诉 Builder 需要创建哪些部件,以及如何组装它们。
- 举例
from abc import ABC, abstractmethod
class Product:
def __init__(self):
self.part_a = None
self.part_b = None
self.part_c = None
class Builder(ABC):
def __init__(self):
self.product = Product()
@abstractmethod
def build_part_a(self):
pass
@abstractmethod
def build_part_b(self):
pass
@abstractmethod
def build_part_c(self):
pass
class ConcreteBuilder1(Builder):
def build_part_a(self):
self.product.part_a = "Part A1"
def build_part_b(self):
self.product.part_b = "Part B1"
def build_part_c(self):
self.product.part_c = "Part C1"
class ConcreteBuilder2(Builder):
def build_part_a(self):
self.product.part_a = "Part A2"
def build_part_b(self):
self.product.part_b = "Part B2"
def build_part_c(self):
self.product.part_c = "Part C2"
class Director:
def __init__(self, builder):
self.builder = builder
def construct(self):
self.builder.build_part_a()
self.builder.build_part_b()
self.builder.build_part_c()
return self.builder.product
# Usage example
builder1 = ConcreteBuilder1()
director = Director(builder1)
product1 = director.construct()
print(product1.part_a) # Output: Part A1
print(product1.part_b) # Output: Part B1
print(product1.part_c) # Output: Part C1
builder2 = ConcreteBuilder2()
director = Director(builder2)
product2 = director.construct()
print(product2.part_a) # Output: Part A2
print(product2.part_b) # Output: Part B2
print(product2.part_c) # Output: Part C2
上述代码定义了一个产品类, 分成三部分。 定义了一个抽象的建造者类, 定义了抽象的方法分别来建造产品的三部分, 而后实现了两个具体的构造者, 最后定一个Director 类来调用构造者。
- 优缺点
优点:
将复杂对象的构造过程和表示分离。这使得客户端可以更加简单地创建对象,同时也使得代码更加灵活和易于维护。
可以使用相同的构造过程来创建不同的对象表示。因为建造者模式将构造过程和表示分离,所以您可以使用相同的构造过程来创建不同的对象表示。这样可以大大减少代码的重复,并且使得对象表示更加易于扩展。
支持逐步构建复杂对象。建造者模式允许您逐步构建复杂对象,因为您可以分解对象构造过程为一系列简单的步骤,并逐步完成每个步骤。这样可以使得构造过程更加可控,同时也有助于代码的维护和测试。
代码结构清晰。建造者模式的代码结构清晰,并且易于理解和扩展。因为建造者模式将构造过程分解为一系列步骤,并将其封装在不同的类中,所以可以更加清晰地组织代码。
缺点
建造者模式的实现较为复杂。由于建造者模式将对象的构造过程分解为多个步骤,所以需要创建多个类来实现建造者模式。这会增加代码量和复杂度,使得代码变得更难理解和维护。
建造者模式要求建造者类必须知道产品的内部结构。由于建造者类需要创建产品的各个部分并组装它们,所以必须了解产品的内部结构。这种紧密耦合的设计使得建造者模式的扩展性和灵活性变得较差。
建造者模式不适用于创建简单的对象。由于建造者模式将对象构造过程分解为多个步骤,所以如果要创建简单的对象,建造者模式会增加代码的复杂度,使得代码变得更加冗长和难以理解。
建造者模式可能会导致对象数量增加。由于建造者模式创建的对象是由多个部件组成的,所以可能会导致对象的数量增加,从而占用更多的内存空间。
4.5 原型模式
原型模式(Prototype)是一种创建型设计模式,它的主要思想是通过复制一个已经存在的对象来创建新的对象。这个已经存在的对象被称为原型对象,新的对象通过复制原型对象的属性和状态来创建。
在原型模式中,我们需要定义一个原型接口或抽象类,这个接口或抽象类中包含了一个 clone() 方法,用于复制原型对象。具体的原型对象实现这个接口或抽象类,并实现 clone() 方法。当需要创建新的对象时,我们可以通过调用原型对象的 clone() 方法来复制它,从而创建一个新的对象。
- 举例
import copy
from abc import ABC, abstractmethod
# 定义原型类
class Prototype(ABC):
@abstractmethod
def clone(self):
pass
# 定义具体原型类
class ConcretePrototype(Prototype):
def __init__(self, name):
self.name = name
def clone(self):
return copy.deepcopy(self)
def __str__(self):
return self.name
# 定义客户端类
class Client:
def operation(self):
# 创建原型对象
prototype = ConcretePrototype("prototype")
# 克隆原型对象
clone1 = prototype.clone()
clone2 = prototype.clone()
# 输出克隆对象的名称
print("Clone 1 name: " + str(clone1))
print("Clone 2 name: " + str(clone2))
# 测试代码
client = Client()
client.operation()
- 优缺点
优点:
减少对象的创建时间和消耗的资源:原型模式通过复制已有对象来创建新对象,避免了重复创建对象的过程,因此可以大幅度减少对象的创建时间和消耗的资源。
可以避免因重复创建对象而造成的资源浪费:由于原型模式可以通过复制已有对象来创建新对象,因此避免了重复创建对象而造成的资源浪费。
动态地创建对象:原型模式可以动态地创建对象,因为它不需要预先知道要创建的对象的具体类型。
可以保持对象的一致性:由于原型对象与新对象拥有相同的属性和状态,因此可以保持对象的一致性。
可以提高系统性能:由于原型模式减少了对象的创建时间和消耗的资源,因此可以提高系统性能。
缺点:
原型对象的创建过程可能比较复杂:由于原型对象需要包含所有要复制的属性和状态,因此原型对象的创建过程可能比较复杂,尤其是当属性和状态是深层嵌套的时候。
使用场景比较有限:原型模式只有当需要创建的对象可以通过复制现有对象来创建时才适用,因此它的使用场景比较有限。
可能会增加代码的复杂度:由于原型模式需要维护原型对象和具体的原型实现类,因此可能会增加代码的复杂度。
克隆方法的实现可能比较困难:实现克隆方法需要考虑对象的深浅拷贝问题,如果对象的属性和状态比较复杂,那么实现克隆方法可能比较困难。