前期准备
选定参考书及参考链接:
《大话设计模式》
https://github.com/wklken/py-patterns
https://github.com/faif/python-patterns
前置知识复习
理解新知识是在已掌握知识的基础上进行的,如果在学习新知识的途中遇见了不清楚的小知识,一定要立马去弄懂这个小知识,再去学习新知识。
1. 面向对象及UML(主要复习类图)
普通类、抽象类、接口:
接口和抽象类都是继承树的上层,都是普通类上层的抽象层,只是定义了一些方法,用于描述类的功能。
接口和抽象类相同点:不能被实例化、都包含抽象方法,这些抽象方法用于描述类具备的功能。
接口和抽象类不同点:在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象类的优势;接口中只能有抽象的方法;一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接口。(注:Java语言是但继承、Python语言是多继承)
类之间的关系分为:泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)
各种关系的强弱顺序: 泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
泛化:是一种继承关系,表示一般与特殊的关系,它指定了子类如何继承父类的所有特征和行为。
实现:是一种类与接口的关系,表示类是接口所有特征和行为的实现。
关联:是一种拥有的关系,它使一个类知道另一个类的属性和方法。(可以是单向、也可以是双向的)
聚合:是整体与部分的关系,且部分可以离开整体而单独存在。聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
组合:是整体与部分的关系,但部分不能离开整体而单独存在。组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。
依赖:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.
注:关联、聚合、组合均可看为关联,其代码表现为成员变量;依赖的代码表现为局部变量(定义在方法中的变量)、方法的参数或者对静态方法的调用
2. python语言的各种表示
python面向对象:https://www.runoob.com/python3/python3-class.html
修饰器函数的作用:https://blog.csdn.net/qq_42708830/article/details/96902090
总结:各个模式的简单印象
一、简单工厂模式(创建型)
意图:由一个工厂对象决定创建出哪一种产品类的实例。
类图:
代码示例:
class Operation(object):
@property
def number_a(self):
return self.__number_a
@number_a.setter
def number_a(self, value):
self.__number_a = value
@property
def number_b(self):
return self.__number_b
@number_b.setter
def number_b(self, value):
self.__number_b = value
class OperationAdd(Operation):
def get_result(self):
return self.number_a + self.number_b
class OperationSub(Operation):
def get_result(self):
return self.number_a - self.number_b
class OperationFactory(object):
@staticmethod
def create_operation(operate):
if operate == "+":
return OperationAdd()
elif operate == "-":
return OperationSub()
if __name__ == '__main__':
op = OperationFactory.create_operation('-')
op.number_a = 10
op.number_b = 5
print(op.get_result())
注:
适用环境:
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
用例:计算器中的各种运算;各种支付方法;
二、策略模式(行为型)
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Strategy(metaclass=ABCMeta):
"""
策略类, 定义所有支持的算法的公共接口
"""
@abstractmethod
def calculate(self):
pass
class StrategyA(Strategy):
"""
具体策略类, 封装了具体的算法和行为
"""
def calculate(self):
print("calculate A")
class StrategyB(Strategy):
"""
具体策略类, 封装了具体的算法和行为
"""
def calculate(self):
print("calculate B")
class Context(object):
"""
上下文, 维护一个对strategy对象的引用
"""
def __init__(self, strategy):
self.__strategy = strategy
def do_strategy(self):
self.__strategy.calculate()
if __name__ == '__main__':
strategy = "B"
if strategy == "A":
c = Context(StrategyA())
elif strategy == "B":
c = Context(StrategyB())
else:
pass
c.do_strategy()
注:
优缺点:
- 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
- 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
用例:诸葛亮的锦囊妙计,每一个锦囊就是一个策略;旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略;
三、装饰模式(结构型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Component(metaclass=ABCMeta):
"""
定义一个对象接口
可以给这些对象动态地增加职责
"""
@abstractmethod
def operation(self):
pass
class ConcreteComponent(Component):
"""
定义了一个具体对象, 也可以给这个对象增加职责
"""
def operation(self):
print("Hello world")
class Decorator(Component):
"""
装饰抽象类, 继承了component, 从外类来扩展component类的功能, 但对于component来说, 是无须知道decorator类的存在的
"""
def __init__(self, component):
self.__component = component
def operation(self):
if self.__component:
self.__component.operation()
class DecoratorA(Decorator):
"""
具体装饰对象, 给component添加职责
"""
def operation(self):
print("<h1>")
super(DecoratorA, self).operation()
print("</h1>")
class DecoratorB(Decorator):
def operation(self):
print("<strong>")
super(DecoratorB, self).operation()
print("</strong>")
if __name__ == '__main__':
c = ConcreteComponent()
d1 = DecoratorA(c)
d2 = DecoratorB(d1)
d2.operation()
注:
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
四、代理模式(结构型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Subject(metaclass=ABCMeta):
"""
定义了RealSubject和Proxy的共用接口
"""
@abstractmethod
def request(self):
pass
class RealSubject(Subject):
"""
定义了真正的实体
"""
def request(self):
print("hello")
class Proxy(Subject):
"""
保存一个引用使得代理可以访问实体并提供一个与subject的接口相同的接口, 这样代理就可以用来替代实体
"""
def __init__(self):
self.__realsubject = RealSubject()
def request(self):
self.__realsubject.request()
if __name__ == '__main__':
proxy = Proxy()
proxy.request()
注:
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂
五、工厂方法模式(创建型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Product(metaclass=ABCMeta):
"""
定义工厂方法所创建的对象接口
"""
@abstractmethod
def echo(self):
pass
class ConcreteProductA(Product):
"""
具体的产品, 实现了product的接口
"""
def echo(self):
print(self.__class__.__name__)
class ConcreteProductB(Product):
"""
具体的产品, 实现了product的接口
"""
def echo(self):
print(self.__class__.__name__)
class Creator(object):
"""
声明了工厂方法, 该方法返回一个Product类型的对象
"""
@abstractmethod
def create(self):
pass
class ConcreteCreatorA(Creator):
"""
重定义, 返回一个ConcreteProduct实例
"""
def create(self):
return ConcreteProductA()
class ConcreteCreatorB(Creator):
def create(self):
return ConcreteProductB()
if __name__ == '__main__':
factory_a = ConcreteCreatorA()
product = factory_a.create()
product.echo()
factory_b = ConcreteCreatorB()
product = factory_b.create()
product.echo()
六、原型模式(创建型)
类图:
代码示例:
import copy
from abc import ABCMeta, abstractmethod
class Prototype(metaclass=ABCMeta):
"""
原型类, 声明一个克隆自身的接口
"""
def __init__(self, id):
self.__id = id
@property
def id(self):
return self.__id
@id.setter
def id(self, value):
self.__id = value
@abstractmethod
def clone(self):
pass
class ConcretePrototypeA(Prototype):
"""
具体原型类, 实现一个克隆自身的操作
"""
def clone(self):
# 浅拷贝, 注意
return copy.copy(self)
class ConcretePrototypeB(Prototype):
"""
具体原型类, 实现一个克隆自身的操作
"""
def clone(self):
return copy.copy(self)
class Manager(object):
def __init__(self):
self._dict = {}
def register(self, name, prototype):
self._dict[name] = prototype
def create(self, proto_name):
p = self._dict.get(proto_name)
return p.clone()
if __name__ == '__main__':
ca = ConcretePrototypeA(1)
c2 = ca.clone()
print(c2.id)
# with manager
cb = ConcretePrototypeB(2)
m = Manager()
m.register("ca", ca)
m.register("cb", cb)
x = m.create("cb")
print(x.id)
x = m.create("ca")
print(x.id)
注:
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
优点: 1、性能提高。 2、逃避构造函数的约束。
缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
七、模板方法模式(行为型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class AbstractClass(metaclass=ABCMeta):
"""
实现了一个模板方法, 定义了算法的挂架
"""
def algorithm_frame(self):
print("begin...")
self.operation1()
self.operation2()
print("end...")
@abstractmethod
def operation1(self):
pass
@abstractmethod
def operation2(self):
pass
class ConcreteClass(AbstractClass):
"""
实现了特定的步骤
"""
def operation1(self):
print("hello")
def operation2(self):
print("world")
if __name__ == '__main__':
c = ConcreteClass()
c.algorithm_frame()
注:
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
八、外观模式(结构型)
类图:
代码示例:
class SystemA(object):
def call_a(self):
print("call a")
class SystemB(object):
def call_b(self):
print("call b")
class SystemC(object):
def call_c(self):
print("call c")
class Facade(object):
def __init__(self):
self.sys_a = SystemA()
self.sys_b = SystemB()
self.sys_c = SystemC()
def action_a(self):
self.sys_a.call_a()
self.sys_b.call_b()
def action_b(self):
self.sys_b.call_b()
self.sys_c.call_c()
if __name__ == '__main__':
facade = Facade()
facade.action_a()
注:
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
九、建造者模式(创建型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Product(object):
"""
具体产品
"""
def __init__(self):
self.__parts = []
def add(self, part):
self.__parts.append(part)
def show(self):
print('-'.join(item for item in self.__parts))
class Builder(metaclass=ABCMeta):
"""
为创建一个product对象的各个部件指定的抽象接口
"""
@abstractmethod
def build_part_1(self):
pass
@abstractmethod
def build_part_2(self):
pass
@abstractmethod
def get_result(self):
pass
class BuilderA(Builder):
"""
具体建造者A
"""
def __init__(self):
self.__product = Product()
def build_part_1(self):
self.__product.add("partA1")
def build_part_2(self):
self.__product.add("partA2")
def get_result(self):
return self.__product
class BuilderB(Builder):
"""
具体建造者B
"""
def __init__(self):
self.__product = Product()
def build_part_1(self):
self.__product.add("partB1")
def build_part_2(self):
self.__product.add("partB2")
def get_result(self):
return self.__product
class Director(object):
"""
director不知道自己用的是哪个, 即: 只有不知道, 才能随时替换
"""
@staticmethod
def construct(builder):
builder.build_part_1()
builder.build_part_2()
if __name__ == '__main__':
ba = BuilderA()
bb = BuilderB()
Director.construct(ba)
product = ba.get_result()
product.show()
Director.construct(bb)
product = bb.get_result()
product.show()
注:
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
十、观察者模式(行为型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class Subject(object):
"""
把所有对观察者对象的引用保存在一个聚集里
每个主题都可以有任意数量的观察者.
抽象主题提供了接口增加/删除观察者对象
"""
def __init__(self):
self.__observers = []
def attach(self, observer):
self.__observers.append(observer)
def detach(self, observer):
self.__observers.remove(observer)
def notify(self):
for o in self.__observers:
o.update()
class ConcreteSubject(Subject):
"""
具体主题, 将有关状态存入具体的观察者对象
在具体主题的内部状态改变时, 给所有登记过的观察者发出通知
"""
@property
def status(self):
return self.__status
@status.setter
def status(self, value):
self.__status = value
class Observer(metaclass=ABCMeta):
"""
抽象观察者, 位所有具体观察者定义接口, 在得到主题的通知时更新自己
"""
@abstractmethod
def update(self):
pass
class ConcreteObserver(Observer):
"""
具体观察者, 实现抽象观察者锁要求的更新接口
以使本身的状态与主题的状态相协调
"""
def __init__(self, subject, name):
self.subject = subject
self.name = name
self.objserver_staus = None
def update(self):
self.objserver_staus = self.subject.status
print("the observer: %s status change to %s" % (self.name , self.objserver_staus))
if __name__ == '__main__':
s = ConcreteSubject()
x = ConcreteObserver(s, "X")
y = ConcreteObserver(s, "Y")
z = ConcreteObserver(s, "z")
s.attach(x)
s.attach(y)
s.attach(y)
s.status = "A"
s.notify()
s.status = "B"
s.detach(y)
s.notify()
注:
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
十一、抽象工厂模式(创建型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class AbstractProductA(object):
"""
抽象产品, 可能拥有多种实现
"""
def __init__(self, name):
self.name = name
def __str__(self):
return "ProductA: %s" % self.name
class ConcreteProductA1(AbstractProductA):
pass
class ConcreteProductA2(AbstractProductA):
pass
class AbstractProductB(object):
"""
抽象产品, 可能拥有多种实现
"""
def __init__(self, name):
self.name = name
def __str__(self):
return "ProductB: %s" % self.name
class ConcreteProductB1(AbstractProductB):
pass
class ConcreteProductB2(AbstractProductB):
pass
class AbstractFactory(metaclass=ABCMeta):
"""
抽象工厂接口, 包含所有产品创建的抽象方法
"""
@abstractmethod
def create_product_a(self):
pass
@abstractmethod
def create_product_b(self):
pass
class ConcreteFactory1(AbstractFactory):
"""
具体工厂, 创建具有特定实现的产品对象
"""
def create_product_a(self):
return ConcreteProductA1("PA1")
def create_product_b(self):
return ConcreteProductB1("PB1")
class ConcreteFactory2(AbstractFactory):
def create_product_a(self):
return ConcreteProductA2("PA2")
def create_product_b(self):
return ConcreteProductB2("PB2")
if __name__ == '__main__':
factory = ConcreteFactory2()
product_a = factory.create_product_a()
print(product_a)
注:
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
十二、状态模式(行为型)
类图:
代码示例:
from abc import ABCMeta, abstractmethod
class State(metaclass=ABCMeta):
"""
抽象状态类, 定义一个接口以封装与Context的一个特定状态相关的行为
"""
@abstractmethod
def handle(self, context):
pass
class ConcreteStateA(State):
def handle(self, context):
"""
设定下一个状态是
"""
print("此处执行状态对应的操作")
context.state = ConcreteStateB()
class ConcreteStateB(State):
def handle(self, context):
"""
设定下一个状态是
"""
print("此处执行状态对应的操作")
context.state = ConcreteStateA()
class Context(object):
"""
维护一个ConcreteState子类的实例, 这个实例定义当前的状态
"""
def __init__(self, state):
self.__state = state
@property
def state(self):
return self.__state
@state.setter
def state(self, value):
print("current status:", value.__class__.__name__)
self.__state = value
def request(self):
self.__state.handle(self)
if __name__ == '__main__':
c = Context(ConcreteStateA())
c.request()
c.request()
c.request()
c.request()
注:
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
十三、适配器模式
类图:
代码示例:
# 对象适配器模式
from abc import ABCMeta, abstractmethod
class Adaptee(object):
"""
需要适配的类, 需要被被适配的
"""
def special_request(self):
print("I am special")
class Target(metaclass=ABCMeta):
"""
客户锁期待的接口, 目标可以使具体或抽象的类, 也可以是接口
"""
@abstractmethod
def request(self):
pass
class Adapter(Target):
"""
适配器, 通过在内部包装一个adapter对象, 把源接口转换成目标接口
"""
def __init__(self):
self.adaptee = Adaptee()
def request(self):
self.adaptee.special_request()
if __name__ == '__main__':
a = Adapter()
a.request()
# 类适配器模式
from abc import ABCMeta, abstractmethod
class Adaptee(object):
"""
需要适配的类, 需要被被适配的
"""
def special_request(self):
print("I am special")
class Target(metaclass=ABCMeta):
"""
客户锁期待的接口, 目标可以使具体或抽象的类, 也可以是接口
"""
@abstractmethod
def request(self):
pass
class Adapter(Adaptee):
"""
适配器类
"""
def request(self):
super(Adapter, self).special_request()
class ConcreteAdapter(Target):
"""
普通类
"""
def request(self):
print("I am a common")
if __name__ == '__main__':
c = ConcreteAdapter()
c.request()
a = Adapter()
a.request()
注:
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
十四、备忘录模式
类图:
代码示例:
注: