什么是设计模式
设计模式是针对面向对象的,所谓的设计就是在编写程序之前对其有一个基础的架构,如需要创建哪些类,定义那些变量,有哪些方法。每一个设计模式系统的命名、解释和评价了面向对象系统中一个重要和重复出现的设计。设计模式四个基本要素:模式名称、问题、解决方案、效果。
一些需要的知识点
面向对象的三大特性:
-
封装:封装指的是两方面 , 一方面把相关的功能归纳到一个类中, 另一方面把数据封装到对象中。
-
继承:继承就是为了提升代码的复用性,更加灵活。
-
多态:多态指的是一个变量可以有多种状态或者多种形态。
接口:
-
本质是一个抽象类,要求继承接口的类,必须实现接口内定义的一些方法。
-
抽象类就是不能实例化的类。
-
如果继承抽象类后一样没有实现抽象方法,那么它也是一个抽象类。
-
作用:限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。
#在python中没有严谨的限制函数签名,只限制了函数名。 from abc import abstractmethod, ABCMeta class Interface(metaclass=ABCMeta): @abstractmethod def method(self, arg): pass
设计模式六大原则:
-
开闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
-
里氏(Liskov)替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
-
能够进行统一的调用,函数签名必须一致。
-
-
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。
-
A,B同时调用C模块,C模块进行修改,那么AB模块可能也需要修改,所以接口就是哪些东西不能改的限制,先有接口后又代码。
-
-
接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
-
如果不实现不需要的方法,那么就无法实例化,可以使用多继承来继承多个小基类。
-
-
迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。
-
解耦,让依赖越少,那么自由度越高。
-
-
单一职责原则:不要存在多于一个导致类变更的原因。
-
通俗的说,即一个类只负责一项职责。
-
设计模式分类
创建型模式:工厂方法模式,抽象工厂模式,创建者模式,原型模式,单例模式。结构型模式:适配器模式,桥模式,组合模式,装饰模式,外观模式,享元模式,代理模式。行为型模式:解释器模式,责任链模式,命令模式,迭代器模式,中介者模式,备忘录模式,观察者模式,状态模式,策略模式,模板方法模式。
创建型模式
创建型模式的重点在于如何创建一个对象。
简单工厂模式
简单工厂模式:
-
不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。
-
角色:工厂角色,抽象产品角色,具体产品角色。
-
优点:隐藏了对象创建的实现细节,客户端不需要修改代码
-
缺点:
-
违反了单一职责原则,将创建逻辑集中到一个工厂类里。
-
当添加新产品时,需要修改工厂类代码,违反了开闭原则
-
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): raise NotImplementedError class Alipay(Payment): def __init__(self, use_huabei=False): self.use_huabei = use_huabei def pay(self, money): if self.use_huabei: print("花呗支付%s元" % money) else: print("支付宝支付%s元" % money) class ApplePay(Payment): def __init__(self, s): pass def pay(self, money): print("苹果支付%s元" % money) class PaymentFactory: def create_payment(self, method): if method == "alipay": return Alipay() elif method == "applepay": return ApplePay() elif method == 'huabei': return Alipay(use_huabei=True) else: raise NameError(method) pf = PaymentFactory() p = pf.create_payment('huabei') p.pay(100)
工厂方法模式:
-
定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。
-
角色:抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色。
-
工厂方法模式拥有继承抽象工厂类的抽象工厂类。
-
使用场景:需要生产多种大量的对象的时候;需要降低耦合度的时候;当系统的产品种类需要扩展的时候。
-
优点: 每个具体产品对应一个具体工厂类,隐藏了对象创建的细节。
-
缺点:每增加一个具体产品类,就要添加一个具体工厂类。
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): raise NotImplementedError class Alipay(Payment): def __init__(self, use_huabei=False): self.use_huabei = use_huabei def pay(self, money): if self.use_huabei: print("花呗支付%s元" % money) else: print("支付宝支付%s元" % money) class ApplePay(Payment): def pay(self, money): print("苹果支付%s元"%money) class PaymentFactory(metaclass=ABCMeta): @abstractmethod def create_payment(self): pass class AlipayFactory(PaymentFactory): def create_payment(self): return Alipay(use_huabei=False) class ApplePayFactory(PaymentFactory): def create_payment(self): return ApplePay() class HuabeiFactory(PaymentFactory): def create_payment(self): return Alipay(use_huabei=True)
抽象工厂模式:
-
多个对象需要一起使用的时候使用,多个小对象组合成一个大对象。如面向对象中实现代码复用的组合。
-
角色:抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色,客户端。
-
抽象工厂模式中每个具体工厂都生产一套产品。
-
适用场景:系统要独立于产品的创建与组合时;强调一系列相关的产品对象的设计以便进行联合使用时;提供一个产品类库,想隐藏产品的具体实现时。
-
优点:将客户端与类的具体实现相分离;每个工厂创建了一个完整的产品系列,使得易于交换产品系列;有利于产品的一致性(即产品之间的约束关系)。
-
缺点:难以支持新种类的(抽象)产品
from abc import abstractmethod, ABCMeta # ------抽象产品------ class PhoneShell(metaclass=ABCMeta): @abstractmethod def show_shell(self): pass class CPU(metaclass=ABCMeta): @abstractmethod def show_cpu(self): pass class OS(metaclass=ABCMeta): @abstractmethod def show_os(self): pass # ------抽象工厂------ class PhoneFactory(metaclass=ABCMeta): @abstractmethod def make_shell(self): pass @abstractmethod def make_cpu(self): pass @abstractmethod def make_os(self): pass # ------具体产品------ class SmallShell(PhoneShell): def show_shell(self): print("普通手机小手机壳") class BigShell(PhoneShell): def show_shell(self): print("普通手机大手机壳") class AppleShell(PhoneShell): def show_shell(self): print("苹果手机壳") class SnapDragonCPU(CPU): def show_cpu(self): print("骁龙CPU") class MediaTekCPU(CPU): def show_cpu(self): print("联发科CPU") class AppleCPU(CPU): def show_cpu(self): print("苹果CPU") class Android(OS): def show_os(self): print("Android系统") class IOS(OS): def show_os(self): print("iOS系统") # ------具体工厂------ class MiFactory(PhoneFactory): def make_cpu(self): return SnapDragonCPU() def make_os(self): return Android() def make_shell(self): return BigShell() class HuaweiFactory(PhoneFactory): def make_cpu(self): return MediaTekCPU() def make_os(self): return Android() def make_shell(self): return SmallShell() class IPhoneFactory(PhoneFactory): def make_cpu(self): return AppleCPU() def make_os(self): return IOS() def make_shell(self): return AppleShell() # ------客户端------ class Phone: def __init__(self, cpu, os, shell): self.cpu = cpu self.os = os self.shell = shell def show_info(self): print("手机信息:") self.cpu.show_cpu() self.os.show_os() self.shell.show_shell() def make_phone(factory): cpu = factory.make_cpu() os = factory.make_os() shell = factory.make_shell() return Phone(cpu, os, shell) p1 = make_phone(IPhoneFactory()) p1.show_info()
将一个复杂的对象的构建和它的表示分离,使同样的构建过程可以创建不同的表示。
-
角色:抽象建造者,具体建造者,指挥者,产品。
-
建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。
-
适用场景:
-
当创建复杂对象的算法(Director)应该独立于该对象的组成部分以及它们的装配方式(Builder)时。
-
当构造过程允许被构造的对象有不同的表示时(不同Builder)。
-
优点:隐藏了一个产品的内部结构和装配过程;将构造代码与表示代码分开;可以对构造过程进行更精细的控制。
from abc import abstractmethod, ABCMeta #------产品------ class Player: def __init__(self, face=None, body=None, arm=None, leg=None): self.face = face self.arm = arm self.leg = leg self.body = body def __str__(self): return "%s, %s, %s, %s" % (self.face, self.arm, self.body, self.leg) #------建造者------ class PlayerBuilder(metaclass=ABCMeta): @abstractmethod def build_face(self): pass @abstractmethod def build_arm(self): pass @abstractmethod def build_leg(self): pass @abstractmethod def build_body(self): pass @abstractmethod def get_player(self): pass class BeautifulWomanBuilder(PlayerBuilder): def __init__(self): self.player = Player() def build_face(self): self.player.face = "漂亮脸蛋" def build_arm(self): self.player.arm="细胳膊" def build_body(self): self.player.body="细腰" def build_leg(self): self.player.leg="长腿" def get_player(self): return self.player #无所谓beautfulbuilder编写的顺序 class PlayerDirector: def build_player(self, builder): builder.build_body() builder.build_face() builder.build_arm() builder.build_leg() return builder.get_player() pd = PlayerDirector() pb = BeautifulWomanBuilder() p = pd.build_player(pb) print(p)
单例模式
保证一个类只有一个实例对象,并提供一个访问它的全局访问点,日志对象就是单例,数据库连接对象也是单例的。
-
适用场景:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
-
优点:对唯一实例的受控访问;单例相当于全局变量,但防止命名空间污染。
单例模式
保证一个类只有一个实例对象,并提供一个访问它的全局访问点,日志对象就是单例,数据库连接对象也是单例的。适用场景:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。优点:对唯一实例的受控访问;单例相当于全局变量,但防止命名空间污染。
from abc import abstractmethod, ABCMeta class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): cls._instance = super(Singleton, cls).__new__(cls) return cls._instance class MyClass(Singleton): def __init__(self, name=None): if name is not None: self.name = name a = MyClass("a") print(a) print(a.name) b = MyClass("b") print(b) print(b.name) print(a) print(a.name)
结构型模式
适配器模式
当两个类不能一起使用,那么增加一些方法使它们能够一起使用,这就叫适配器模式。类适配器处理多个待适配类时,十分麻烦,所以通过组合来写适配器,也就是对象适配器,会更好。
-
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-
角色:目标接口,待适配的类,适配器。
-
实现方式:类适配器,对象适配器。
-
适用场景:想使用一个已经存在的类,而它的接口不符合你的要求;(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。)
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): raise NotImplementedError class Alipay(Payment): def pay(self, money): print("支付宝支付%s元"%money) class ApplePay(Payment): def pay(self, money): print("苹果支付%s元"%money) #------待适配类------ class WechatPay: def huaqian(self, money): print("微信支付%s元"%money) class BankPay: def huaqian(self, money): print("银行卡支付%s元" % money) # # 类适配器 # class NewWechatPay(WechatPay, Payment): # def pay(self, money): # self.huaqian(money) # 对象适配器 class PaymentAdapter(Payment): def __init__(self, p): self.payment = p def pay(self, money): self.payment.huaqian(100) p = PaymentAdapter(BankPay()) p.pay(100)
代理模式
为其他对象提供一种代理以控制对这个对象的访问。角色:抽象实体,实体,代理。使用场景:
-
远程代理:为远程的对象提供代理。
-
虚代理:根据需要创建很大的对象。
-
保护代理:控制对原始对象的访问,用于对象有不同访问权限时。优点:
-
远程代理:可以隐藏对象位于远程地址空间的事实。
-
虚代理:可以进行优化,例如根据要求创建对象。
-
保护代理:允许在访问一个对象时有一些附加的内务处理。
from abc import ABCMeta, abstractmethod class Subject(metaclass=ABCMeta): @abstractmethod def get_content(self): pass class RealSubject(Subject): def __init__(self, filename): print("读取%s文件内容"%filename) f = open(filename) self.content = f.read() f.close() def get_content(self): return self.content class ProxyB(Subject): def __init__(self, filename): self.filename = filename self.subj = None def get_content(self): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.get_content() p = ProxyB("abc.txt")
组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。角色:抽象组件,叶子组件,复合组件,客户端。适用场景:
-
表示对象的“部分-整体”层次结构(特别是结构是递归的)
-
希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象
- 优点:定义了包含基本对象和组合对象的类层次结构;简化客户端代码,即客户端可以一致地使用组合对象和单个对象;更容易增加新类型的组件。
-
缺点:很难限制组合中的组件。
from abc import abstractmethod, ABCMeta class Graphic(metaclass=ABCMeta): @abstractmethod def draw(self): pass @abstractmethod def add(self, graphic): pass def getchildren(self): pass class Point(Graphic): def __init__(self, x, y): self.x = x self.y = y def draw(self): print(self) def add(self, graphic): raise TypeError def getchildren(self): raise TypeError def __str__(self): return "点(%s, %s)" % (self.x, self.y) class Line(Graphic): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def draw(self): print(self) def add(self, graphic): raise TypeError def getchildren(self): raise TypeError def __str__(self): return "线段[%s, %s]" % (self.p1, self.p2) # 复合图形 class Picture(Graphic): def __init__(self): self.children = [] def add(self, graphic): self.children.append(graphic) def getchildren(self): return self.children def draw(self): print("------复合图形------") for g in self.children: g.draw() print("------END------") pic1 = Picture() pic1.add(Point(2,3)) pic1.add(Line(Point(1,2), Point(4,5))) pic1.add(Line(Point(0,1), Point(2,1))) pic2 = Picture() pic2.add(Point(-2,-1)) pic2.add(Line(Point(0,0), Point(1,1))) pic = Picture() pic.add(pic1) pic.add(pic2) pic1.draw()
行为型模式
观察者模式(发布-订阅模式)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式。
角色:
- 抽象主体(发布者)
- 具体主体
- 抽象发布者
- 具体发布者
优点:目标和观察者之间的抽象耦合最小。
缺点:多个观察者之间互不知道对方存在,因此一个观察者对主题的修改可能造成错误的更新。
适用场景:
- 当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用。
-
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 对象之间不是紧密耦合。
from abc import ABCMeta, abstractmethod class Observer(metaclass=ABCMeta): @abstractmethod def update(self, notice): pass class Notice: def __init__(self): self.observers = [] def attach(self, obs): self.observers.append(obs) def detach(self, obs): self.observers.remove(obs) def notify(self): for obj in self.observers: obj.update(self) class ManagerNotice(Notice): def __init__(self, company_info=None): super().__init__() self.__company_info = company_info @property def company_info(self): return self.__company_info @company_info.setter def company_info(self, info): self.__company_info = info self.notify() class Manager(Observer): def __init__(self): self.company_info = None def update(self, noti): self.company_info = noti.company_info
策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
优点:定义了一系列可重用的算法和行为;消除了一些条件语句;提供行为的不同实现。
缺点:客户必须了解全部策略;
适用场景:
- 相同算法的不同变体
- 封装一些条件语句
- 适用一些客户不需要知道的数据。
class Sort(metaclass=ABCMeta): @abstractmethod def sort(self, data): pass class Merge_sort(Sort): def sort(self, data): pass class Quick_sort(Sort): def sort(self, data): pass class Context: def __init__(self, data, strategy=None): self.data = data self.strategy = strategy def set_strategy(self, strategy): self.strategy = strategy def do_strategy(self): if self.strategy: self.strategy.sort(self.data) else: raise TypeError
责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色:抽象处理者,具体处理者,客户端。
使用场景:
- 有多个对象可以处理一个请求,哪个对象处理由运行时决定
- 在不明确接收者的情况下,向多个对象中的一个提交一个请求
优点:降低耦合度,不需要知道是那个对象处理的请求。
缺点:请求不能保证一定被接受。
from abc import ABCMeta, abstractmethod class Handler(metaclass=ABCMeta): @abstractmethod def handle_leave(self, day): pass class GeneralManagerliHandler(Handler): def handle_leave(self, day): if day < 10: print("总经理批准%d天假"%day) else: print("呵呵") class DepartmentManagerHandler(Handler): def __init__(self): self.successor = GeneralManagerliHandler() def handle_leave(self, day): if day < 7: print("部门经理批准%d天假"%day) else: print("部门经理无权准假") self.successor.handle_leave(day) class ProjectDirectorHandler(Handler): def __init__(self): self.successor = DepartmentManagerHandler() def handle_leave(self, day): if day < 3: print("项目主管批准%d天假") else: print("项目主管无权准假") self.successor.handle_leave(day) day = 10 h = ProjectDirectorHandler() h.handle_leave(day)
模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。如市面上的一些框架。
角色:
- 抽象类,定义抽象的原子操作,实现一个原子方法作为算法的骨架。
- 具体类,实现原子操作
适用场景:
- 一次性实现一个算法中不可变的部分。
- 把各个子类中的公共行为提出来,避免重复。
from abc import ABCMeta, abstractmethod class IOHandler(metaclass=ABCMeta): @abstractmethod def open(self, name): pass @abstractmethod def deal(self, change): pass @abstractmethod def close(self): pass def process(self, name, change): self.open(name) self.deal(change) self.close()