【设计模式】
参考博客 《大话设计模式》Python版代码实现 二十三种设计模式及其python实现
一、设计模式介绍
设计模式(Design Patterns)
——可复用面向对象软件的基础
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样你就能一次又一次地使用该方案而不必做重复劳动。”每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。
设计模式四个基本要素:模式名称、问题、解决方案、效果
二、设计模式分类
经典的《设计模式》一书归纳出23种设计模式,这23种模式又可归为,创建型、结构型和行为型3大类
2.1.创建型模式
前面讲过,社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:
简单工厂模式(Simple Factory);
工厂方法模式(Factory Method);
抽象工厂模式(Abstract Factory);
创建者模式(Builder);
原型模式(Prototype);
单例模式(Singleton)。
说明:严格来说,简单工厂模式不是GoF总结出来的23种设计模式之一。
依赖于继承的创建型模式:工厂方法模式
依赖于组合的创建性模式:抽象工厂模式、创建者模式
2.2 结构型模式
在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:
适配器模式(Adapter);
桥模式(Bridge);
组合模式(Composite);
装饰模式(Decorator);
外观模式(Facade);
享元模式(Flyweight)
代理模式(Proxy);
2.3 行为型模式
在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高,这里有11个具体的行为型模式可供研究,它们分别是:
解释器模式(Interpreter)。
责任链模式(Chain of Responsibility);
命令模式(Command);
迭代器模式(Iterator);
中介者模式(Mediator);
备忘录模式(Memento);
观察者模式(Observer);
状态模式(State);
策略模式(Strategy);
访问者模式(Visitor);
模板方法模式(Template Method);
3. 设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏替换原则(Liskov Substitution Principle)
里氏替换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。及所有引用基类(父类)的地方必须能透明地使用其子类的对象。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒置原则(Dependence Inversion Principle)
这个是开闭原则的基础,高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。具体内容:是对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。总结概括:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口;降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、单一职责原则:
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
四、写在正式介绍设计模式之前
面向对象三大特性:封装,继承,多态
封装:
1、把数据和函数包装在类里
2、类的边界限制了一些外界的访问 ---》 私有属性
继承:解决代码复用 重写方法叫做:override 重写,复写
多态:父类可以代表子类
抽象类:
抽象类不能被实例化
抽象类的抽象方法,必须在子类中实现
接口:
一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这些方法。
作用:限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。
接口就是一种抽象的基类(父类),限制继承它的类必须实现接口中定义的某些方法
from abc import abstractmethod, ABCMeta #定义抽象类 class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): """支付方法,参数money,每个继承的类都要实现这个方法""" pass #实现Payment接口 class Alipay(Payment): def pay(self, money): print("支付宝支付%s元"%money) class ApplePay(Payment): def pay(self, money): print("苹果支付%s元"%money) class WechatPay(Payment): def pay(self, money): print("微信支付%s元"%money) def func(x): x.pay(100)
五、各种设计模式
1、简单工厂模式
创建对象放在了工厂类中,而不是再暴露在外边,通过工厂类实现所有操作。
from abc import abstractmethod, ABCMeta #定义支付方法的抽象类 class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass #定义不同的支付方法 class Alipay(Payment): def __init__(self, enable_yuebao=False): self.enable_yuebao = enable_yuebao def pay(self, money): if self.enable_yuebao: print("余额宝支付%s元" % money) else: print("支付宝支付%s元" % money) class ApplePay(Payment): 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 == "yuebao": return Alipay(enable_yuebao=True) else: raise NameError(method) f = PaymentFactory() p = f.create_payment("alipay") p.pay(100)
2、工厂方法模式
具体工厂依赖具体产品
内容:定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。
角色:
抽象工厂角色(Creator)
具体工厂角色(Concrete Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)
模式特点:定义一个用于创建对象的接口,让子类决定实例化哪一个类。这使得一个类的实例化延迟到其子类。
这个模式和简单工厂有区别,简单工厂模式只有一个工厂,工厂方法模式相比简单工厂模式将每个具体产品都对应了一个具体工厂。
适用场景:
需要生产多种、大量复杂对象的时候
需要降低耦合度的时候
当系统中的产品种类需要经常扩展的时候
优点:
每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
隐藏了对象创建的实现细节
缺点:
每增加一个具体产品类,就必须增加一个相应的具体工厂类
内容:不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。
角色:
工厂角色(Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)
优点:
隐藏了对象创建的实现细节,工厂根据条件产生不同功能的类。
客户端不需要修改代码
缺点:
违反了单一职责原则,将创建逻辑几种到一个工厂类里
当添加新产品时,需要修改工厂类代码,违反了开闭原则
from abc import abstractmethod, ABCMeta #定义支付类型的抽象类 class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass #继承支付抽象类 class Alipay(Payment): def pay(self, money): 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() class ApplePayFactory(PaymentFactory): def create_payment(self): return ApplePay() #具体使用,利用具体工厂的对象 实例 具体支付的对象 然后 调用支付方法 # 用户输入 # 支付宝,120 af = AlipayFactory() ali = af.create_payment() ali.pay(120)
3、抽象工厂模式
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
内容:定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。
例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。
角色:
抽象工厂角色(Creator)
具体工厂角色(Concrete Creator)
抽象产品角色(Product)
具体产品角色(Concrete Product)
客户端(Client)
相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。
适用场景:
系统要独立于产品的创建与组合时
强调一系列相关的产品对象的设计以便进行联合使用时
提供一个产品类库,想隐藏产品的具体实现时
优点:
将客户端与类的具体实现相分离
每个工厂创建了一个完整的产品系列,使得易于交换产品系列
有利于产品的一致性(即产品之间的约束关系)
缺点:
难以支持新种类的(抽象)产品
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(HuaweiFactory()) p1.show_info()
4、建造者模式
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用场景:
当创建复杂对象的算法(Director)应该独立于该对象的组成部分(Builder)以及它们的装配方式时
当构造过程允许被构造的对象有不同的表示时(不同Builder)。
角色:
抽象建造者(Builder)
具体建造者(Concrete Builder)
指挥者(Director)
产品(Product)
建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象
优点:
隐藏了一个产品的内部结构和装配过程
将构造代码与表示代码分开
可以对构造过程进行更精细的控制
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 # 用于生产 产品的类 class PlayerDirector: def build_player(self, builder): builder.build_body() builder.build_arm() builder.build_leg() builder.build_face() return builder.get_player() #客户端 director = PlayerDirector() builder = BeautifulWomanBuilder() p = director.build_player(builder) print(p)
5、单例模式
意图:创建一个类,保证一个类只有一个实例,并提供一个访问它的全局访问点。单例相当于是全局变量,内部的方法可能会被更改
角色:
单例(Singleton)
适用场景
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
优点:
对唯一实例的受控访问
单例相当于全局变量,但防止了命名空间被污染
与单例模式功能相似的概念:全局变量、静态变量(方法)
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): self.name = name a = MyClass("a") print(a) print(a.name) b = MyClass('b') # print(b) print(b.name) b.name = 'xxx' # print(a) print(a.name)
单例模式: 单例模式 每次只实例化一个对象!造对象的过程是__new__方法,给对象传数据是__init__的方法,做成一个实例! 类不管怎样创建多个实例,对象都会以第一个实例为准 #方式一:文件导入,文件内创建一个对象,通过这个对象导入方法 #方式二:通过静态方法实例化对象 class Foo: # 定义静态变量实例 _instance = None def __init__(self): pass @staticmethod def get_instance(cls): if cls._instance: return cls._instance else: obj = cls() cls._instance = obj return obj #方式三: __new__ class Foo(object): _instance = None def __init__(self): pass def __new__(cls, *args, **kwargs): if cls._instance: return cls._instance else: obj = object.__new__(cls, *args, **kwargs) cls._instance = obj return obj 用途: - 自定义curb组件时,对于类构建的信息,仅需一个实例 - 发布文章,特殊字符的过滤: KindEditor
6、适配器模式
意图:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色:
目标接口(Target)
待适配的类(Adaptee)
适配器(Adapter)
两种实现方式:
类适配器:使用多继承
对象适配器:使用组合
适用场景:
你想使用一个已经存在的类,而它的接口不符合你的要求;
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
类适配器和对象适配器有不同的权衡。
类适配器规则:
使用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有他的子类时,类Adapter将不能胜任工作。
使用Adapter可以重定义Adapter的部分行为,因为Adapter是Adaptee的一个子类。
仅仅引入一个对象,并不需要额外的指针以间接得到adaptee.
对象适配器规则:
允许一个Adapter与多个Adaptee -- 即Adaptee本身以及它的所有子类(如果有子类的话)一一同时工作。Adapter也可以一次给所有的Adaptee添加功能。
使得重定义 Adaptee 的行为比较困难。这需要生成Adaptee的子类并且使得 Adapter 引用这个子类而不是引用Adaptee本身。
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 RealWeChatPay(Payment, WechatPay): def pay(self, money): return self.huaqian(money) #------对象适配器------ class PayAdapter(Payment): def __init__(self, payment): self.payment = payment #类的一个对象 def pay(self, money): return self.payment.huaqian(money) RealWeChatPay().pay(100) #PayAdapter(WechatPay()).pay(1000)
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): """支付方法,参数money""" pass #实现Payment接口 class Alipay(Payment): def pay(self, money): print("支付宝支付%s元"%money) class ApplePay(Payment): def pay(self, money): print("苹果支付%s元"%money) class PaymentX(metaclass=ABCMeta): @abstractmethod def zhifu(self, money): """支付方法,参数money""" pass class WechatPay(PaymentX): def zhifu(self, money): print("微信支付%s元"%money) class CreditCardPay(PaymentX): def zhifu(self, money): print("信用卡支付%s元" % money) class PaypalPay(PaymentX): def zhifu(self, money): print("信用卡支付%s元" % money) #类适配器 class RealWechatPay(Payment, WechatPay): def pay(self, money): self.zhifu(money) p = RealWechatPay() p.pay(100) #对象适配器 class RealPay(Payment): def __init__(self, payment_cls): self.__a = payment_cls() def pay(self, money): self.__a.zhifu(money) def test_payment(p): p.pay(100) test_payment(RealPay(CreditCardPay))
7、组合模式
意图:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色:
抽象组件(Component)
叶子组件(Leaf)
复合组件(Composite)
客户端(Client)
适用场景:
表示对象的“部分-整体”层次结构(特别是结构是递归的)
希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象
优点:
定义了包含基本对象和组合对象的类层次结构
简化客户端代码,即客户端可以一致地使用组合对象和单个对象
更容易增加新类型的组件
缺点:
很难限制组合中的组件
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() point = Point(2,3) pic1.add(point) 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) pic.draw() pic1.draw() point.draw()
8、代理模式
意图:为其他对象提供一种代理以控制对这个对象的访问。在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。
角色:
抽象实体(Subject)
实体(RealSubject)
代理(Proxy)
适用场景:
远程代理:为远程的对象提供代理。一个对象在不同的地址空间提供局部代表。
虚代理:根据需要创建很大的对象。
保护代理:控制对原始对象的访问,用于对象有不同访问权限时。保护代理用于对象应该有不同的访问权限的时候。例如,在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供了访问保护。
智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
优点:
远程代理:可以隐藏对象位于远程地址空间的事实
虚代理:可以进行优化,例如根据要求创建对象
保护代理:允许在访问一个对象时有一些附加的内务处理
当第一次引用一个持久对象时,将它装入内存。在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
from abc import ABCMeta, abstractmethod class Subject(metaclass=ABCMeta): @abstractmethod def get_content(self): pass def set_content(self, content): pass #文件操作,获取文件内容和设置 class RealSubject(Subject): def __init__(self, filename): self.filename = filename print("读取%s文件内容"%filename) f = open(filename) self.content = f.read() f.close() def get_content(self): return self.content def set_content(self, content): f = open(self.filename, 'w') f.write(content) self.content = content f.close() class ProxyA(Subject): def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): return self.subj.get_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() x = ProxyB('abc.txt') """abc.txt 内文本 hello world""" #print(x.get_content()) # --保护代理 class ProxyC(Subject): def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): self.subj.get_content() def set_content(self, content): raise PermissionError filename = "abc.txt" username = input() if username!="somebody": p = ProxyC(filename) else: p = ProxyA(filename) print(p.get_content())
9、责任链模式
意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色:
抽象处理者(Handler)
具体处理者(ConcreteHandler)
客户端(Client)
适用场景:
有多个对象可以处理一个请求,哪个对象处理由运行时决定
在不明确接收者的情况下,向多个对象中的一个提交一个请求
可处理一个请求的对象集合应被动态指定。
优点:
降低耦合度:一个对象无需知道是其他哪一个对象处理其请求
缺点:
请求不保证被接收:链的末端没有处理或链配置错误
例:
请假部门批准:leader ---> 部门经理 ---> 总经理
Javascript事件浮升机制
from abc import ABCMeta, abstractmethod class Handler(metaclass=ABCMeta): @abstractmethod def handle_leave(self, day): pass class GeneralManagerHandler(Handler): def handle_leave(self, day): if day < 10: print("总经理批准%d天假"%day) else: print("呵呵") class DepartmentManagerHandler(Handler): def __init__(self): self.successor = GeneralManagerHandler() 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天假"%day) else: print("项目主管无权准假") self.successor.handle_leave(day) day = 9 h = ProjectDirectorHandler() h.handle_leave(day)
class Handler(metaclass=ABCMeta): @abstractmethod def add_event(self, func): pass @abstractmethod def handle(self): pass class BodyHandler(Handler): def __init__(self): self.func = None def add_event(self, func): self.func = func def handle(self): if self.func: return self.func() else: print("已到最后一级,无法处理") class ElementHandler(Handler): def __init__(self, successor): self.func = None self.successor = successor def add_event(self, func): self.func = func def handle(self): if self.func: return self.func() else: return self.successor.handle() # 客户端 # <body><div><a> body = {'type': 'body', 'name': 'body', 'children': [], 'father': None} div = {'type': 'div', 'name': 'div', 'children': [], 'father': body} a = {'type': 'a', 'name': 'a', 'children': [], 'father': div} body['children'].append(div) div['children'].append(a) body['event_handler'] = BodyHandler() div['event_handler'] = ElementHandler(div['father']['event_handler']) a['event_handler'] = ElementHandler(a['father']['event_handler']) def attach_event(element, func): element['event_handler'].add_event(func) def func_div(): print("这是给div的函数") def func_a(): print("这是给a的函数") def func_body(): print("这是给body的函数") #attach_event(div, func_div) attach_event(a, func_a) attach_event(body, func_body) a['event_handler'].handle()
10、迭代器模式
意图:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
适用性:
访问一个聚合对象的内容而无需暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
实现方法:__iter__、__next__
class Range: def __init__(self,start,end): self.start = start self.end = end def __iter__(self): #将对象转变成迭代器 return self def __next__(self): #自取值 if self.start == self.end: raise StopIteration n = self.start self.start+=1 return n for i in Range(1,10): print(i)
11、观察者模式
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式
角色:
抽象主题(Subject)
具体主题(ConcreteSubject)——发布者
抽象观察者(Observer)
具体观察者(ConcreteObserver)——订阅者
适用场景:
当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
优点:
目标和观察者之间的抽象耦合最小
支持广播通信
缺点:
多个观察者之间互不知道对方存在,因此一个观察者对主题的修改可能造成错误的更新。
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) obs.company_info=None 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 notice = ManagerNotice() abes = Manager() dws = Manager() # print(alex.company_info) # print(wusir.company_info) notice.attach(abes) notice.attach(dws) # notice.company_info="公司运行良好" # # print(alex.company_info) # print(wusir.company_info) notice.company_info="公司将要上市" print(abes.company_info) print(dws.company_info) # notice.detach(dws) # # notice.company_info="公司要破产了,赶快跑路" # print(abes.company_info) print(dws.company_info)
12、策略模式
意图:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
角色:
抽象策略(Strategy)
具体策略(ConcreteStrategy)
上下文(Context)
适用场景:
许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
算法使用了客户端无需知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
一个类中的多种行为以多个条件语句的形式存在,可以将这些行为封装如不同的策略类中。
from abc import ABCMeta, abstractmethod import random class Sort(metaclass=ABCMeta): @abstractmethod def sort(self, data): pass class QuickSort(Sort): def quick_sort(self, data, left, right): if left < right: mid = self.partition(data, left, right) self.quick_sort(data, left, mid - 1) self.quick_sort(data, mid + 1, right) def partition(self, data, left, right): tmp = data[left] while left < right: while left < right and data[right] >= tmp: right -= 1 data[left] = data[right] while left < right and data[left] <= tmp: left += 1 data[right] = data[left] data[left] = tmp return left def sort(self, data): print("快速排序") return self.quick_sort(data, 0, len(data) - 1) class MergeSort(Sort): def merge(self, data, low, mid, high): i = low j = mid + 1 ltmp = [] while i <= mid and j <= high: if data[i] <= data[j]: ltmp.append(data[i]) i += 1 else: ltmp.append(data[j]) j += 1 while i <= mid: ltmp.append(data[i]) i += 1 while j <= high: ltmp.append(data[j]) j += 1 data[low:high + 1] = ltmp def merge_sort(self, data, low, high): if low < high: mid = (low + high) // 2 self.merge_sort(data, low, mid) self.merge_sort(data, mid + 1, high) self.merge(data, low, mid, high) def sort(self, data): print("归并排序") return self.merge_sort(data, 0, len(data) - 1) 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 li = list(range(100000)) random.shuffle(li) context = Context(li, MergeSort()) context.do_strategy() #print(context.data) random.shuffle(context.data) context.set_strategy(QuickSort()) context.do_strategy()
13、模板方法模式
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
角色:
抽象类(AbstractClass):定义抽象的原子操作(钩子操作);实现一个模板方法作为算法的骨架。
具体类(ConcreteClass):实现原子操作
适用场景:
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复。
控制子类扩展。模板方法只在特定点调用某操作,这样就只允许在这些点进行扩展。
优点:
定义了一系列可重用的算法和行为
消除了一些条件语句
可以提供相同行为的不同实现
缺点:
客户必须了解不同的策略
策略与上下文之间的通信开销
增加了对象的数目
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() class FileHandler(IOHandler): def open(self, name): self.file = open(name,"w") def deal(self, change): self.file.write(change) def close(self): self.file.close() f = FileHandler() f.process("abc.txt", "Hello World")