0

【Python】设计模式

 3 years ago
source link: https://www.guofei.site/2019/10/26/design_patterns.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【Python】设计模式

2019年10月26日

Author: Guofei

文章归类: 设计模式 ,文章编号: 1004


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2019/10/26/design_patterns.html

Edit

设计模式是开发者和架构师的宝贵经验,是具有泛用性的方法论。

单例模式

是什么?确保类有且只有一个对象

用途:
数据库、打印机、导入库等只要一个实例的场景 缺点:

  1. 对象可能在某个地方被误改,但开发人员并不知道
  2. 可能会提高耦合性(类似全局变量)

实现

class A(object):
    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(A, cls).__new__(cls)
        return cls.instance


obj1 = A()
obj2 = A()
obj1 is obj2 # 返回 True

有点儿难理解,分析如下:

  • 创建对象时,先调用__new__,由 __new__ 调用 __init__. 所以一般建对象时,不写 __new__.

升级用法是 懒汉实例化(有空再补)

工厂模式

简单工厂模式

from abc import ABCMeta, abstractmethod


class Animal(metaclass=ABCMeta):
    @abstractmethod
    def do_say(self):
        pass


class Dog(Animal):
    def do_say(self):
        print('Bhow Bhow!!')


class Cat(Animal):
    def do_say(self):
        print('Meow Meow!!')


class ForestFactory(object):
    def make_sound(self, object_type):
        return eval(object_type)().do_say()


ff = ForestFactory()
ff.make_sound('Cat')
ff.make_sound('Dog')
  • Animal 接口,创建两种产品 CatDog
  • ForestFactory 是一个工厂,运行时创建适当的 Animal 实例,并输出正确的声音

工厂模式的好处:松耦合,客户端不需要考虑传递哪些参数,或实例化哪些类

工厂方法

from abc import ABCMeta, abstractmethod


class Section(metaclass=ABCMeta):
    @abstractmethod
    def describe(self):
        pass


class PersonalSection(Section):
    def describe(self):
        print('Personal Section')


class AlbumSection(Section):
    def describe(self):
        print('Album Section')


class PatentSection(Section):
    def describe(self):
        print('Patent Section')


class PublicationSection(Section):
    def describe(self):
        print('Publication Section')


# %%
class Profile(metaclass=ABCMeta):
    def __init__(self):
        self.sections = []
        self.create_profile()

    @abstractmethod
    def create_profile(self):
        pass

    def get_sections(self):
        return self.sections

    def add_sections(self, section):
        self.sections.append(section)


class LinkedIn(Profile):
    def create_profile(self):
        self.add_sections(PersonalSection())
        self.add_sections(PatentSection())
        self.add_sections(PublicationSection())


class Facebook(Profile):
    def create_profile(self):
        self.add_sections(PersonalSection())
        self.add_sections(AlbumSection())

适配器模式

如果我们调用接口时,不能修改接口名,那么可以使用适配器模式。

例如,我们需要调用以下3个类:

class Computer:
    def __init__(self, name):
        self.name = name

    def execute(self):
        print('{} execute a program'.format(self.name))


class MusicPlayer:
    def __init__(self, name):
        self.name = name

    def execute(self):
        print('{} is playing a song'.format(self.name))


class Human:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print('{} says hello'.format(self.name))

旧系统是为了 Computer 类设计的,因此客户端只知道如何调用 execute() 方法,而不知道 player(), speak(),并且不允许改变 MusicPlayer, Human 这两个类。这样就可以使用适配器模式。

class Adapter:
    def __init__(self, obj, adapted_methods):
        self.obj = obj
        self.__dict__.update(adapted_methods)

    def __repr__(self):
        return self.obj

如何使用呢?

computer = Computer('MacBook')
music_player = MusicPlayer('iphone')
human = Human('jobs')

objs = []
objs.append(Adapter(computer, dict(execute=computer.execute)))
objs.append(Adapter(music_player, dict(execute=music_player.play)))
objs.append(Adapter(human, dict(execute=human.speak)))

# 调用方法就统一起来了

for i in objs:
    i.execute()

代理模式

class You:
    def __init__(self):
        print('You:: Lets buy the shirt')
        self.debitCard = DebitCard()
        self.isPurchased = None

    def make_payment(self):
        self.isPurchased = self.debitCard.do_pay()
        if self.isPurchased:
            print('You:: Wow! The shirt is mine')
        else:
            print('You:: I should earn more :(')


class Payment:
    def do_pay(self):
        pass


class Bank(Payment):
    def __init__(self):
        self.card = None
        self.acount = None

    def __getAccount(self):
        self.acount = self.card
        return self.acount

    def __hasFunds(self):
        print('Bank:: Checking if Account', self.__getAccount(), 'has enough funds')
        return True

    def setCard(self, card):
        self.card = card

    def do_pay(self):
        if self.__hasFunds():
            print('Bank:: Paying the merchant')
            return True
        else:
            print('Bank:: Sorry, not enough funds!')
            return False


class DebitCard(Payment):
    def __init__(self):
        self.bank = Bank()

    def do_pay(self):
        card = input('Proxy:: Input your Card Number')
        self.bank.setCard(card)
        return self.bank.do_pay()


you = You()
you.make_payment()
  • Payment 是一个抽象基类,代表一个接口
  • Bank 类是一个真实主题
  • DebitCard 是真实主题 Bank 的代理

代理模式的优点

  • 可以缓存笨重的对象(或需要反复访问的对象),提高性能
  • 提供对真实主题的访问授权(权限合适的情况下)
  • 便于网络连接、远程服务器之类的

观察者模式

class Subject:
    def __init__(self):
        self.__observers = []

    def register(self, observers):
        self.__observers.append(observers)

    def notifyAll(self, *arg, **kwargs):
        for observer in self.__observers:
            observer.notify(self, *arg, **kwargs)


class Observer1:
    def __init__(self, subject):
        subject.register(self)

    def notify(self, subject, *args, **kwargs):
        print(self, 'got', args, kwargs, 'from', subject)


class Observer2:
    def __init__(self, subject):
        subject.register(self)

    def notify(self, subject, *args, **kwargs):
        print(self, 'got', args, kwargs, 'from', subject)


subject = Subject()
observer1 = Observer1(subject)
observer2 = Observer2(subject)

subject.notifyAll('notification', k=5)

优点:松耦合

  • 使得测试、维护更简单
  • 主题对观察者有一个特定的接口。不需要了解特定的观察者类
  • 可以随时增、删任意观察者
  • 观察者与主题没有绑定在一起。观察者可以在其他地方重复使用

命令模式

命令模式最常见的案例是对操作(撤销、重做)封装成一个对象。

下面是重命名、删除、新建操作的命令模式。
(为了阅读和测试方便,不真的实现这些操作,而是用print代替)

class RenameFile:
    def __init__(self, path_src, path_dst):
        self.path_src, self.path_dst = path_src, path_dst

    def do(self):
        print('rename {} {}'.format(self.path_src, self.path_dst))

    def undo(self):
        print('rename {} {}'.format(self.path_dst, self.path_src))


class DeleteFile:
    def __init__(self, path):
        self.path = path

    def do(self):
        print('mv {} .trash/{}'.format(self.path, self.path))

    def undo(self):
        print('mv .trach/{} {}'.format(self.path, self.path))


class CreateFile:
    def __init__(self, path):
        self.path = path

    def do(self):
        print('mkdir {}'.format(self.path))

    def undo(self):
        print('rmdir {}'.format(self.path))


commands = [CreateFile('file1'), DeleteFile('file1'), CreateFile('file2'), RenameFile('file2', 'file1')]

for cmd in commands:
    cmd.do()

answer = input('undo all the commands?')
if answer in 'Yy':
    for cmd in commands[::-1]:
        cmd.undo()

命令模式不只是适用于撤销操作。还可以:

  • 组合一连串命令
  • 某个操作失败,则全部回滚

模版方法–封装算法

from abc import ABCMeta, abstractmethod


class Trip(metaclass=ABCMeta):
    @abstractmethod
    def setTransport(self):
        pass

    @abstractmethod
    def day1(self):
        pass

    @abstractmethod
    def day2(self):
        pass

    @abstractmethod
    def day3(self):
        pass

    @abstractmethod
    def returnHome(self):
        pass

    def itinerary(self):
        self.setTransport()
        self.day1()
        self.day2()
        self.day3()
        self.returnHome()


class BeijingTrip(Trip):
    def setTransport(self):
        print('坐高铁到北京')

    def day1(self):
        print('去南锣鼓巷')

    def day2(self):
        print('去香山游玩')

    def day3(self):
        print('去爬长城')

    def returnHome(self):
        print('坐高铁回家')


class ShanghaiTrip(Trip):
    def setTransport(self):
        print('坐飞机去上海')

    def day1(self):
        print('去迪士尼')

    def day2(self):
        print('去东方明珠')

    def day3(self):
        print('去南京路')

    def returnHome(self):
        print('坐飞机回家')


class TravelAgency:
    def arrange_trip(self):
        choice = input('北京 or 上海?')
        if choice == '北京':
            self.trip = BeijingTrip()
        if choice == '上海':
            self.trip = ShanghaiTrip()
        self.trip.itinerary()


TravelAgency().arrange_trip()

状态设计模式

from abc import ABCMeta, abstractmethod


class State(metaclass=ABCMeta):
    @abstractmethod
    def Handle(self):
        pass


class ConcreteStateA(State):
    def Handle(self):
        print('ConcreteStateA')


class ConcreteStateB(State):
    def Handle(self):
        print('ConcreteStateB')


class Context(State):
    def __init__(self):
        self.state = None

    def getState(self):
        return self.state

    def setState(self, state):
        self.state = state

    def Handle(self):
        self.state.Handle()


context = Context()
stateA = ConcreteStateA()
stateB = ConcreteStateB()

context.setState(stateA)
context.Handle()
  • 规避if-else, switch-case
  • 容易添加状态
  • 类爆炸。当情况越来越多,类也就是越来越多

反模式

不良设计。

  • 不动性。应用程序非常难以重用
  • 刚性。任何小的改动都会导致其他部分很多改动
  • 脆弱性。任何小的更改都可能导致整个系统崩溃
  • 粘滞性。架构层面的更改非常困难,因此修改必须由开发人员在代码或环境本身中进行。

软件开发反模式

  • 开发人员的想法随着开发过程的推进而变化
  • 用例随着客户的反馈而更改
  • 最初设计的数据结构随着功能方便的考虑而变换
  • 意大利面条式代码
  • 金锤:任何问题使用同一个解决方案
    • 不了解技术的高层或技术领袖
    • 锤子多次验证有效,但这次不同
    • 公司被金锤绑架,已经投资了大量资源。员工已经使用顺手。
  • 熔岩流。大量的死代码,人们害怕一旦对其进行修改,就会破坏其他东西。久而久之,这段代码就固定下来,像熔岩凝固一样。
    • 大量试错代码
    • 一人编写的代码,未经过代码审查,随意地移交

软件架构中的反模式

重新发明轮子

供应商套牢

参考资料

【印】Chetan Giridhar:《Python 设计模式》, 中国工信出版集团


您的支持将鼓励我继续创作!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK