python常见几种设计模式

1. 创建型模式

1.1. 工厂模式

  • 解释:
    • 工厂方法(Factory Method),它是一个方法(或以地道的Python术语来说,是一个函数),对不同的输入参数返回不同的对象
    • 如果因为应用创建对象的代码分布在多个不同的地方,而不是仅在一个函数/方法中,你发 现没法跟踪这些对象,那么应该考虑使用工厂方法模式。工厂方法集中地在一个地方创建对象,使对象跟踪变得更容易。
    • 有工厂函数决定到底使用哪个类
  • 例子:
    import xml.etree.ElementTree as etree
    import json
    
    # JSONConnector类解析JSON文件,通过parsed_data()方法以一个字典(dict)的形式 返回数据。
    # 修饰器property使parsed_data()显得更像一个常规的变量,而不是一个方法
    class JSONConnector:
        def __init__(self, filepath):
            self.data = dict()
            with open(filepath, mode='r', encoding='utf-8') as f:
                self.data = json.load(f)
    
        @property
        def parsed_data(self):
            return self.data
    
    # XMLConnector类解析xml数据
    class XMLConnector:
        def __init__(self, filepath):
            self.tree = etree.parse(filepath)
    
        @property
        def parsed_data(self):
            return self.tree
    
    # 函数connection_factory是一个工厂方法,
    # 基于输入文件路径的扩展名返回一个JSONConnector或XMLConnector的实例
    def connection_factory(filepath):
        if filepath.endswith('json'):
            connector = JSONConnector
        elif filepath.endswith('xml'):
            connector = XMLConnector
        else:
            raise ValueError('Cannot connect to {}'.format(filepath))
        return connector(filepath)
    
    # 函数connect_to()对connection_factory()进行包装,添加了异常处理
    def connect_to(filepath):
        factory = None
        try:
            factory = connection_factory(filepath)
        except ValueError as ve:
            print(ve)
        return factory
    
    
    # 注意,虽然JSONConnector和XMLConnector拥有相同的接口,
    # 但是对于parsed_data() 返回的数据并不是以统一的方式进行处理。
    # 对于每个连接器,需使用不同的Python代码来处理。
    def main():
        sqlite_factory = connect_to('data/person.sq3')
        print()
    
        xml_factory = connect_to('data/person.xml')
        xml_data = xml_factory.parsed_data
        liars = xml_data.findall(".//{}[{}='{}']".format('person',
                                                         'lastName', 'Liar'))
        print('found: {} persons'.format(len(liars)))
        for liar in liars:
            print('first name: {}'.format(liar.find('firstName').text))
            print('last name: {}'.format(liar.find('lastName').text))
            [print('phone number ({})'.format(p.attrib['type']),
                   p.text) for p in liar.find('phoneNumbers')]
    
        print()
    
        json_factory = connect_to('data/donut.json')
        json_data = json_factory.parsed_data
        print('found: {} donuts'.format(len(json_data)))
        for donut in json_data:
            print('name: {}'.format(donut['name']))
            print('price: ${}'.format(donut['ppu']))
            [print('topping: {} {}'.format(t['id'], t['type'])) for t in donut['topping']]
    
    if __name__ == '__main__':
        main()
    

1.2. 抽象工厂模式

  • 解释:
    • 一个抽象工厂是(逻辑上的)一组工厂方法,其中的每个工厂方法负责产生不同种类的对象
    • 通常一开始时使用工厂方法,因为它更简单。如果后来发现应用需要许多工厂方法,那么将创建 一系列对象的过程合并在一起更合理,从而最终引入抽象工厂。
    • 抽象出一个工厂类来处理所有后端类,由一个入口,决定是使用后端的哪个类
  • 例子:
    # Frog青蛙碰到Bug虫子,就吃掉它
    class Frog:
        def __init__(self, name):
            self.name = name
    
        def __str__(self):
            return self.name
    
        # 这里模拟Frog和Bug类之间的交互
        def interact_with(self, obstacle):
            print('{} the Frog encounters {} and {}!'.format(self,
            obstacle, obstacle.action()))
    
    class Bug:
        def __str__(self):
            return 'a bug'
    
        def action(self):
            return 'eats it'
    
    # 类FrogWorld是一个抽象工厂,其主要职责是创建游戏的主人公和障碍物。
    # 区分创建方法并使其名字通用(比如,make_character()和make_obstacle()),
    # 这让我们可以动态改变当前激活的工厂(也因此改变了当前激活的游戏),而无需进行任何代码变更。
    class FrogWorld:
        def __init__(self, name):
            print(self)
            self.player_name = name
    
        def __str__(self):
            return '\n\n\t------ Frog World ———'
    
        def make_character(self):
            return Frog(self.player_name)
    
        def make_obstacle(self):
            return Bug()
    
    # 男巫类Wizard
    class Wizard:
        def __init__(self, name):
            self.name = name
    
        def __str__(self):
            return self.name
    
        # 男巫类Wizard和怪兽类Ork两者之间的交互
        def interact_with(self, obstacle):
            print('{} the Wizard battles against {} and {}!'.format(self, obstacle, obstacle.action()))
    
    # 怪兽类Ork
    class Ork:
        def __str__(self):
            return 'an evil ork'
    
        def action(self):
            return 'kills it'
    
    # 抽象工厂,生成实例
    class WizardWorld:
        def __init__(self, name):
            print(self)
            self.player_name = name
    
        def __str__(self):
            return '\n\n\t------ Wizard World ———'
    
        def make_character(self):
            return Wizard(self.player_name)
    
        def make_obstacle(self):
            return Ork()
    
    # 类GameEnvironment是我们游戏的主入口。它接受factory作为输入,
    # 用其创建游戏的世界。方法play()则会启动hero和obstacle之间的交互
    class GameEnvironment:
        def __init__(self, factory):
            self.hero = factory.make_character()
            self.obstacle = factory.make_obstacle()
    
        def play(self):
            self.hero.interact_with(self.obstacle)
    
    # 函数validate_age()提示用户提供一个有效的年龄。如果年龄无效,则会返回一个元组, 其第一个元素设置为False。
    # 如果年龄没问题,元素的第一个元素则设置为True,但我们真正关 心的是元素的第二个元素,也就是用户提供的年龄,
    def validate_age(name):
        try:
            age = input('Welcome {}. How old are you? '.format(name))
            age = int(age)
        except ValueError as err:
            print("Age {} is invalid, please try \
            again…".format(age))
            return (False, age)
        return (True, age)
    
    # 该函数请求用户的姓名和年龄,并根据用户的年龄决定该玩 哪个游戏
    def main():
        name = input("Hello. What's your name? ")
        valid_input = False
        while not valid_input:
            valid_input, age = validate_age(name)
        game = FrogWorld if age < 18 else WizardWorld
        environment = GameEnvironment(game(name))
        environment.play()
    
    if __name__ == '__main__':
        main()
    

1.3. 建造者模式

  • 解释
    • 创建一个由多个部分构成的对象,而且它的构成需要一步接一步地完成。 只有当各个部分都创建好,这个对象才算是完整的。这正是建造者设计模式(Builder design pattern)的用武之地。
    • 建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造 过程可用于创建多个不同的表现
    • 有建造者和指挥者
  • 例1——配置电脑:
    # coding: utf-8
    
    class Computer:
        def __init__(self, serial_number):
            self.serial = serial_number
            self.memory = None      # 单位为GB
            self.hdd = None         # 单位为GB
            self.gpu = None
    
        def __str__(self):
            info = ('Memory: {}GB'.format(self.memory),
                    'Hard Disk: {}GB'.format(self.hdd),
                    'Graphics Card: {}'.format(self.gpu))
            return '\n'.join(info)
    
    # 引入了一个建造者ComputerBuilder、一个指挥者HardwareEngineer
    # 以及一步接一步装配一台电脑的过程,这样现在就支持不同的配置了
    class ComputerBuilder:
        def __init__(self):
            self.computer = Computer('AG23385193')
    
        def configure_memory(self, amount):
            self.computer.memory = amount
    
        def configure_hdd(self, amount):
            self.computer.hdd = amount
    
        def configure_gpu(self, gpu_model):
            self.computer.gpu = gpu_model
    
    
    class HardwareEngineer:
        def __init__(self):
            self.builder = None
    
        def construct_computer(self, memory, hdd, gpu):
            self.builder = ComputerBuilder()
            [step for step in (self.builder.configure_memory(memory),
                               self.builder.configure_hdd(hdd),
                               self.builder.configure_gpu(gpu))]
    
        @property
        def computer(self):
            return self.builder.computer
    
    
    def main():
        engineer = HardwareEngineer()
        engineer.construct_computer(hdd=500, memory=8, gpu='GeForce GTX 650 Ti')
        computer = engineer.computer
        print(computer)
    
    if __name__ == '__main__':
        main()
    
  • 例2——烤面包:
    # coding: utf-8
    
    from enum import Enum  # 枚举
    import time
    
    PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready') # 准备阶段
    PizzaDough = Enum('PizzaDough', 'thin thick') # 生面团
    PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche') # 调味料
    PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano') # 配料
    STEP_DELAY = 3          # 考虑是示例,单位为秒
    
    
    class Pizza:
        def __init__(self, name):
            self.name = name
            self.dough = None
            self.sauce = None
            self.topping = []
    
        def __str__(self):
            return self.name
    
        # 这个本来不用放在这里的,但是生面团都一样,因此这里提高代码利用率放在这里
        def prepare_dough(self, dough):
            self.dough = dough
            print('preparing the {} dough of your {}...'.format(self.dough.name, self))
            time.sleep(STEP_DELAY)
            print('done with the {} dough'.format(self.dough.name))
    
    # 建造者:制作玛格丽特比萨(MargaritaBudiler),
    class MargaritaBuilder:
        def __init__(self):
            self.pizza = Pizza('margarita')
            self.progress = PizzaProgress.queued
            self.baking_time = 5        # 考虑是示例,单位为秒
    
        def prepare_dough(self):
            self.progress = PizzaProgress.preparation
            self.pizza.prepare_dough(PizzaDough.thin)
    
        def add_sauce(self):
            print('adding the tomato sauce to your margarita...')
            self.pizza.sauce = PizzaSauce.tomato
            time.sleep(STEP_DELAY)
            print('done with the tomato sauce')
    
        def add_topping(self):
            print('adding the topping (double mozzarella, oregano) to your margarita')
            self.pizza.topping.append([i for i in
                                       (PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
            time.sleep(STEP_DELAY)
            print('done with the topping (double mozzarrella, oregano)')
    
        def bake(self):
            self.progress = PizzaProgress.baking
            print('baking your margarita for {} seconds'.format(self.baking_time))
            time.sleep(self.baking_time)
            self.progress = PizzaProgress.ready
            print('your margarita is ready')
    
    # 建造者:制作奶油熏肉比萨(CreamyBaconBuilder)。
    class CreamyBaconBuilder:
        def __init__(self):
            self.pizza = Pizza('creamy bacon')
            self.progress = PizzaProgress.queued
            self.baking_time = 7        # 考虑是示例,单位为秒
    
        def prepare_dough(self):
            self.progress = PizzaProgress.preparation
            self.pizza.prepare_dough(PizzaDough.thick)
    
        def add_sauce(self):
            print('adding the crème fraîche sauce to your creamy bacon')
            self.pizza.sauce = PizzaSauce.creme_fraiche
            time.sleep(STEP_DELAY)
            print('done with the crème fraîche sauce')
    
        def add_topping(self):
            print('adding the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano) to your creamy bacon')
            self.pizza.topping.append([t for t in
                                       (PizzaTopping.mozzarella, PizzaTopping.bacon,
                                        PizzaTopping.ham, PizzaTopping.mushrooms,
                                        PizzaTopping.red_onion, PizzaTopping.oregano)])
            time.sleep(STEP_DELAY)
            print('done with the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano)')
    
        def bake(self):
            self.progress = PizzaProgress.baking
            print('baking your creamy bacon for {} seconds'.format(self.baking_time))
            time.sleep(self.baking_time)
            self.progress = PizzaProgress.ready
            print('your creamy bacon is ready')
    
    # 指挥者就是服务员。Waiter类的核心是construct_pizza方法,
    # 该方法接受一个建造者作为参数,并以正确的顺序执行比萨的所有准备步骤。
    class Waiter:
        def __init__(self):
            self.builder = None
    
        def construct_pizza(self, builder):
            self.builder = builder
            [step() for step in (builder.prepare_dough,
                                 builder.add_sauce, builder.add_topping, builder.bake)]
    
        @property
        def pizza(self):
            return self.builder.pizza
    
    # 函数validate_style()类似于第1章中描述的validate_age()函数,用于确保用户提供有效的输入,
    # 当前案例中这个输入是映射到一个比萨建造者的字符;输入字符m表示使用 MargaritaBuilder类,
    # 输入字符c则使用CreamyBaconBuilder类。
    # 这些映射关系存储在参数 builder中。该函数会返回一个元组,如果输入有效,则元组的第一个元素被设置为True,否则为False,
    def validate_style(builders):
        try:
            pizza_style = input('What pizza would you like, [m]argarita or [c]reamy bacon? ')
            builder = builders[pizza_style]()
            valid_input = True
        except KeyError as err:
            print('Sorry, only margarita (key m) and creamy bacon (key c) are available')
            return (False, None)
        return (True, builder)
    
    
    def main():
        builders = dict(m=MargaritaBuilder, c=CreamyBaconBuilder)
        valid_input = False
        while not valid_input:
            valid_input, builder = validate_style(builders)
        print()
        waiter = Waiter()
        waiter.construct_pizza(builder)
        pizza = waiter.pizza
        print()
        print('Enjoy your {}!'.format(pizza))
    
    if __name__ == '__main__':
        main()
    
  • 例3——流利的建造者:
    • 建造者模式变体,链式地调用建造者方法,通过将建造者本身定义为内部类并从其每个设置器方法返回自身来实现。方法build()返回最终的对象。这个模式被称为流利的建造者。
    • 代码:
      # coding: utf-8
      
      class Pizza:
          def __init__(self, builder):
              self.garlic = builder.garlic
              self.extra_cheese = builder.extra_cheese
      
          def __str__(self):
              garlic = 'yes' if self.garlic else 'no'
              cheese = 'yes' if self.extra_cheese else 'no'
              info = ('Garlic: {}'.format(garlic), 'Extra cheese: {}'.format(cheese))
              return '\n'.join(info)
      
          class PizzaBuilder:
              def __init__(self):
                  self.extra_cheese = False
                  self.garlic = False
      
              def add_garlic(self):
                  self.garlic = True
                  return self
      
              def add_extra_cheese(self):
                  self.extra_cheese = True
                  return self
      
              def build(self):
                  return Pizza(self)
      
      if __name__ == '__main__':
          pizza = Pizza.PizzaBuilder().add_garlic().add_extra_cheese().build()
          print(pizza)
      
      

1.4. 原型模式

  • 解释:
    • 原型设计模式(Prototype design pattern)帮助我们创建对象的克隆,其最简单的形式就是一 个clone()函数,接受一个对象作为输入参数,返回输入对象的一个副本。
    • 在我 们知道对象的某些部分会被变更但又希望保持原有对象不变之时,通常需要对象的一个副本。
  • 例子——书的描述:
    # coding: utf-8
    
    import copy
    from collections import OrderedDict
    
    
    class Book:
        # 仅有三个形参是固定的:name、authors和price,
        # v但是使用rest变长列表,调用者 能以关键词的形式(名称=值)传入更多的参数。
        # self.__dict__.update(rest)一行将rest 的内容添加到Book类的内部字典中,成为它的一部分。
        def __init__(self, name, authors, price, **rest):
            '''rest的例子有:出版商,长度,标签,出版日期'''
            self.name = name
            self.authors = authors
            self.price = price      # 单位为美元
            self.__dict__.update(rest)
    
        # 我们并不知道所有被添加参数的名称,但又需要访问内部字典将这些参数应用到__str__()中,
        # 并且字典的内容并不遵循任何特定的顺序,所以使用一个OrderedDict来强制元素有序,
        # 否则,每次程序执行都会产生不同的输出。
        def __str__(self):
            mylist = []
            ordered = OrderedDict(sorted(self.__dict__.items()))
            for i in ordered.keys():
                mylist.append('{}: {}'.format(i, ordered[i]))
                if i == 'price':
                    mylist.append('$')
                mylist.append('\n')
            return ''.join(mylist)
    
    # Prototype类实现了原型设计模式。Prototype类的核心是clone()方法,该方法使用我们 熟悉的copy.deepcopy()函数来完成真正的克隆工作。
    # 但Prototype类在支持克隆之外做了一点更多的事情,它包含了方法register()和unregister(),这两个方法用于在一个字典中追 踪被克隆的对象。
    class Prototype:
        def __init__(self):
            self.objects = dict()
    
        def register(self, identifier, obj):
            self.objects[identifier] = obj
    
        def unregister(self, identifier):
            del self.objects[identifier]
    
        def clone(self, identifier, **attr):
            found = self.objects.get(identifier)
            if not found:
                raise ValueError('Incorrect object identifier: {}'.format(identifier))
            obj = copy.deepcopy(found)
            obj.__dict__.update(attr)
            return obj
    
    
    def main():
        b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall',
                  length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures'))
    
        prototype = Prototype()
        cid = 'k&r-first'
        prototype.register(cid, b1)
        b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99,
                             length=274, publication_date='1988-04-01', edition=2)
    
        for i in (b1, b2):
            print(i)
        print('ID b1 : {} != ID b2 : {}'.format(id(b1), id(b2)))
    
    if __name__ == '__main__':
        main()
    

2. 结构型模式

简便记忆:外观(装饰器)代理(控制器)享元(适配器)

2.1. 外观模式

  • 解释
    • 外观设计模式有助于隐藏 系统的内部复杂性,并通过一个简化的接口向客户端暴露必要的部分
    • 使用外观模式的最常见理由是为一个复杂系统提供单个简单的入口点。引入外观之后,客户端代码通过简单地调用一个方法/函数就能使用一个系统。同时,内部系统并不会丢失任何功能,外观只是封装了内部系统。
  • 例子
    # coding: utf-8
    
    from enum import Enum
    from abc import ABCMeta, abstractmethod
    
    State = Enum('State', 'new running sleeping restart zombie')
    
    
    class User:
        pass
    
    
    class Process:
        pass
    
    
    class File:
        pass
    
    
    class Server(metaclass=ABCMeta):
        @abstractmethod
        def __init__(self):
            pass
    
        def __str__(self):
            return self.name
    
        @abstractmethod
        def boot(self):
            pass
    
        @abstractmethod
        def kill(self, restart=True):
            pass
    
    
    class FileServer(Server):
        def __init__(self):
            '''初始化文件服务进程要求的操作'''
            self.name = 'FileServer'
            self.state = State.new
    
        def boot(self):
            print('booting the {}'.format(self))
            '''启动文件服务进程要求的操作'''
            self.state = State.running
    
        def kill(self, restart=True):
            print('Killing {}'.format(self))
            '''杀死文件服务进程要求的操作'''
            self.state = State.restart if restart else State.zombie
    
        def create_file(self, user, name, permissions):
            '''检查访问权限的有效性、用户权限,等等'''
    
            print("trying to create the file '{}' for user '{}' with permissions {}".format(name, user, permissions))
    
    
    class ProcessServer(Server):
        def __init__(self):
            '''初始化进程服务进程要求的操作'''
            self.name = 'ProcessServer'
            self.state = State.new
    
        def boot(self):
            print('booting the {}'.format(self))
            '''启动进程服务进程要求的操作'''
            self.state = State.running
    
        def kill(self, restart=True):
            print('Killing {}'.format(self))
            '''杀死进程服务进程要求的操作'''
            self.state = State.restart if restart else State.zombie
    
        def create_process(self, user, name):
            '''检查用户权限、生成PID,等等'''
    
            print("trying to create the process '{}' for user '{}'".format(name, user))
    
    
    class WindowServer:
        pass
    
    
    class NetworkServer:
        pass
    
    
    class OperatingSystem:
        '''外观'''
        def __init__(self):
            self.fs = FileServer()
            self.ps = ProcessServer()
    
        def start(self):
            [i.boot() for i in (self.fs, self.ps)]
    
        def create_file(self, user, name, permissions):
            return self.fs.create_file(user, name, permissions)
    
        def create_process(self, user, name):
            return self.ps.create_process(user, name)
    
    
    def main():
        os = OperatingSystem()
        os.start()
        os.create_file('foo', 'hello', '-rw-r-r')
        os.create_process('bar', 'ls /tmp')
    
    if __name__ == '__main__':
        main()
    

2.2. 装饰器模式

  • 解释
    • 一个Python修饰器就是对Python语 法的一个特定改变,用于扩展一个类、方法或函数的行为,而无需使用继承。
  • 引入:
    ## 1、直接使用递归,函数执行时间
    def fibonacci(n):
        assert(n >= 0), 'n must be >= 0'
        return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
    
    if __name__ == '__main__':
        from timeit import Timer
        t = Timer('fibonacci(8)', 'from __main__ import fibonacci')
        print(t.timeit())
    # 结果16s多
    
    ## 使用dict缓存计算结果
    known = {0:0, 1:1}
    def fibonacci(n):
        assert(n >= 0), 'n must be >= 0'
        if n in known:
            return known[n]
        res = fibonacci(n-1) + fibonacci(n-2)
        known[n] = res
        return res
    
    if __name__ == '__main__':
        from timeit import Timer
        t = Timer('fibonacci(100)', 'from __main__ import fibonacci')
        print(t.timeit())
    # 结果:0.3s多
    
    
    ## 使用装饰器
    
    # 函数functools.wraps() 是一个为创建修饰器提供便利的函数;虽不强制,但推荐使用,
    # 因为它能保留被修饰函数的文档和签名。这种情况要求参数列表*args,因为被修饰的函数可能有 输入参数。
    import functools
    def memoize(fn):
        known = dict()
    
        @functools.wraps(fn)
        def memoizer(*args):
            if args not in known:
                known[args] = fn(*args)
            return known[args]
        return memoizer
    
  • 例子:
    # coding: utf-8
    
    import functools
    
    def memoize(fn):
        known = dict()
    
        @functools.wraps(fn)
        def memoizer(*args):
            if args not in known:
                known[args] = fn(*args)
            return known[args]
    
        return memoizer
    
    
    @memoize
    def nsum(n):
        '''返回前n个数字的和'''
        assert(n >= 0), 'n must be >= 0'
        return 0 if n == 0 else n + nsum(n-1)
    
    
    @memoize
    def fibonacci(n):
        '''返回斐波那契数列的第n个数'''
        assert(n >= 0), 'n must be >= 0'
        return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
    
    if __name__ == '__main__':
        from timeit import Timer
        measure = [{'exec': 'fibonacci(100)', 'import': 'fibonacci',
                    'func': fibonacci}, {'exec': 'nsum(200)', 'import': 'nsum',
                                         'func': nsum}]
        for m in measure:
            t = Timer('{}'.format(m['exec']), 'from __main__ import \
                {}'.format(m['import']))
            print('name: {}, doc: {}, executing: {}, time: \
                {}'.format(m['func'].__name__, m['func'].__doc__,
                           m['exec'], t.timeit()))
    

2.3. 代理模式

  • 解释:
    • 远程代理:实际存在于不同地址空间(例如,某个网络服务器)的对象在本地的代理者。
    • 虚拟代理:用于懒初始化,将一个大计算量对象的创建延迟到真正需要的时候进行。
    • 保护/防护代理:控制对敏感对象的访问。
    • 智能(引用)代理:在对象被访问时执行额外的动作。此类代理的例子包括引用计数和
      线程安全检查。
  • 代码一:懒初始化
    # coding: utf-8
    
    """
    LazyProperty类实际上是一个描述符(请参考网页[t.cn/RqrYBND])。描述符(descriptor)
    是Python中重写类属性访问方法(__get__()、__set__()和__delete__())的默认行为要使
    用的一种推荐机制。LazyProperty类仅重写了__set__(),因为这是其需要重写的唯一访问方
    法。换句话说,我们无需重写所有访问方法。__get__()方法所访问的特性值,正是下层方法想
    要赋的值,并使用setattr()来手动赋值。__get__()实际做的事情非常简单,就是使用值来
    替代方法!这意味着不仅特性是惰性加载的,而且仅可以设置一次。
    """
    
    class LazyProperty:
        def __init__(self, method):
            self.method = method
            self.method_name = method.__name__
            # print('function overriden: {}'.format(self.method))
            # print("function's name: {}".format(self.method_name))
    
        def __get__(self, obj, cls):
            if not obj:
                return None
            value = self.method(obj) # 获取obj对象的method属性的值么?
            # print('value {}'.format(value))
            setattr(obj, self.method_name, value)
            return value
    
    
    class Test:
        def __init__(self):
            self.x = 'foo'
            self.y = 'bar'
            self._resource = None
    
        @LazyProperty
        def resource(self):
            print('initializing self._resource which is: {}'.format(self._resource))
            self._resource = tuple(range(5))    # 代价大的
            return self._resource
    
    
    def main():
        t = Test()
        print(t.x)
        print(t.y)
        # 做更多的事情。。。
        print(t.resource)
        print(t.resource)
    
    if __name__ == '__main__':
        main()
    
  • python文件描述器(有些书中是文件描述符)
    • 参考:https://blog.csdn.net/hsc_1/article/details/81027546
class A:
    def __init__(self, val):
        self.val = val
    def __get__(self, instance, owner):
        print('self is ', self)
        print('instance is ', instance)
        print('owner is ', owner)
        return self.val

class B:
    a = A(10)

b = B()
print(b.a)

# descriptor
#     一个类中定义了__set__, __get__和__delete__中的一个或多个就是descriptor。
#     __get__函数定义的时候需要的参数为三个,分别为self, instance, owner。
#     __set__函数定义的时候需要的参数为三个,分别是self, instance, value。

# 理解了下面的一句话就理解了文件描述器的实质:
# self很简单,就是descriptor的实例;
# instance就是descriptor所在类中的实例;
# owner指的是descriptor所在的类。

# 还可以看出来的是,打印的b.a不是一个是实例对象,而是通过调用a.__get__()函数返回的值。

# 所以我感觉这个descriptor叫做描述器(借鉴于decorator)似乎更合适。。。

# 因为这个descriptor似乎是用来描述所在的实例的。。。

# 总结:通过实例调用(b.a)这个descriptor对象,返回的并不是这个descriptor(你可以尝试打印一个实例对象,
# 看看返回的是什么),而是该descriptor调用__get__返回的值,所以这个a像是b的描述器。


class LazyProperty(object):
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value

class A:
    @LazyProperty
    def func(self):
        time.sleep(1)
        return 10
  • 代码二:
    # coding: utf-8
    
    """
    查看用户列表:这一操作不要求特殊权限。
    添加新用户:这一操作要求客户端提供一个特殊的密码。
    """
    
    class SensitiveInfo:
        def __init__(self):
            self.users = ['nick', 'tom', 'ben', 'mike']
    
        def read(self):
            print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))
    
        def add(self, user):
            self.users.append(user)
            print('Added user {}'.format(user))
    
    class Info:
        '''SensitiveInfo的保护代理'''
    
        def __init__(self):
            self.protected = SensitiveInfo()
            self.secret = '0xdeadbeef'
    
        def read(self):
            self.protected.read()
    
        def add(self, user):
            sec = input('what is the secret? ')
            self.protected.add(user) if sec == self.secret else print("That's wrong!")
    
    def main():
        info = Info()
        while True:
            print('1. read list |==| 2. add user |==| 3. quit')
            key = input('choose option: ')
            if key == '1':
                info.read()
            elif key == '2':
                name = input('choose username: ')
                info.add(name)
            elif key == '3':
                exit()
            else:
                print('unknown option: {}'.format(key))
    
    if __name__ == '__main__':
        main()
    

2.4. 控制器模式

  • 全称:模型-视图-控制器模式
  • 解释:
    • 关注点分离(Separation of Concerns,SoC)原则是软件工程相关的设计原则之一。SoC原则背后的思想是将一个应用切分成不同的部分,每个部分解决一个单独的关注点。分层设计中的层次(数据访问层、业务逻辑层和表示层等)即是关注点的例子。
  • 代码
    """
    用户输入一个数字,然后就能看到与这个数字相关的名人名言。
    名人名言存储在一个quotes元组中。这种数据通常是存储在数据库、文件或其他地方,
    只有模型能够直接访问它。
    """
    
    quotes = ('A man is not complete until he is married. Then he is finished.',
              'As I said before, I never repeat myself.',
              'Behind a successful man is an exhausted woman.',
              'Black holes really suck...', 'Facts are stubborn things.')
    
    
    class QuoteModel:
        def get_quote(self, n):
            try:
                value = quotes[n]
            except IndexError as err:
                value = 'Not found!'
            return value
    
    
    class QuoteTerminalView:
        def show(self, quote):
            print('And the quote is: "{}"'.format(quote))
    
        def error(self, msg):
            print('Error: {}'.format(msg))
    
        def select_quote(self):
            return input('Which quote number would you like to see?')
    
    
    class QuoteTerminalController:
        def __init__(self):
            self.model = QuoteModel()
            self.view = QuoteTerminalView()
    
        def run(self):
            valid_input = False
            while not valid_input:
                n = self.view.select_quote()
                try:
                    n = int(n)
                except ValueError as err:
                    self.view.error("Incorrect index '{}'".format(n))
                else:
                    valid_input = True
            quote = self.model.get_quote(n)
            self.view.show(quote)
    
    
    def main():
        controller = QuoteTerminalController()
        while True:
            controller.run()
    
    if __name__ == '__main__':
        main()
    

2.5. 享元模式

  • 解释:
    • 享元设计模式通过为相似对象引入数据共享来最小化内存使用,提升性能
    • 一个享元(Flyweight)就是一个包含状态独立的不可变(又称固有的)数据的共享对象。依赖状态的可变(又称非固有的)数据不应是享元的一部分,因为每个对象的这种信息都不同,无法共享。如果享元需要非固有的数据,应该由客户端代码显式地提供
    • 比如:假设我们正在设计一个性能关键的游戏,例如第一人称射击(First-Person Shooter,FPS)游戏。在FPS游戏中,玩家(士兵)共享一些状态,如外在表现和行为。例如,在《反恐精英》游戏中,同一团队(反恐精英或恐怖分子)的所有士兵看起来都是一样的(外在表现)。同一个游戏中,(两个团队的)所有士兵都有一些共同的动作,比如,跳起、低头等(行为)。这意味着我们可以创建一个享元来包含所有共同的数据。当然,士兵也有许多因人而异的可变数据,这些数据不是享元的一部分,比如,枪支、健康状况和地理位置等。
  • 条件
    • 应用需要使用大量的对象。
    • 对象太多,存储/渲染它们的代价太大。一旦移除对象中的可变状态(因为在需要之时,应该由客户端代码显式地传递给享元),多组不同的对象可被相对更少的共享对象所替代。
    • 对象ID对于应用不重要。对象共享会造成ID比较的失败,所以不能依赖对象ID(那些在客户端代码看来不同的对象,最终具有相同的ID)。
  • 代码
    # coding: utf-8
    """
    构造一小片水果树的森林,小到能确保在单个终端页面中阅读整个输出。
    然而,无论你构造的森林有多大,内存分配都保持相同。
    """
    
    import random
    from enum import Enum
    
    # 枚举
    TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')
    
    
    class Tree:
        # pool是一个类属性(类的所有实例共享的一个变量),(换句话说,是我们的缓存)
        pool = dict()
    
        # 使用特殊方法__new__(这个方法在__init__之前被调用),
        # 我们把Tree类变换成一个元类,元类支持自引用。这意味着cls引用的是Tree类。
        # 当客户端要创建Tree的一个实例时,会以tree_type参数传递树的种类。
        # 树的种类用于检查是否创建过相同种类的树。如果是,则返回之前创建的对象;
        # 否则,将这个新的树种添加到池中,并返回相应的新对象
        def __new__(cls, tree_type):
            obj = cls.pool.get(tree_type, None)
            if not obj:
                obj = object.__new__(cls)  # 实例化,对象.方法(类)
                cls.pool[tree_type] = obj  # 将对象加入类的pool中
                obj.tree_type = tree_type  # 设置对象的属性
            return obj
            # 元类编程中obj是本类实例化的对象
            # cls是类
    
        # 方法render()用于在屏幕上渲染一棵树。注意,享元不知道的所有可变(外部的)信息都需要由客户端代码显式地传递。
        # 在当前案例中,每棵树都用到一个随机的年龄和一个x, y形式的位置。
        # 为了让render()更加有用,有必要确保没有树会被渲染到另一个棵之上。
        def render(self, age, x, y):
            print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))
    
    
    # main()函数展示了我们可以如何使用享元模式。一棵树的年龄是1到30年之间的一个随机值。
    # 坐标使用1到100之间的随机值。虽然渲染了18棵树,但仅分配了3棵树的内存。
    # 输出的最后一行证明当使用享元时,我们不能依赖对象的ID。函数id()会返回对象的内存地址。
    # Python规范并没有要求id()返回对象的内存地址,只是要求id()为每个对象返回一个唯一性ID,
    # 不过CPython(Python的官方实现)正好使用对象的内存地址作为对象唯一性ID。
    # 在我们的例子中,即使两个对象看起来不相同,但是如果它们属于同一个享元家族(在这里,家族由tree_type定义),那么它们实际上有相同的ID。
    # 当然,不同ID的比较仍然可用于不同家族的对象,但这仅在客户端知道实现细节的情况下才可行(通常并非如此)。
    def main():
        rnd = random.Random()
        age_min, age_max = 1, 30    # 单位为年
        min_point, max_point = 0, 100
        tree_counter = 0
    
        for _ in range(10):
            t1 = Tree(TreeType.apple_tree)
            t1.render(rnd.randint(age_min, age_max),
                      rnd.randint(min_point, max_point),
                      rnd.randint(min_point, max_point))
            tree_counter += 1
    
        for _ in range(3):
            t2 = Tree(TreeType.cherry_tree)
            t2.render(rnd.randint(age_min, age_max),
                      rnd.randint(min_point, max_point),
                      rnd.randint(min_point, max_point))
            tree_counter += 1
    
        for _ in range(5):
            t3 = Tree(TreeType.peach_tree)
            t3.render(rnd.randint(age_min, age_max),
                      rnd.randint(min_point, max_point),
                      rnd.randint(min_point, max_point))
            tree_counter += 1
    
        print('trees rendered: {}'.format(tree_counter))
        print('trees actually created: {}'.format(len(Tree.pool)))
    
        t4 = Tree(TreeType.cherry_tree)
        t5 = Tree(TreeType.cherry_tree)
        t6 = Tree(TreeType.apple_tree)
        print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
        print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))
    
    if __name__ == '__main__':
        main()
    

2.6. 适配器模式

  • 解释:
    • 适配器模式(Adapter pattern)是一种结构型设计模式,帮助我们实现两个不兼容接口之间的兼容。
    • 如果我们希望把一个老组件用于一个新系统中, 或者把一个新组件用于一个老系统中,不对代码进行任何修改两者就能够通信的情况很少见。但 又并非总是能修改代码,或因为我们无法访问这些代码(例如,组件以外部库的方式提供),或 因为修改代码本身就不切实际。在这些情况下,我们可以编写一个额外的代码层,该代码层包含 让两个接口之间能够通信需要进行的所有修改。这个代码层就叫适配器。
  • 例子:
    # 外部模块:external.py
    class Synthesizer:
        def __init__(self, name):
            self.name = name
    
        def __str__(self):
            return 'the {} synthesizer'.format(self.name)
    
        def play(self):
            return 'is playing an electronic song'
    
    
    class Human:
        def __init__(self, name):
            self.name = name
    
        def __str__(self):
            return '{} the human'.format(self.name)
    
        def speak(self):
            return 'says hello'
    
    
    # 自己的代码adapter.py
    from external import Synthesizer, Human
    
    class Computer:
        def __init__(self, name):
            self.name = name
    
        def __str__(self):
            return 'the {} computer'.format(self.name)
    
        def execute(self):
            return 'executes a program'
    
    # 客户端仅知道如何调用execute()方法,并不知道play() 4 和speak()。
    # 在不改变Synthesizer和Human类的前提下,我们该如何做才能让代码有效?适配器是救星!
    # 我们创建一个通用的Adapter类,将一些带不同接口的对象适配到一个统一接口中。
    # __init__()方法的obj参数是我们想要适配的对象,adapted_methods是一个字典,键值对中的键是客户端要调用的方法,值是应该被调用的方法。
    class Adapter:
        def __init__(self, obj, adapted_methods):
            self.obj = obj
            self.__dict__.update(adapted_methods)
    
        def __str__(self):
            return str(self.obj)
    
    
    def main():
        objects = [Computer('Asus')]
        synth = Synthesizer('moog')
        objects.append(Adapter(synth, dict(execute=synth.play)))
        human = Human('Bob')
        objects.append(Adapter(human, dict(execute=human.speak)))
    
        for i in objects:
            print('{} {}'.format(str(i), i.execute()))
    
    if __name__ == "__main__":
        main()
    

3. 行为型模式

简便记忆:观察者命令解释器将责任链、状态机做成策略模板

3.1. 观察者模式

  • 说明
    • 观察者模式描述单个对象(发布者,又称为主持者或可观察者)与一个或多个对象(订阅者,又称为观察者)之间的发布—订阅关系。
    • 发布者发生了改变,会通知订阅者
  • 代码
    # 基类Publisher,包括添加、删除及通知观察者这些公用功能。
    # 观察者们保存在列表observers中。add()方法注册一个新的观察者,或者在该观察者已存在时引发一个错误。
    # remove()方法注销一个已有观察者,或者在该观察者尚未存在时引发一个错误。
    # 最后,notify()方法则在变化发生时通知所有观察者。
    class Publisher:
        def __init__(self):
            self.observers = []
    
        def add(self, observer):
            if observer not in self.observers:
                self.observers.append(observer)
            else:
                print('Failed to add: {}'.format(observer))
    
        def remove(self, observer):
            try:
                self.observers.remove(observer)
            except ValueError:
                print('Failed to remove: {}'.format(observer))
    
        def notify(self):
            [o.exec(self) for o in self.observers]
    
    # efaultFormatter类继承自Publisher,并添加格式化程序特定的功能。
    class DefaultFormatter(Publisher):
        # __init__()做的第一件事情就是调用基类的__init__()方法
        # DefaultFormatter实例有自己的名字,这样便于我们跟踪其状态。
        def __init__(self, name):
            Publisher.__init__(self)
            self.name = name
            self._data = 0
    
        # __str__()方法返回关于发布者名称和_data值的信息。
        # type(self).__name是一种获取类名的方便技巧,避免硬编码类名。
        def __str__(self):
            return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)
    
        # @property修饰器来提供_data变量的读访问方式。这样,我们就能使用object.data来替代object.data()。
        @property
        def data(self):
            return self._data
    
        # @setter该修饰器会在每次使用赋值操作符(=)为_data变量赋新值时被调用。
        @data.setter
        def data(self, new_value):
            try:
                self._data = int(new_value)
            except ValueError as e:
                print('Error: {}'.format(e))
            else:
                self.notify()
    
    # 两个观察者:HexFormatter和BinaryFormatter的功能非常相似。
    # 唯一的不同在于如何格式化从发布者那获取到的数据值,即分别以十六进制和二进制进行格式化。
    class HexFormatter:
        def exec(self, publisher):
            print("{}: '{}' has now hex data = {}".format(type(self).__name__,
                                                          publisher.name, hex(publisher.data)))
    
    class BinaryFormatter:
        def exec(self, publisher):
            print("{}: '{}' has now bin data = {}".format(type(self).__name__,
                                                          publisher.name, bin(publisher.data)))
    
    def main():
        df = DefaultFormatter('test1')
        print(df)
    
        print()
        hf = HexFormatter()
        df.add(hf)
        df.data = 3
        print(df)
    
        print()
        bf = BinaryFormatter()
        df.add(bf)
        df.data = 21
        print(df)
    
        print()
        df.remove(hf)
        df.data = 40
        print(df)
    
        print()
        df.remove(hf)
        df.add(bf)
        df.data = 'hello'
        print(df)
    
        print()
        df.data = 15.8
        print(df)
    
    if __name__ == '__main__':
        main()
    

3.2. 命令模式

  • 说明
    • 支持撤销操作
  • 代码
    import os
    
    verbose = True
    
    # 文件改名
    class RenameFile:
        def __init__(self, path_src, path_dest):
            self.src, self.dest = path_src, path_dest
    
        def execute(self):
            if verbose:
                print("[renaming '{}' to '{}']".format(self.src, self.dest))
            os.rename(self.src, self.dest)
    
        def undo(self):
            if verbose:
                print("[renaming '{}' back to '{}']".format(self.dest, self.src))
            os.rename(self.dest, self.src)
    
    # 创建文件
    class CreateFile:
        def __init__(self, path, txt='hello world\n'):
            self.path, self.txt = path, txt
    
        def execute(self):
            if verbose:
                print("[creating file '{}']".format(self.path))
            with open(self.path, mode='w', encoding='utf-8') as out_file:
                out_file.write(self.txt)
    
        def undo(self):
            delete_file(self.path)
    
    # 读取文件不支持回滚
    class ReadFile:
        def __init__(self, path):
            self.path = path
    
        def execute(self):
            if verbose:
                print("[reading file '{}']".format(self.path))
            with open(self.path, mode='r', encoding='utf-8') as in_file:
                print(in_file.read(), end='')
    
    # 删除文件也不支持回滚
    def delete_file(path):
        if verbose:
            print("deleting file '{}'".format(path))
        os.remove(path)
    
    
    def main():
        orig_name, new_name = 'file1', 'file2'
    
        commands = []
        for cmd in CreateFile(orig_name), ReadFile(orig_name), RenameFile(orig_name, new_name):
            commands.append(cmd)
    
        [c.execute() for c in commands]
    
        answer = input('reverse the executed commands? [y/n] ')
    
        if answer not in 'yY':
            print("the result is {}".format(new_name))
            exit()
    
        for c in reversed(commands):
            try:
                c.undo()
            except AttributeError as e:
                pass
    
    if __name__ == '__main__':
        main()
    

3.3. 解释器模式

  • 说明
    • 解释器模式背后的主要思想是让非初级用户和领域专家使用一门简单的语言来表达想法
    • 个人更建议使用yaml格式来解析
  • 代码
    # coding: utf-8
    
    """
    创建一种内部DSL控制一个智能屋
    一个事件的形式为command -> receiver -> arguments。参数部分是可选的。
    并不是所有事件都要求参数。不要求任何参数的事件例子如下所示。
    open -> gate
    ->符号用于标记事件一个部分的结束,并声明下一个部分的开始。
    Pyparsing是python中的一个模块
    
    使用巴科斯-诺尔形式(Backus-Naur Form,BNF)表示法来定义语法
    event ::= command token receiver token arguments
    command ::= word+
    word ::= a collection of one or more alphanumeric characters
    token ::= ->
    receiver ::= word+
    arguments ::= word+
    
    实际的代码:
    word = Word(alphanums)
    command = Group(OneOrMore(word))
    token = Suppress("->")
    device = Group(OneOrMore(word))
    argument = Group(OneOrMore(word))
    event = command + token + device + Optional(token + argument)
    """
    
    from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums
    
    class Gate:
        def __init__(self):
            self.is_open = False
    
        def __str__(self):
            return 'open' if self.is_open else 'closed'
    
        def open(self):
            print('opening the gate')
            self.is_open = True
    
        def close(self):
            print('closing the gate')
            self.is_open = False
    
    
    class Garage:
        def __init__(self):
            self.is_open = False
    
        def __str__(self):
            return 'open' if self.is_open else 'closed'
    
        def open(self):
            print('opening the garage')
            self.is_open = True
    
        def close(self):
            print('closing the garage')
            self.is_open = False
    
    
    class Aircondition:
        def __init__(self):
            self.is_on = False
    
        def __str__(self):
            return 'on' if self.is_on else 'off'
    
        def turn_on(self):
            print('turning on the aircondition')
            self.is_on = True
    
        def turn_off(self):
            print('turning off the aircondition')
            self.is_on = False
    
    
    class Heating:
        def __init__(self):
            self.is_on = False
    
        def __str__(self):
            return 'on' if self.is_on else 'off'
    
        def turn_on(self):
            print('turning on the heating')
            self.is_on = True
    
        def turn_off(self):
            print('turning off the heating')
            self.is_on = False
    
    
    class Boiler:
        def __init__(self):
            self.temperature = 83  # in celsius
    
        def __str__(self):
            return 'boiler temperature: {}'.format(self.temperature)
    
        def increase_temperature(self, amount):
            print("increasing the boiler's temperature by {} degrees".format(amount))
            self.temperature += amount
    
        def decrease_temperature(self, amount):
            print("decreasing the boiler's temperature by {} degrees".format(amount))
            self.temperature -= amount
    
    
    class Fridge:
        def __init__(self):
            self.temperature = 2  # 单位为摄氏度
    
        def __str__(self):
            return 'fridge temperature: {}'.format(self.temperature)
    
        def increase_temperature(self, amount):
            print("increasing the fridge's temperature by {} degrees".format(amount))
            self.temperature += amount
    
        def decrease_temperature(self, amount):
            print("decreasing the fridge's temperature by {} degrees".format(amount))
            self.temperature -= amount
    
    
    def main():
        word = Word(alphanums)
        command = Group(OneOrMore(word))
        token = Suppress("->")
        device = Group(OneOrMore(word))
        argument = Group(OneOrMore(word))
        event = command + token + device + Optional(token + argument)
    
        gate = Gate()
        garage = Garage()
        airco = Aircondition()
        heating = Heating()
        boiler = Boiler()
        fridge = Fridge()
    
        tests = ('open -> gate',
                 'close -> garage',
                 'turn on -> aircondition',
                 'turn off -> heating',
                 'increase -> boiler temperature -> 5 degrees',
                 'decrease -> fridge temperature -> 2 degrees')
        open_actions = {'gate': gate.open,
                        'garage': garage.open,
                        'aircondition': airco.turn_on,
                        'heating': heating.turn_on,
                        'boiler temperature': boiler.increase_temperature,
                        'fridge temperature': fridge.increase_temperature}
        close_actions = {'gate': gate.close,
                         'garage': garage.close,
                         'aircondition': airco.turn_off,
                         'heating': heating.turn_off,
                         'boiler temperature': boiler.decrease_temperature,
                         'fridge temperature': fridge.decrease_temperature}
    
        """
        获取Pyparsing解析结果的最简单方式是使用parseString()方法,该方法返回的结果是一
        个ParseResults实例,它实际上是一个可视为嵌套列表的解析树。例如,执行print(event.
        parseString('increase -> boiler temperature -> 3 degrees'))得到的结果如下所示。
        [['increase'], ['boiler', 'temperature'], ['3', 'degrees']]
        """
        for t in tests:
            if len(event.parseString(t)) == 2:  # 没有参数
                cmd, dev = event.parseString(t)
                cmd_str, dev_str = ' '.join(cmd), ' '.join(dev)
                if 'open' in cmd_str or 'turn on' in cmd_str:
                    open_actions[dev_str]()
                elif 'close' in cmd_str or 'turn off' in cmd_str:
                    close_actions[dev_str]()
            elif len(event.parseString(t)) == 3:  # 有参数
                cmd, dev, arg = event.parseString(t)
                cmd_str, dev_str, arg_str = ' '.join(cmd), ' '.join(dev), ' '.join(arg)
                num_arg = 0
                try:
                    num_arg = int(arg_str.split()[0])  # 抽取数值部分
                except ValueError as err:
                    print("expected number but got: '{}'".format(arg_str[0]))
                if 'increase' in cmd_str and num_arg > 0:
                    open_actions[dev_str](num_arg)
                elif 'decrease' in cmd_str and num_arg > 0:
                    close_actions[dev_str](num_arg)
    
    if __name__ == '__main__':
        main()
    

3.4. 责任链模式

  • 解释
    • (1) 存在一个对象链(链表、树或任何其他便捷的数据结构)。
    • (2) 我们一开始将请求发送给链中的第一个对象。
    • (3) 对象决定其是否要处理该请求。
    • (4) 对象将请求转发给下一个对象。
    • (5) 重复该过程,直到到达链尾。
  • 例子:
    #!/usr/bin/python
    #coding:utf8
    
    class Handler:
        def successor(self, successor):
            self.successor = successor
    
    class ConcreteHandler1(Handler):
        def handle(self, request):
            if request > 0 and request <= 10:
                print("in handler1")
            else:
                self.successor.handle(request)
    
    class ConcreteHandler2(Handler):
        def handle(self, request):
            if request > 10 and request <= 20:
                print("in handler2")
            else:
                self.successor.handle(request)
    
    class ConcreteHandler3(Handler):
        def handle(self, request):
            if request > 20 and request <= 30:
                print("in handler3")
            else:
                print('end of chain, no handler for {}'.format(request))
    
    class Client:
        def __init__(self):
            h1 = ConcreteHandler1()
            h2 = ConcreteHandler2()
            h3 = ConcreteHandler3()
    
            h1.successor(h2)
            h2.successor(h3)
    
            requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
            for request in requests:
                h1.handle(request)
    
    if __name__ == "__main__":
        client = Client()
    

3.5. 状态(机)模式

  • 说明
    • 状态
    • 状态转换
    • 进程状态
  • 代码一:使用state_machine模块
    from state_machine import State, Event, acts_as_state_machine, after, before, InvalidStateTransition
    
    @acts_as_state_machine
    class Process:
        created = State(initial=True)
        waiting = State()
        running = State()
        terminated = State()
        blocked = State()
        swapped_out_waiting = State()
        swapped_out_blocked = State()
    
        wait = Event(from_states=(created, running, blocked,
                                  swapped_out_waiting), to_state=waiting)
        run = Event(from_states=waiting, to_state=running)
        terminate = Event(from_states=running, to_state=terminated)
        block = Event(from_states=(running, swapped_out_blocked),
                      to_state=blocked)
        swap_wait = Event(from_states=waiting, to_state=swapped_out_waiting)
        swap_block = Event(from_states=blocked, to_state=swapped_out_blocked)
    
        def __init__(self, name):
            self.name = name
    
        @after('wait')
        def wait_info(self):
            print('{} entered waiting mode'.format(self.name))
    
        @after('run')
        def run_info(self):
            print('{} is running'.format(self.name))
    
        @before('terminate')
        def terminate_info(self):
            print('{} terminated'.format(self.name))
    
        @after('block')
        def block_info(self):
            print('{} is blocked'.format(self.name))
    
        @after('swap_wait')
        def swap_wait_info(self):
            print('{} is swapped out and waiting'.format(self.name))
    
        @after('swap_block')
        def swap_block_info(self):
            print('{} is swapped out and blocked'.format(self.name))
    
    def transition(process, event, event_name):
        try:
            event()
        except InvalidStateTransition as err:
            print('Error: transition of {} from {} to {} failed'.format(process.name,
                                                                        process.current_state, event_name))
    def state_info(process):
        print('state of {}: {}'.format(process.name, process.current_state))
    
    def main():
        RUNNING = 'running'
        WAITING = 'waiting'
        BLOCKED = 'blocked'
        TERMINATED = 'terminated'
    
        p1, p2 = Process('process1'), Process('process2')
        [state_info(p) for p in (p1, p2)]
    
        print()
        transition(p1, p1.wait, WAITING)
        transition(p2, p2.terminate, TERMINATED)
        [state_info(p) for p in (p1, p2)]
    
        print()
        transition(p1, p1.run, RUNNING)
        transition(p2, p2.wait, WAITING)
        [state_info(p) for p in (p1, p2)]
    
        print()
        transition(p2, p2.run, RUNNING)
        [state_info(p) for p in (p1, p2)]
    
        print()
        [transition(p, p.block, BLOCKED) for p in (p1, p2)]
        [state_info(p) for p in (p1, p2)]
    
        print()
        [transition(p, p.terminate, TERMINATED) for p in (p1, p2)]
        [state_info(p) for p in (p1, p2)]
    
    if __name__ == '__main__':
        main()
    
  • 代码二:原生得实现
    """
    The state pattern allows us to change an object's behavior at runtime
    — something that Python naturally excels at!
    This example has a very simple radio. It has an AM/FM toggle switch,
    and a scan button to scan to the next station.
    """
    
    class State(object):
       """Base state. This is to share functionality"""
    
       def scan(self):
           """Scan the dial to the next station"""
           self.pos += 1
           if self.pos == len(self.stations):
               self.pos = 0
           print "Scanning… Station is", self.stations[self.pos], self.name
    
    class AmState(State):
       def __init__(self, radio):
           self.radio = radio
           self.stations = ["1250", "1380", "1510"]
           self.pos = 0
           self.name = "AM"
    
       def toggle_amfm(self):
           print "Switching to FM"
           self.radio.state = self.radio.fmstate
    
    class FmState(State):
       def __init__(self, radio):
           self.radio = radio
           self.stations = ["81.3", "89.1", "103.9"]
           self.pos = 0
           self.name = "FM"
    
       def toggle_amfm(self):
           print "Switching to AM"
           self.radio.state = self.radio.amstate
    
    class Radio(object):
       """A radio.
       It has a scan button, and an AM/FM toggle switch."""
    
       def __init__(self):
           """We have an AM state and an FM state"""
    
           self.amstate = AmState(self)
           self.fmstate = FmState(self)
           self.state = self.amstate
    
       def toggle_amfm(self):
           self.state.toggle_amfm()
       def scan(self):
           self.state.scan()
    
    # Test our radio out
    radio = Radio()
    actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
    actions = actions * 2
    for action in actions:
       action()
    
  • 参考监控系统中底层网路的架构代码

3.6. 策略模式

  • 说明
    • 根据不同的场景、特征或者变量里决定使用哪种策略
  • 代码
    # coding: utf-8
    # 检测一个字符串中所有的字符串是否都是唯一的
    
    import time
    SLOW = 3  # 单位为秒
    LIMIT = 5   # 字符数
    WARNING = 'too bad, you picked the slow algorithm :('
    
    
    def pairs(seq):
        n = len(seq)
        for i in range(n):
            yield seq[i], seq[(i + 1) % n]
    
    # 方法一:排序,然后判断相邻两个元素是否相等
    def allUniqueSort(s):
        if len(s) > LIMIT:
            print(WARNING)
            time.sleep(SLOW)
        srtStr = sorted(s)
        for (c1, c2) in pairs(srtStr):
            if c1 == c2:
                return False
        return True
    
    # 方法二:利用python中的集合来处理
    def allUniqueSet(s):
        if len(s) < LIMIT:
            print(WARNING)
            time.sleep(SLOW)
        return True if len(set(s)) == len(s) else False
    
    
    def allUnique(s, strategy):
        return strategy(s)
    
    
    def main():
        while True:
            word = None
            while not word:
                word = input('Insert word (type quit to exit)> ')
                if word == 'quit':
                    print('bye')
                    return
    
                strategy_picked = None
                strategies = {'1': allUniqueSet, '2': allUniqueSort}
                while strategy_picked not in strategies.keys():
                    strategy_picked = input('Choose strategy: [1] Use a set, [2] Sort and pair> ')
    
                    try:
                        strategy = strategies[strategy_picked]
                        print('allUnique({}): {}'.format(word, allUnique(word, strategy)))
                    except KeyError as err:
                        print('Incorrect option: {}'.format(strategy_picked))
    
    if __name__ == '__main__':
        main()
    

3.7. 模板模式

  • 说明
    • 减少代码的冗余性
    • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 代码
    """
    将一段文本发送给一个函数,该函数要生成一个包含该文本的横幅。
    横幅有多种风格,比如点或虚线围绕文本。横幅生成器有一个默认风格,
    但应该能够使用我们自己提供的风格。
    """
    
    from cowpy import cow
    
    def dots_style(msg):
        msg = msg.capitalize()
        msg = '.' * 10 + msg + '.' * 10
        return msg
    
    def admire_style(msg):
        msg = msg.upper()
        return '!'.join(msg)
    
    def cow_style(msg):
        msg = cow.milk_random_cow(msg)
        return msg
    
    def generate_banner(msg, style=dots_style):
        print('-- start of banner --')
        print(style(msg))
        print('-- end of banner --\n\n')
    
    
    def main():
        msg = 'happy coding'
        [generate_banner(msg, style) for style in (dots_style, admire_style, cow_style)]
    
    if __name__ == '__main__':
        main()
    

参考:
《精通python设计模式》
《二十三种设计模式及其python实现》

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值