Python 设计模式(第2版) -- 第四部分(其他设计模式)

Python 设计模式(第2版)

最后介绍下其他设计模式。

模型—视图—控制器(MVC)-- 复合模式

根据 GoF 的定义,“复合模式将两个或更多模式组合成解决常见或普遍性问题的解决方案”。复合模式不是同时使用的一组模式,而是一个问题的通用解决方案。

MVC 不仅是一种实现用户界面的软件模式,同时也是一种易于修改和维护的架构。通常来说,MVC 模式将应用程序分为3个基本部分:模型、视图和控制器。

MVC 模式的工作机制为:模型提供数据和业务逻辑(如何存储和查询信息),视图负责数据的展示(如何呈现),而控制器是两者之间的粘合剂,根据用户要求的呈现方式来协调模型和视图。

MVC 设计模式使用以下术语——模型、视图、控制器和客户端。

  • 模型:声明一个存储和操作数据的类。
  • 视图:声明一个类来构建用户界面和显示数据。
  • 控制器:声明一个连接模型和视图的类。
  • 客户端:声明一个类,根据某些操作来获得某些结果。

MVC 模式经常用于以下情况。

  • 当需要更改展示方式而不更改业务逻辑时。
  • 多个控制器可用于使用多个视图来更改用户界面上的展示。
  • 再次重申,当模型改变时,视图无需改动,因为它们是相互独立的。

简而言之,MVC 模式的主要意图如下。

  • 将数据和数据的展示隔离开来。
  • 使类的维护和实现更加简单。
  • 灵活地改变数据的存储和显示方式。两者都是独立的,因此可以灵活修改。

为了实现了一个 MVC 设计模式,假设我们想要开发一个应用程序,告诉用户云公司所提供的营销服务,包括电子邮件、短信和语音设施。

首先要开发 model 类(模型),定义产品提供的服务,即电子邮件、短信和语音。然后,来定义 view 类(视图),它提供了将信息反馈给客户端的方法。这些方法是 list_services() 和 list_ pricing()。接下来,开始定义 Controller 类,这个类定义了两个方法,即 get_services() 和 get_pricing()。这两个方法都是用来查询模型并获取数据的,然后将数据馈送到视图,从而展示给用户。Client 类将实例化控制器,然后控制器对象就会根据客户端的请求来调用适当的方法。

class Model(object):

    services = {
        "email": {"number": 1000, "price": 2,},
        "sms": {"number": 1000, "price": 10,},
        "voice": {"number": 1000, "price": 15,},
    }


class View(object):

    def list_services(self, services):
        for svc in services:
            print(svc, " ")

    def list_pricing(self, services):
        for svc in services:
            print("For", Model.services[svc]["number"], svc, "message you pay $", Model.services[svc]["price"],)


class Controller(object):

    def __init__(self):
        self.model = Model()
        self.view = View()

    def get_services(self):
        services = self.model.services.keys()
        return self.view.list_services(services)

    def get_pricing(self):
        services = self.model.services.keys()
        return self.view.list_pricing(services)


class Client(object):
    controller = Controller()
    print("Services Provided:")
    controller.get_services()
    print("Pricing for Services:")
    controller.get_pricing()


Client()

以下是MVC模式的优点。

  • 使用 MVC,开发人员可以将软件应用程序分为 3 个主要部分:模型、视图和控制器。这有助于提高可维护性,强制松耦合,并降低复杂性。
  • MVC 允许对前端进行独立更改,而对后端逻辑无需任何修改或只需进行很少的更改,因此开发工作仍可以独立运行。
  • 类似地,可以更改模型或业务逻辑,而无需对视图进行任何更改。
  • 此外,可以更改控制器,而不会对视图或模型造成任何影响。
  • MVC 还有助于招聘具有特定能力的人员,例如平台工程师和UI工程师,他们可以在自己的专业领域独立工作。

状态设计模式 – 状态设计模式

状态模式也属于行为模式的范畴,在此模式中,一个对象可以基于其内部状态封装多个行为。状态模式也可以看作是在运行时改变对象行为的一种方式。

状态设计模式在 3 个主要参与者的协助下工作。

  • State:这被认为是封装对象行为的接口。这个行为与对象的状态相关联。
  • ConcreteState:这是实现 State 接口的子类。ConcreteState 实现与对象的特定状态相关联的实际行为。
  • Context:这定义了客户感兴趣的接口。Context 还维护一个 ConcreteState 子类的实例,该子类在内部定义了对象的特定状态的实现。

考察一个状态设计模式的真实用例。例如,以一个计算机系统(台式机/笔记本电脑)为例:它可以有多个状态,如开机、关机、挂起或休眠。想利用状态设计模式来表述这些状态,看看如何处理。

首先,不妨从 ComputerState 接口开始入手:state 应定义两个属性,它们是 name 和 allowed。属性 name 表示对象的状态,而属性 allowed 是定义允许进入的状态的对象的列表;state 必须定义一个 switch() 方法,由它来实际改变对象(在这种情况下是计算机)的状态。

下面,来考察实现了 State 接口的 ConcreteState。On:这将打开计算机。这时候允许的状态是 Off、Suspend 和 Hibernate。Off:这将关闭计算机。这时候允许的状态只有 On。Hibernate:该状态将计算机置于休眠模式。当计算机处于这种状态时,只能执行打开操作。Suspend:该状态将使计算机挂起,一旦计算机挂起,就可以执行打开操作。

现在,来考察 context 类(Computer)。上下文需要做两个主要的事情:__init__():该方法定义了计算机的基本状态。change():该方法将更改对象的状态,但是行为的实际更改是由 ConcreteState 类实现(on、off、suspend 和 hibernate)的。

class ComputerState(object):
    name = "state"
    allowed = []

    def switch(self, state):
        if state.name in self.allowed:
            print('Current:', self, ' => switched to new state', state.name)
            self. __class__ = state
        else:
            print('Current:', self, ' => switching to', state.name, 'not possible.')

    def __str__(self):
        return self.name

class Off(ComputerState):
    name = "off"
    allowed = ['on']

class On(ComputerState):
    name = "on"
    allowed = ['off', 'suspend', 'hibernate']

class Suspend(ComputerState):
    name = "suspend"
    allowed = ['on']

class Hibernate(ComputerState):
    name = "hibernate"
    allowed = ['on']

class Computer(object):

    def __init__(self, model='HP'):
        self.model = model
        self.state = Off()

    def change(self, state):
        self.state.switch(state)

if __name__ == "__main__":
    comp = Computer()
    # Switch on
    comp.change(On)
    # Switch off
    comp.change(Off)

    # Switch on again
    comp.change(On)
    # Suspend
    comp.change(Suspend)
    # Try to hibernate - cannot!
    comp.change(Hibernate)
    # switch on back
    comp.change(On)
    # Finally off
    comp.change(Off)

下面是状态设计模式的优点。

  • 在状态设计模式中,对象的行为是其状态的函数结果,并且行为在运行时根据状态而改变。这消除了对 if/else 或 switch/case 条件逻辑的依赖。例如,在电视远程遥控的场景中,我们还可以通过简单地写一个类和方法来实现相应的行为,但是该类和方法将用到参数,并使用 if/else 语句块来执行具体操作(打开/关闭电视)。
  • 使用状态模式,实现多态行为的好处是显而易见的,并且更易于添加状态来支持额外的行为。
  • 状态设计模式还提高了聚合性,因为特定于状态的行为被聚合到 ConcreteState 类中,并且放置在代码中的同一个地方。
  • 使用状态设计模式,通过只添加一个 ConcreteState 类来添加行为是非常容易的。因此,状态模式不仅改善了扩展应用程序行为时的灵活性,而且全面提高了代码的可维护性。

状态模式的不足:

  • 类爆炸:由于每个状态都需要在 ConcreteState 的帮助下定义,因此我们可能导致创建了太多功能较为单一的类。我们不妨考虑有限状态机的情况——如果有许多状态,但每个状态与另一个状态没有太大不同,我们仍然需要将它们写成单独的 ConcreteState 类。这既增加了代码量,又使得状态机的结构更加难以审查。
  • 随着每个新行为的引入(即使添加行为只是添加一个 ConcreteState),Context 类都需要进行相应的更新以处理每个行为。这使得上下文行为更容易受到每个新的行为的影响。

反模式

反模式是处理重复出现问题的某些解决方案的后果。

反模式可以分为两大类。1.软件开发反模式。2.软件架构反模式。

软件开发反模式

下面,将列举软件开发中的几种反模式。

1.意大利面条式代码

以特殊的方式开发结构,软件控制流会变得错综复杂。

意大利面条式代码的典型成因包括:

  • 对面向对象编程和分析的无知;
  • 没有考虑产品的架构或设计;
  • 快餐式思维。

当遭遇意大利面条式代码时,就会面临下列问题:

  • 结构的重用性将会降到最低;
  • 维护工作量过高;
  • 进行修改时,扩展性和灵活性会降低。

2.金锤

由于某个解决方案(技术、设计或模块)在多个项目中效果不错,所以就把它推广到更多的地方。一把锤子搞定所有的钉子(解决所有问题)。

之所以出现金锤,典型的原因有如下几个:

  • 来自不了解具体问题的高层(架构师或技术领袖)的建议;
  • 虽然某解决方案在过去多次验证有效,但当前项目却具有不同的背景和要求;
  • 公司已经被这种技术“绑架”了,因为他们已经在它上面投资了大量资金来培训员工,或员工们对这种技术情有独钟,因为已经用顺手了。

3.熔岩流

这个反模式与软件应用程序中的死代码或一段用不到的代码有关,随着时间的流逝,这段代码会一直留在软件中并固化其位置,就像熔岩变成硬岩一样。

熔岩流的成因包括以下几个:

  • 在产品中有大量的试错代码;
  • 由一个人单独编写的代码,未经审查,就在没有任何培训的情况下移交给了其他开发团队;
  • 软件架构或设计的初始思想是通过代码库实现的,但没有人能理解它。

熔岩流的症状如下:

  • 开发的测试工作(如果完成的话)具有很低的代码覆盖率;
  • 代码中含有莫名奇妙的注释;
  • 过时的接口,或开发人员需要围绕既有代码展开工作;

4.复制粘贴或剪切粘贴式编程

这是最常见的反模式之一。有些开发人员为了提高开发进度,经常会原封不动地复制这些片段,并用于自己的应用程序中。

导致复制粘贴或剪切粘贴式编程的原因如下:

  • 新手开发者不习惯编写代码或不知道如何开发代码;
  • 快速修复bug或“急就章”式的开发;
  • 代码重复,无法满足跨模块标准化以及代码结构化的要求;
  • 缺乏长远打算或深谋远虑。

剪切粘贴或复制粘贴式编程的后果包括:

  • 多个软件应用程序存在同种类型的问题;
  • 维护成本会更高,同时bug的生命周期也会变得更长;
  • 较少的模块化代码库,相同的代码会散落于多处;
  • 继承问题。

软件架构反模式

1.重新发明轮子

重新审视相同的问题并为它重新设计解决方案并没有什么意义,这基本上就是重新发明轮子。

导致重新发明轮子的原因如下:

  • 缺乏中央文档或存储库来讲解架构级问题和存放已实现的解决方案;
  • 社区或公司内的技术领袖之间缺乏沟通;
  • 组织中遵循的流程是从头开始构建的,通常情况下,这样的流程是不成熟的,并且流程的实现通常是不严谨的,并且很难坚持。

这种反模式的后果如下所示:

  • 解决一个标准问题的解决方案太多,其中许多解决方案考虑得并不周全;
  • 会耗费工程团队更多的时间和资源,导致预算超标,上市时间延后;
  • 封闭的系统架构(仅适用于一种产品的架构)、重复劳动和糟糕的风险管理。

2.供应商套牢

正如这种反模式的名称所示,产品公司往往依赖于供应商提供的某些技术。这些技术对于他们的系统来说如此密不可分,以至于系统很难摆脱这些技术。

以下是导致供应商锁定的原因:

  • 熟悉供应商公司的权威人士以及技术采购的可能折扣;
  • 基于营销和销售业务而不是技术评估选择的技术;
  • 在当前项目中使用经过验证的技术(验证表明,使用此技术的投资回报率非常高),即使它不适合当前项目的需要或要求;
  • 技术人员/开发人员已经接受过相关技术的培训。

供应商锁定的后果如下所示:

  • 公司产品的发布周期和维护周期直接取决于供应商的发布时间;
  • 该产品是围绕该技术而不是根据客户的要求开发的;
  • 产品上市时间不可靠,不能满足客户的期望。

3.委员会设计

有时,根据组织中的流程,一群人会坐在一起来设计特定的系统,所得到的软件架构通常是复杂的或不合格的。因为这涉及太多的思维过程,并且这些设计思想可能是由没有相应的技能或相应产品设计经验的技术专家所提出的。

委员会设计的原因如下:

  • 根据组织的流程,产品的架构或设计是由众多的利益相关者批准的;
  • 没有指定单独的联系人或负责设计的架构师;
  • 由营销或技术专家确定设计优先级,而不是由客户反馈来确定。

与该模式有关的症状如下所示:

  • 开发人员和架构师之间的观点冲突,即使在设计完成后依旧如此;
  • 过于复杂的设计,很难记录;
  • 规格或设计的任何改动都需要经过多次审查,导致实现延迟。
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值