做服务的网站吗,企业网站建设的策划书,免费php空间,广州网站开发小程序Python 设计模式(第2版)
再介绍下行为型设计模式。
行为型模式#xff0c;顾名思义#xff0c;它主要关注的是对象的责任。它们用来处理对象之间的交互#xff0c;以实现更大的功能。行为型模式建议#xff1a;对象之间应该能够彼此交互#xff0c;同时还应该是松散耦合…Python 设计模式(第2版)
再介绍下行为型设计模式。
行为型模式顾名思义它主要关注的是对象的责任。它们用来处理对象之间的交互以实现更大的功能。行为型模式建议对象之间应该能够彼此交互同时还应该是松散耦合的。
观察者设计模式是最简单的行为型模式之一所以首先来看看观察者设计模式。
观察者模式 – 了解对象的情况
观察者模式的主要目标如下
它定义了对象之间的一对多的依赖关系从而使得一个对象中的任何更改都将自动通知给其他依赖对象它封装了主题的核心组件。
观察者模式可用于以下多种场景
在分布式系统中实现事件服务用作新闻机构的框架股票市场也是观察者模式的一个大型场景。
下面是观察者设计模式的 Python 实现
class Subject:def __init__(self):self. __observers []def register(self, observer):self. __observers.append(observer)def notifyAll(self, *args, **kwargs):for observer in self. __observers:observer.notify(self, *args, **kwargs)class Observer1:def __init__(self, subject):subject.register(self)def notify(self, subject, *args):print(type(self). __name__, :: Got, args, From, subject)class Observer2:def __init__(self, subject):subject.register(self)def notify(self, subject, *args):print(type(self). __name__, :: Got, args, From, subject)subject Subject()
observer1 Observer1(subject)
observer2 Observer2(subject)
subject.notifyAll(notification)观察者模式有 3 个主要角色主题Subject观察者Observer具体观察者ConcreteObserver。
这里将以新闻机构为例来展示观察者模式的现实世界场景。新闻机构通常从不同地点收集新闻并将其发布给订阅者。
主题的行为由 NewsPublisher 类表示 NewsPublisher 提供了一个供订户使用的接口 attach() 方法供观察者Observer来注册 NewsPublisherObserverdetach() 方法用于注销subscriber() 方法返回已经使用 Subject 注册的所有订户的列表notifySubscriber() 方法可以用来遍历已向 NewsPublisher 注册的所有订户发布者可以使用 addNews() 方法创建新消息getNews() 用于返回最新消息并通知观察者。
现在来讨论观察者Observer接口在这个例子中Subscriber 表示 Observer它是一个抽象的基类代表其他 ConcreteObserverSubscriber 有一个 update() 方法但是它需要由 ConcreteObservers 实现update() 方法是由 ConcreteObserver 实现的这样只要有新闻发布的时候它们都能得到 Subject (NewsPublishers)的相应通知。
本例有两个主要观察者分别是实现订户接口的 EmailSubscriber 和 SMSSubscriber还建立了另一个观察者 AnyOtherObserver它是用来演示 Observers 与 Subject 的松散耦合关系的 每个具体观察者的 __init__() 方法都是使用 attach() 方法向 NewsPublisher 进行注册的具体观察者的 update() 方法由 NewsPublisher 在内部用来通知添加了新的新闻。
from abc import ABCMeta, abstractmethodclass NewsPublisher:def __init__(self):self. __subscribers []self. __latestNews Nonedef attach(self, subscriber):self. __subscribers.append(subscriber)def detach(self):return self. __subscribers.pop()def subscribers(self):return [type(x). __name__ for x in self. __subscribers]def notifySubscribers(self):for sub in self. __subscribers:sub.update()def addNews(self, news):self. __latestNews newsdef getNews(self):return Got News:, self. __latestNewsclass Subscriber(metaclassABCMeta):abstractmethoddef update(self):passclass SMSSubscriber:def __init__(self, publisher):self.publisher publisherself.publisher.attach(self)def update(self):print(type(self). __name__, self.publisher.getNews())class EmailSubscriber:def __init__(self, publisher):self.publisher publisherself.publisher.attach(self)def update(self):print(type(self). __name__, self.publisher.getNews())class AnyOtherSubscriber:def __init__(self, publisher):self.publisher publisherself.publisher.attach(self)def update(self):print(type(self). __name__, self.publisher.getNews())if __name__ __main__:news_publisher NewsPublisher()for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:Subscribers(news_publisher)print(\nSubscribers:, news_publisher.subscribers())news_publisher.addNews(Hello World! )news_publisher.notifySubscribers()print(\nDetached:, type(news_publisher.detach()). __name__)print(\nSubscribers:, news_publisher.subscribers())news_publisher.addNews(My second news! )news_publisher.notifySubscribers()客户端为 NewsPublisher 创建一个对象以供具体观察者用于各种操作使用发布者的对象初始化 SMSSubscriber、EmailSubscriber 和 AnyOtherSubscriber 类在 Python 中当我们创建对象时__init__() 方法就会被调用。在 ConcreteObserver 类中__init__() 方法在内部使用 NewsPublisher 的 attach() 方法进行注册以获取新闻更新然后我们打印出已经通过主题注册的所有订户具体观察者的列表接着使用 newsPublisher(news_publisher)的对象通过 addNews() 方法创建新消息NewsPublisher 的 notifySubscribers() 方法用于通知所有订户出现了新消息。notifySubscribers() 方法在内部调用由具体观察者实现的 update() 方法以便它们可以获得最新的消息。
观察者模式具有以下优点
它使得彼此交互的对象之间保持松耦合它使得我们可以在无需对主题或观察者进行任何修改的情况下高效地发送数据到其他对象可以随时添加/删除观察者。
以下是观察者模式的缺点
观察者接口必须由具体观察者实现而这涉及继承。无法进行组合因为观察者接口可以实例化如果实现不当的话观察者可能会增加复杂性并导致性能降低在软件应用程序中通知有时可能是不可靠的并导致竞争条件或不一致性。
命令模式 – 封装调用
命令模式通常使用以下术语Command、Receiver、Invoker 和 Client
Command 对象了解 Receiver 对象的情况并能调用 Receiver 对象的方法调用者方法的参数值存储在 Command 对象中调用者知道如何执行命令客户端用来创建 Command 对象并设置其接收者。
命令模式的主要意图如下
将请求封装为对象可用不同的请求对客户进行参数化允许将请求保存在队列中我们将在本章后面进行讨论提供面向对象的回调。
命令模式可用于以下各种情景
根据需要执行的操作对对象进行参数化将操作添加到队列并在不同地点执行请求创建一个结构来根据较小操作完成高级操作。
以下的 Python 代码实现了命令设计模式。假设我们想要开发一个安装向导或者更常见的安装程序。通常安装意味着需要根据用户做出的选择来复制或移动文件系统中的文件。在下面的示例中我们首先在客户端代码中创建 Wizard 对象然后使用 preferences() 方法存储用户在向导的各个屏幕期间做出的选择。在向导中单击 Finish 按钮时就会调用 execute() 方法。之后execute() 方法将会根据首选项来开始安装。
class Wizard():def __init__(self, src, rootdir):self.choices []self.rootdir rootdirself.src srcdef preferences(self, command):self.choices.append(command)def execute(self):for choice in self.choices:if list(choice.values())[0]:print(Copying binaries --, self.src, to , self. rootdir)else:print(No Operation)if __name__ __main__:## Client codewizard Wizard(python3.5.gzip, /usr/bin/)## Users chooses to install Python onlywizard.preferences({python:True})wizard.preferences({java:False})wizard.execute()这里将通过一个在互联网世界中经常讲到的证券交易所的例子来演示命令模式的实现。
作为证券交易所的用户你会创建买入或卖出股票的订单。通常情况下你无法直接执行买入或卖出。实际上代理或经纪人在你和证券交易所之间扮演了中介的角色。代理负责将你的请求提交给证券交易所完成工作。我们假设你想在星期一早上开市后卖出股票。但是在星期日晚上虽然交易所尚未开市你就可以向代理提出卖出股票的请求。然后代理会将该请求放入排队以便在星期一早晨当交易所开市的时候执行该请求完成相应的交易。这实际上就是一个命令模式的经典情形。
首先介绍 Command 对象即 Order还开发了表示 ConcreteCommand 的两个主要的具体类BuyStockOrder 和 SellStockOrderStockTrade 类表示该示例中的 Receiver 对象Agent 类表示调用者代理是客户端和 StockExchange 之间的中介并执行客户下达的订单。
from abc import ABCMeta, abstractmethodclass Order(metaclassABCMeta):abstractmethoddef execute(self):passclass BuyStockOrder(Order):def __init__(self, stock):self.stock stockdef execute(self):self.stock.buy()class SellStockOrder(Order):def __init__(self, stock):self.stock stockdef execute(self):self.stock.sell()class StockTrade:def buy(self):print(You will buy stocks)def sell(self):print(You will sell stocks)class Agent:def __init__(self):self. __orderQueue []def placeOrder(self, order):self. __orderQueue.append(order)order.execute()if __name__ __main__:#Clientstock StockTrade()buyStock BuyStockOrder(stock)sellStock SellStockOrder(stock)#Invokeragent Agent()agent.placeOrder(buyStock)agent.placeOrder(sellStock)客户首先设置其接收者StockTrade 类 它使用 BuyStockOrder 和 SellStockOrder(ConcreteCommand)创建订单来买卖股票执行 StockTrade 的相关操作调用者对象是通过实例化 Agent 类创建的Agent 的 placeOrder() 方法用于获取客户端所下的订单。
在软件中应用命令模式的方式有很多种。我们将讨论与云应用密切相关的两个实现。
重做或回滚操作。异步任务执行。
命令模式具有以下优点
将调用操作的类与知道如何执行该操作的对象解耦提供队列系统后可以创建一系列命令添加新命令更加容易并且无需更改现有代码还可以使用命令模式来定义回滚系统例如在向导示例中我们可以编写一个回滚方法。
下面是命令模式的缺点
为了实现目标需要大量的类和对象进行协作。应用程序开发人员为了正确开发这些类需要倍加小心每个单独的命令都是一个 ConcreteCommand 类从而增加了需要实现和维护的类的数量。
模板方法模板 – 封装算法
模板方法模式通过一种称为模板方法的方式来定义程序框架或算法。
模板方法模式适用于以下场景
当多个算法或类实现类似或相同逻辑的时候在子类中实现算法有助于减少重复代码的时候可以让子类利用覆盖实现行为来定义多个算法的时候。
模板方法模式使用以下术语 —— AbstractClass、ConcreteClass、Template Method 和 Client。
AbstractClass声明一个定义算法步骤的接口。ConcreteClass定义子类特定的步骤。template_method()通过调用步骤方法来定义算法。
想象一个旅行社的例子例如 Dev Travels。他们定义了各种旅游路线并提供度假套装行程。一个行程套餐本质上是你作为客户允诺的一次旅行。旅行还涉及一些详细信息如游览的地点、交通方式和与旅行有关的其他因素。
抽象对象由 Trip 类表示。它是一个接口Python 的抽象基类定义了不同日子使用的交通方式和参观的地点等细节setTransport 是一个抽象方法它由 ConcreteClass 实现作用是设置交通方式day1()、day2()、day3() 抽象方法定义了特定日期所参观的地点itinerary() 模板方法创建完整的行程即算法在本例中为旅行。
在本例中我们主要有两个实现 Trip 接口的具体类VeniceTrip 和 MaldivesTrip这两个具体类代表游客根据他们的选择和兴趣所进行的两次不同的旅行VeniceTrip 和 MaldivesTrip 都实现了 setTransport()、day1()、day2()、day3() 和 returnHome()。
TravelAgency 类代表该示例中的 Client 对象它定义了 arrange_trip() 方法让客户选择历史旅行或海滩旅行这个对象然后调用 itinerary() 模板方法并根据客户的选择为游客安排相应的旅行。
from abc import abstractmethod, ABCMetaclass Trip(metaclassABCMeta):abstractmethoddef setTransport(self):passabstractmethoddef day1(self):passabstractmethoddef day2(self):passabstractmethoddef day3(self):passabstractmethoddef returnHome(self):passdef itinerary(self):self.setTransport()self.day1()self.day2()self.day3()self.returnHome()class VeniceTrip(Trip):def setTransport(self):print(Take a boat and find your way in the Grand Canal)def day1(self):print(Visit St Marks Basilica in St Marks Square)def day2(self):print(Appreciate Doges Palace)def day3(self):print(Enjoy the food near the Rialto Bridge)def returnHome(self):print(Get souvenirs for friends and get back)class MaldivesTrip(Trip):def setTransport(self):print(On foot, on any island, Wow! )def day1(self):print(Enjoy the marine life of Banana Reef)def day2(self):print(Go for the water sports and snorkelling)def day3(self):print(Relax on the beach and enjoy the sun)def returnHome(self):print(Dont feel like leaving the beach..)class TravelAgency:def arrange_trip(self):choice input(What kind of place youd like to go historical or to a beach? )if choice historical:self.trip VeniceTrip()self.trip.itinerary()if choice beach:self.trip MaldivesTrip()self.trip.itinerary()TravelAgency().arrange_trip()模板方法模式提供以下优点
正如我们在本章前面所看到的没有代码重复由于模板方法模式使用继承而不是合成因此能够对代码进行重用。所以只有为数不多的几个方法需要重写灵活性允许子类决定如何实现算法中的步骤。
模板方法模式的缺点如下
调试和理解模板方法模式中的流程序列有时会令人困惑。你最终实现的方法可能是一个不应该实现的方法或根本没有实现抽象方法。文档和严格的错误处理必须由程序员完成模板框架的维护可能是一个问题因为任何层次低层或高层的变更都可能对实现造成干扰。因此使用模板方法模式可能会使维护变得异常痛苦。