设计模式及Python实现

 

设计模式是什么?

Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样你就能一次又一次地使用该方案而不必做重复劳动。”

设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。最为重要的是,设计模式也是一把双刃剑,如果设计模式被用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。

起初,你会认为“模式”就是为了解决一类特定问题而特别想出来的明智之举。说的没错,看起来的确是通过很多人一起工作,从不同的角度看待问题进而形成的一个最通用、最灵活的解决方案。也许这些问题你曾经见过或是曾经解决过,但是你的解决方案很可能没有模式这么完备。

虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。

可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。

三种最基本的设计模式:

  1. 创建模式,提供实例化的方法,为适合的状况提供相应的对象创建方法。
  2. 结构化模式,通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。
  3. 行为模式,用于在不同的实体建进行通信,为实体之间的通信提供更容易,更灵活的通信方法。

设计模式六大原则

  • 开闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
  • 里氏(Liskov)替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
  • 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。
  • 接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
  • 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。
  • 单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。

接口

接口:一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这些方法。
作用:限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。

接口就是一种抽象的基类(父类),限制继承它的类必须实现接口中定义的某些方法。

Python中使用ABCMeta、abstractmethod的抽象类、抽象方法来实现接口的功能。接口类定义方法,不具体实现,限制子类必须有该方法。在接口子类中实现具体的功能。

# 通过抽象类和抽象方法,做抽象用
from abc import ABCMeta
from abc import abstractmethod # 导入抽象方法 class Father(metaclass=ABCMeta): # 创建抽象类 @abstractmethod
def f1(self):
pass @abstractmethod
def f2(self):
pass class F1(Father):
def f1(self):
pass def f2(self):
pass def f3(self):
pass obj = F1()
class Interface:
def method(self, arg):
raise NotImplementedError

报错定义接口

创建型模式

1. 简单工厂模式

内容:不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。

角色

  • 工厂角色(Creator)
  • 抽象产品角色(Product)
  • 具体产品角色(Concrete Product)

优点

  • 隐藏了对象创建的实现细节
  • 客户端不需要修改代码

缺点

  • 违反了单一职责原则,将创建逻辑几种到一个工厂类里
  • 当添加新产品时,需要修改工厂类代码,违反了开闭原则
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 == 'yuebao':
return Alipay(enable_yuebao=True)
elif method == "applepay":
return ApplePay()
else:
raise NameError(method) f = PaymentFactory()
p = f.create_payment("yuebao")
p.pay(100)

PaymentFactory简单工厂

2. 工厂方法模式(Factory Method)

内容:定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。

角色

  • 抽象工厂角色(Creator)
  • 具体工厂角色(Concrete 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() af = AlipayFactory()
ali = af.create_payment()
ali.pay(120)

工厂方法

3. 抽象工厂方法(Abstract Factory)

内容:定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。
例:生产一部手机,需要手机壳、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.  建造者模式(Builder)

内容:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
角色

  • 抽象建造者(Builder)
  • 具体建造者(Concrete Builder)
  • 指挥者(Director)
  • 产品(Product)

建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。

适用场景

  • 当创建复杂对象的算法(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 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)

内容:保证一个类只有一个实例,并提供一个访问它的全局访问点。
角色

  • 单例(Singleton)

适用场景

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时

优点

  • 对唯一实例的受控访问
  • 单例相当于全局变量,但防止了命名空间被污染。

与单例模式功能相似的概念:全局变量、静态变量(方法)

实现

class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, )
return cls._instance class MyClass(Singleton):
a = 1 def __init__(self, name):
self.name = name one = MyClass('egon')
two = MyClass('alex') print(id(one))
print(id(two))
print(one == two)
print(one is two)

1. 使用__new__方法

def singleton(cls, *args, **kw):
instances = {} def get_instance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls] return get_instance @singleton
class MyClass2:
a = 1 one = MyClass2()
two = MyClass2() print(id(one)) #
print(id(two)) #
print(one == two)
print(one is two)

2. 装饰器方法

# Python的模块是天然的单例模式。
# module_name.py
class MySingleton(object):
def foo(self):
print('danli') my_singleton = MySingleton() # to use
from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton)) from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton))

3. import方法

class Singleton(type):
_instances = {} def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls] # Python2
# class MyClass:
# __metaclass__ = Singleton # Python3
class MyClass(metaclass=Singleton):
pass one = MyClass()
two = MyClass() print(id(one))
print(id(two))
print(one == two)
print(one is two)

4. 使用metaclass

6. 原型模式(Prototype)

内容:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

使用场景

  • 通过动态装载;
  • 为了避免创建一个与产品类层次平行的工厂类层次时;
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

import copy

class Prototype:
def __init__(self):
self._objects = {} def register_object(self, name, obj):
"""Register an object"""
self._objects[name] = obj def unregister_object(self, name):
"""Unregister an object"""
del self._objects[name] def clone(self, name, **attr):
"""Clone a registered object and update inner attributes dictionary"""
obj = copy.deepcopy(self._objects.get(name))
obj.__dict__.update(attr)
return obj def main():
class A:
def __str__(self):
return "I am A" a = A()
prototype = Prototype()
prototype.register_object('a', a)
b = prototype.clone('a', a=1, b=2, c=3) print(a)
print(b.a, b.b, b.c) if __name__ == '__main__':
main()

原型模式

创建型模式总结

使用抽象工厂(Abstract Factory)、原型(Prototype)或者建造者(Builder)的设计甚至比工厂方法(Factory Method)的那些设计更灵活,但它们也更加复杂。通常,设计以使用工厂方法(Factory Method)开始。并且当设计者发现需要更大的灵活性时,设计便会想其他创建模式烟花。当你在设计标准之间权衡的时候,了解多个模式可以给你提供给更多的选择余地。

依赖于继承的创建型模式:工厂方法模式

依赖于组合的创建型模式:抽象工厂模式、创建者模式

结构性模式

1. 适配器模式(Adapter Class/Object)

内容:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色

  • 目标接口(Target)
  • 待适配的类(Adaptee)
  • 适配器(Adapter)

两种实现方式

  • 类适配器:使用多继承
  • 对象适配器:使用组合

适用场景

  • 想使用一个已经存在的类,而它的接口不符合你的要求
  • (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

类适配器

  • 用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有他的子类时,类Adaptee将不能胜任工作。
  • 使得Adapter可以重定义Adaptee的部分行为,因为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 cost(self, money):
print("微信支付%s元" % money) # 类适配器
class RealWechatPay(WechatPay, Payment):
def pay(self, money):
return self.cost(money) # 对象适配器 class RealWechatPay2(Payment):
def __init__(self):
self.payment = WechatPay() def pay(self, money):
return self.payment.cost(money) p = RealWechatPay2()
p.pay(111)

适配器

2. 组合模式(Composite)

内容:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色

  • 抽象组件(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()

组合

3. 代理模式 (Proxy)

内容:为其他对象提供一种代理以控制对这个对象的访问。
角色

  • 抽象实体(Subject)
  • 实体(RealSubject)
  • 代理(Proxy)

适用场景

  • 远程代理:为远程的对象提供代理
  • 虚代理:根据需要创建很大的对象
  • 保护代理:控制对原始对象的访问,用于对象有不同访问权限时

优点

  • 远程代理:可以隐藏对象位于远程地址空间的事实
  • 虚代理:可以进行优化,例如根据要求创建对象
  • 保护代理:允许在访问一个对象时有一些附加的内务处理

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() def set_content(self, content):
return self.subj.set_content(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')
# 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!="alex":
# p = ProxyC(filename)
# else:
# p = ProxyA(filename)
#
# print(p.get_content())

代理模式

行为模式

1. 责任链模式(Chain of Responsibility)

内容:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色

  • 抽象处理者(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)
return True
else:
print("呵呵")
return False class DepartmentManagerHandler(Handler):
def __init__(self):
self.successor = GeneralManagerHandler() def handle_leave(self, day):
if day < 7:
print("部门经理批准%d天假" % day)
return True
else:
print("部门经理无权准假")
return 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)
return True
else:
print("项目主管无权准假")
return self.successor.handle_leave(day) day = 11
h = ProjectDirectorHandler()
print(h.handle_leave(day))

请假流程

# --高级例子--模仿js事件处理
from abc import ABCMeta, abstractmethod 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) # print(body) 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) # test def func_a():
print("这是给a的函数") def func_div():
print("这是给div的函数") def func_body():
print("这是给body的函数") attach_event(a, func_a)
attach_event(div, func_div)
attach_event(body, func_body) a['event_handler'].handle()

模仿js事件处理

2. 迭代器模式(Iterator)

内容:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

适用场景:

  • 访问一个聚合对象的内容而无需暴露它的内部表示。
  • 支持对聚合对象的多种遍历。
  • 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)

实现方法:__iter__、__next__

class LinkList:
"""链表 头结点保存链表的长度""" class Node:
def __init__(self, item=None):
self.item = item
self.next = None class LinkListIterator:
def __init__(self, node):
self.node = node def __next__(self):
if self.node:
cur_node = self.node
self.node = cur_node.next
return cur_node.item
else:
raise StopIteration def __iter__(self):
return self def __init__(self, iterable=None):
self.head = LinkList.Node(0)
self.tail = self.head
self.extend(iterable) def append(self, obj):
s = LinkList.Node(obj)
self.tail.next = s
self.tail = s
self.head.item += 1 def extend(self, iterable):
for obj in iterable:
self.append(obj) def __iter__(self):
return self.LinkListIterator(self.head.next) def __len__(self):
return self.head.item def __str__(self):
return "<<" + ", ".join(map(str, self)) + ">>" li = [i for i in range(100)]
lk = LinkList(li)
print(lk)

链表

3. 观察者模式(Observer)

内容:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式
角色

  • 抽象主题(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 def detach(self, obs):
super().detach(obs)
obs.company_info = None @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() alex = Manager()
wusir = Manager() print(alex.company_info)
print(wusir.company_info) notice.attach(alex)
notice.attach(wusir)
notice.company_info = "公司运行良好" print(alex.company_info)
print(wusir.company_info) notice.company_info = "公司将要上市" print(alex.company_info)
print(wusir.company_info) notice.detach(wusir) notice.company_info = "公司要破产了,赶快跑路" print(alex.company_info)
print(wusir.company_info) notice.company_info = "公司已经破产了" print(alex.company_info)
print(wusir.company_info)

发布者——订阅者

4. 策略模式(Strategy)

内容:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
角色

  • 抽象策略(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()

策略模式

5. 模板方法(Template Method)

内容:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
角色

  • 抽象类(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")

模板方法

参考文献:

《二十三种设计模式及其python实现》——李琼羽

 
 
 

浅谈设计模式及python实现的更多相关文章

  1. 浅谈设计模式-visitor访问者模式

    先看一个和visitor无关的案例.假设你现在有一个书架,这个书架有两种操作,1添加书籍2阅读每一本书籍的简介. //书架public class Bookcase { List<Book> ...

  2. 浅谈如何使用python抓取网页中的动态数据

    我们经常会发现网页中的许多数据并不是写死在HTML中的,而是通过js动态载入的.所以也就引出了什么是动态数据的概念, 动态数据在这里指的是网页中由Javascript动态生成的页面内容,是在页面加载到 ...

  3. 浅谈设计模式--装饰者模式(Decorator Pattern)

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  4. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  5. 浅谈设计模式在GIS中的应用

    设计模式在GIS中的应用 一.设计模式概述 随着面向对象技术的广泛应用,软件复用在越来越多的开发工程中被采用.在研究软件复用的过程中,设计模式的概念被提了出来.所谓设计模式就是一些设计面向对象的软件的 ...

  6. 浅谈开发中python通过os模块存储数据

    #其实本人很烦发博客,但为了面试还是发一下好,证明一下自己的能力 前言 首先说一下适用环境,在开发中我们有一些经常用到的数据(数据量大)需要存储起来. 存sql嘛又不合适,要知道在开发中每条sql语句 ...

  7. 浅谈装饰器(Python)

    先来了解函数和执行函数在python的区别   我再重新定义一个函数,在函数前面加上@set_func 执行结果如下:   函数前面没有加@set_fun 执行结果如下:   是不是可以不修改原来的函 ...

  8. Spring源码分析 之浅谈设计模式

    一直想专门写个Spring源码的博客,工作了,可以全身性的投入到互联网行业中.虽然加班很严重,但是依然很开心.趁着凌晨有时间,总结总结. 首先spring,相信大家都很熟悉了. 1.轻量级  零配置, ...

  9. [Android&amp;Java]浅谈设计模式-代码篇:观察者模式Observer

    观察者,就如同一个人,对非常多东西都感兴趣,就好像音乐.电子产品.Game.股票等,这些东西的变化都能引起爱好者们的注意并时刻关注他们.在代码中.我们也有这种一种方式来设计一些好玩的思想来.今天就写个 ...

随机推荐

  1. 系统学习 Java IO (十二)----数据流和对象流

    目录:系统学习 Java IO---- 目录,概览 DataInputStream/DataOutputStream 允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型. 要想使用 ...

  2. 【Linux杂记】Linux配置静态IP地址,修改主机名、host

    博主使用的系统是:乌班图16.04 1.设置静态IP方法如下: #sudo vim /etc/network/interfaces #修改如下部分: auto eth0//ipconfig命令查看网卡 ...

  3. Github项目备份与更新

    需求 假如现在想对使用到的一些Github上的开源组件进行备份,采用自建Gitlab服务器的方式进行备份保存,并且组件需定时保持与Github更新. 总体步骤 组件备份- 整体步骤如下: a.搭建gi ...

  4. html更改弹窗样式(原创,转载需声明)

    代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  5. Failed to start Docker Application Container Engine.

    [root@dockertest ~]# systemctl status docker.service● docker.service - Docker Application Container ...

  6. python 基础学习笔记(1)

    声明:  本人是在校学生,自学python,也是刚刚开始学习,写博客纯属为了让自己整理知识点和关键内容,当然也希望可以通过我都博客来提醒一些零基础学习python的人们.若有什么不对,请大家及时指出, ...

  7. jquery.cookie.js 获取不到cookie的值

    最近使用$.cookie获取cookie 的值.发现完全取不到.看了浏览器里也存了对应的值.可以添加,但是就是取不到., 折腾了半天,后来突然想起来加了个有可能带汉字的cookie键值.删掉就可以取出 ...

  8. django基础知识之上传图片:

    上传图片 当Django在处理文件上传的时候,文件数据被保存在request.FILES FILES中的每个键为<input type="file" name="& ...

  9. OSPF 单区域实验

    实验拓扑 实验需求 按照图示配置 IP 地址 按照图示分区域配置 OSPF ,实现全网互通 为了路由结构稳定,要求路由器使用环回口作为 Router-id 实验步骤 每台路由器都要将本地的所有直连网段 ...

  10. py+selenium 老是定位不到文本内容【已解决】

    问题:定位不到文本内容,路径也正确,该加frame也有加,等待时间也够长 测试: 上图看不出差异,但是测试1就定位得到,测试2就定位不到,为什么? 看下图就知道了 区别就在于,测试2后面多了个空格!! ...