观察者模式之发布订阅

观察者模式重点在于 观察者和被观察者的对应关系,以及将被观察者的改变及时通知到相对应的观察者。
这样的模式基本上可以解决少量数据源的情景,在观察者和被观察者可能是多对多关系的情况下,强耦合的结构会让代码不够清晰,难以维护。

在《JavaScript设计模式》一书中,提到了Observer和Publish/Subscribe的区别。

Observer模式要求希望接收到主题同志的观察者(或对象)必须订阅内容改变的事件。
Publish/Subscribe模式使用了一个主题/事件通道,这个通道介于希望接收到通知(订阅者)的对象和激活事件的对象(发布者)之间。该事件系统允许代码定义应用程序的特定事件,这些事件可以传递自定义参数,自定义参数包含订阅者所需的值。其目的是避免订阅者和发布者之间产生依赖关系。

这里的关键点在于,通过一个事件中心,将发布者和订阅者的耦合关系解开,发布者和订阅者通过事件中心来产生联系。
打个比方,发布者像是发布小广告的,事件中心是一个调度站,订阅者则告诉事件中心,我关注A、B类型的广告,如果有更新,请通知我。调度站记录A,B类型下的订阅者,等到A,B广告发布时,通知到订阅者。
这个例子里,发布者不关心订阅者是谁,也不维护订阅者列表,同订阅者解耦,只将自己发布的内容提交到事件中心。而订阅者和主题的关系,交给了事件中心来维护。
画一个类图来解释一下他们的关系。
发布订阅模式类图

现在用scala实现一个例子

1. 首先定义发布者接口

trait JobPublisher[E <: Event] {
    val jobPublisherName: String

    def publish(event: E): Unit
}

2. 定义订阅者接口



trait JobSubscriber[E <: Event] {
    val jobSubscriberName: String

    def subscribe(topic: String)

    def update(event: E): Unit
}

3. 实现发布者

class JobService extends JobPublisher[Event] {

 override val jobPublisherName: String = Const.JOB_PUBLISHER_TOPIC
    jobTopicChannel.addPublisher(this)

    override def publish(event: Event): Unit = {
        jobTopicChannel.publish(jobPublisherName, event)
    }

}

4. 实现订阅者

class ApplyService extends JobSubscriber[Event] {
  this.subscribe(Const.JOB_PUBLISHER_TOPIC)

  override val jobSubscriberName: String = "test1"

  override def subscribe(topic: String): Unit = {
        jobTopicChannel.subscribe(topic, this)
    }

    override def update(event: Event): Unit = {
        event match {
            case taskEvent: TaskTriggerEvent => {
               
            }
            case _ => Unit
        }
    }

}

5. 辅助类

定义事件类

sealed abstract class Event

case class TaskTriggerEvent(task: Task) extends Event

定义TopicChannel, 维护 Publisher 和 SubScriber的关系

import javax.inject.{Inject, Singleton}


import modules.utils.{JobPublisher, JobSubscriber}
import play.api.Configuration

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext

@Singleton
class JobTopicChannel @Inject()(val conf: Configuration)(implicit executionContext: ExecutionContext) {
   
    private val publisherMap = mutable.HashMap[String, JobPublisher[Event]]()
    private val subscriberMap = mutable.HashMap[String, ArrayBuffer[JobSubscriber[Event]]]()

    def addPublisher(publisher: JobPublisher[Event]) = {
        this.publisherMap += (publisher.jobPublisherName -> publisher)
    }

    def removePublisher(publisher: JobPublisher[Event]) = {
        this.publisherMap -= publisher.jobPublisherName
    }

    def clearPublisher() = {
        this.publisherMap.clear
    }

    def subscribe(pub: String, subscriber: JobSubscriber[Event]) = {
        if (this.subscriberMap.contains(pub)) {
            this.subscriberMap(pub) += subscriber
        } else {
            this.subscriberMap += (pub -> ArrayBuffer(subscriber))
        }
    }

    def publish(pub: String, event: Event) = {
        if (this.publisherMap.contains(pub)) {
            this.subscriberMap(pub).foreach { subItem =>
                subItem.update(event)
            }
        } else {
            throw new NoSuchElementException("There is not the publisher!")
        }
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值