观察者模式

#观察者模式-----了解对象的情况
##一、行为型模式简介

行为型模式主要关注的是对象的责任。它们用来处理对象之间的交互,以实现更大的功能。行为型模式建议:对象之间应该能够彼此交互,同时还应该是松散耦合的。
观察者设计模式是最简单的行为型模式之一。

##二、理解观察者设计模式

在观察者设计模式中,对象(主题)维护了应该依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化。
在分布式应用中,多个服务通常是通过彼此交互来实现用户想要实现的更大型的操作的。服务可以执行多种操作,但是它们执行的操作会直接或很大程度上取决于与其交互的服务对象的状态。

关于用户注册的示例,其中用户服务负责用户在网站上的各种操作。假设我们有另一个成为电子邮件服务的服务,它的作用是监视用户的状态并向用户发送电子邮件。例如,如果用户刚刚注册,则用户服务将调用电子邮件服务的方法,该方法将向用户发送电子邮件以进行账户验证。如果账户经过了验证,但信用度较低,则电子邮件服务将监视用户服务并向用户发送信用度过低的电子邮件警报。

因此,如果在应用中存在一个许多其他服务所依赖的核心服务,那么该核心服务就会成为观察者必须观察/监视其变化的主题。当主题发送变化时观察者应该改变自己的对象的状态,或者采取某些动作。

以博客为例,当你和其他的读者订阅一个博客后,每当发布新博客时,你和其他读者都会收到通知。当然,通知你的方式可以是电子邮件。如果将此场景应用于观察者模式,那么这里的博客就是维护订阅者或观察者列表的主题。因此,当有新的文章添加到博客中时,所有观察者就会通过电子邮件或由观察者定义任何其他通知机制收到相应的通知。

观察者模式的主要目标如下:
(1)它定义了对象之间的一对多的依赖关系,从而使得一个对象中的任何更改都将自动通知给其他依赖对象;
(2)它封装了主题的核心组件。
观察者模式可用于以下多种场景:
(1)在分布式系统中实现事件服务;
(2)用作新闻机构的框架;
(2)股票市场也是观察者模式的一个大型场景。

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)	

if __name__ == '__main__':		
	subject = Subject()
	observer1 = Observer1(subject)
	observer2 = Observer2(subject)
	subject.notifyAll('notification')

##三、观察者模式的UML类图
这里写图片描述

观察UML图发现,该模式有3个主要角色:

  • 主题(Subject):类Subject需要了解Observer。Subject类具有许多方法,Observer可以通过这些方法注册到Subject类中。因此,一个Subject可以处理多个Observer。
  • 观察者(Observer):它为关注主题的对象定义了一个接口。它定义了Observer需要实现的各个方法,以便在主题发生变化时能够获得相应的通知。
  • 具体观察者(ConcreteObserver):它用来保存应该与Subject的状态保持一致的状态。它实现了Observer接口以保持其状态与主题中的变化相一致。

这个流程非常简单。具体观察者通过实现观察者提供的接口向主题注册接口。每当状态发生变化时,该主题都会使用观察者提供的通知方法来通告所有具体观察者。

##四、现实世界中的观察者模式
以新闻机构为例,新闻机构通常从不同地点收集新闻,并将其发送给订阅者。由于信息是实时发送或接收的,所以新闻机构应该尽快向订阅者发送该消息。此外,随着技术的进步,用户不仅可以订阅报纸,而且可以提供其他的形式进行订阅,例如电子邮件、移动设备、短信或语音呼叫。所以,我们还应该具备在将来添加任意其他订阅形式的能力,以便为未来的新技术做好准备。
代码实现:

from abc import ABCMeta, abstractmethod
#主题,新闻发布者NewsPublisher:
class NewsPublisher:
	def __init__(self):
		self.__subscribers = []
		self.__latestNews = None
	#attach()方法供观察者(Observer)来支持注册NewsPublisherObserver:	
	def attach(self, subscriber):	
		self.__subscribers.append(subscriber)
	def detach(self):	#用来注销	
		return self.__subscribers.pop()
	#返回已经使用Subject注册的所有订户的列表:	
	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 = news
	def getNews(self):	
		return 'Got News:' + self.__latestNews
#观察者(Observer)接口,Subscriber代表其他ConcreteObserver:
class Subscriber(metaclass = ABCMeta):		
	@abstractmethod
	#由ConcreteObserver实现,这样只要有新闻发布,它们都能得到通知:
	def update(self):	
		pass
#具体观察者EmailSubscriber、SMSSubscriber、AnyOtherSubscriber:		
class EmailSubscriber(Subscriber):		
	def __init__(self, publisher):
		self.publisher = publisher
		self.publisher.attach(self)	#注册
	def update(self):	
		print(type(self).__name__, self.publisher.getNews())
class SMSSubscriber(Subscriber):		
	def __init__(self, publisher):
		self.publisher = publisher
		self.publisher.attach(self)	#注册
	def update(self):	
		print(type(self).__name__, self.publisher.getNews())
class AnyOtherSubscriber(Subscriber):		
	def __init__(self, publisher):
		self.publisher = publisher
		self.publisher.attach(self)	#注册
	def update(self):	
		print(type(self).__name__, self.publisher.getNews())	
#主题和观察者之间的交互:
if __name__ == '__main__':		
	np = NewsPublisher()
	#初始化订阅者:
	for subscriber in [EmailSubscriber, SMSSubscriber, AnyOtherSubscriber]:
		subscriber(np)
	print('Subscribers:', np.subscribers())
	
	np.addNews('g good news!')	
	np.notifySubscribers()
	
	print('detached:', type(np.detach()).__name__)
	print('Subscribers:', np.subscribers())
	
	np.addNews('Hello World')	
	np.notifySubscribers()

##五、观察者模式的通知方式
有两种不同的方式可以通知观察者在主题中发生的变化。它们可以被分为推模型和拉模型。
(1)拉模型
在拉模型中,观察者扮演积极的角色。

  • 每当发生变化时,主题都会向所有已注册的观察者进行广播。
  • 出现变化时,观察者负责获取相应的变化情况,或者从订户那里拉取数据。
  • 拉模型的效率极低,因为它涉及两个步骤,第一步,主题通知观察者;第二步,观察者从主题那里提取所需的数据。

(2)推模型
在推模型中,主题是起主导作用的一方,如下所述:

  • 与拉模型不同,变化是由主题推送到观察者的。
  • 在推模型中,主题可以向观察者发送详细的信息(即使可能不需要)。当主题发送大量观察者用不到的数据时,会使响应时间过长。
  • 由于只从主题发送所需的数据,所以能够提高性能。

六、松耦合与观察者模式

松耦合是软件开发应该采用的重要设计原理之一。松耦合的主要目的是争取在彼此交互的对象之间实现松散耦合设计。耦合是指一个对象对于与其交互的其他对象的了解程度。
松耦合设计允许我们构建灵活的面向对象系统,有效应对各种变化,因为它们降低了多个对象之间的依赖性。
松耦合架构具有以下特性:

  • 它降低了在一个元素内发生的更改可能对其他元素产生意味影响的风险;
  • 它使得测试、维护和故障排除工作更加简单;
  • 系统可以轻松地分解为可定义的元素;

观察者模式提供了一种实现主题和观察者松耦合的对象设计模式。以下几条可以更好地解释这一点:

  • 主题对观察者唯一的了解就是它实现一个特定的接口。同时,它也不需要了解具体观察者类。
  • 可以随时添加任意的新观察者;
  • 添加新观察者时,根本不需要修改主题。
  • 观察者和主题没有绑定在一起,所有可以彼此独立使用。如果需要的话,观察者可以在任何地方重复使用。
  • 主题或观察者中的变化不会相互影响。由于两者都是独立的或松散耦合的,所以它们可以自由地做出自己的决定。

##七、观察者模式-----优点和缺点
观察者模式具有以下优点:

  • 它使得彼此交互的对象之间保持松耦合;
  • 它使得我们可以在无需对主题或观察者进行任何修改的情况下高效地发送数据到其他对象;
  • 可以随时添加/删除观察者。

观察者模式的缺点:

  • 观察者接口必须由具体观察者实现,而这涉及继承。无法进行组合,因为观察者接口可以不可以实例化;
  • 如果实现不当的话,观察者可能会增加复杂性,并导致性能降低;
  • 在软件应用程序中,通知有时可能是不可靠的,并导致竞争条件或不一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值