我正在尝试使用Scala实现Map条目监听器。
想法:
我无法为此找到解决方案,所以我尝试用Akka实现它:
class TrackingService(system: ActorSystem) extends LazyLogging {
private val trackingActor = system.actorOf(TrackingActor.props)
private val duration = Duration(15, TimeUnit.SECONDS)
private implicit val timeout = Timeout(duration)
def fireEvent(key: String): Unit = {
TrackingActor ! EventFired(key)
}
def eventHappened(key: String): Future[Boolean] = {
TrackingActor ? DoesEventHappened(key)).mapTo[Boolean]
}
def registerHiddenCardListener(key: String, data: MyData): Unit = {
TrackingActor ! Subscribe(key, data)
}
}
case class EventFired(key: String)
case class EventHappened(key: String)
case class EventHappenedResponse(happened: Boolean)
case class Subscribe(key: String, data: Data)
class TrackingActor extends Actor with LazyLogging {
var eventMap: Map[String, Boolean] = Map.empty[String, Boolean]
var dataMap: Map[String, List[Data]] = Map.empty[String, List[Data]]
def receive: Receive = {
case Subscribe(key, data) =>
val currentData: List[Data] = dataMap.getOrElse(key, Nil)
val newData = data :: currentData
dataMap = dataMap + (key -> newData)
case EventHappened(key) => sender() ! EventHappenedResponse(eventMap.getOrElse(key, false))
case e@EventFired(key) =>
eventMap = eventMap + (key -> true)
for {
dataOpt <- dataMap.get(key)
data <- dataOpt
} {
// do callback with data (e.g. send email)
}
case x => logger.warn(s"Received unknown message: $x")
}
}
object TrackingActor {
def props: Props = Props(classOf[TrackingActor])
}
我不喜欢这个解决方案:我不喜欢问模式,但我需要访问非演员类的条目。另外,我不喜欢有2张地图,但我需要存储一些应该用于回调的数据。
关于如何改进这一点的任何想法?
答案 0 :(得分:1)
这是一个想法:
function [fitresult, gof] = createFit_MSSI_Venus(RockerArm, mos)
%CREATEFIT1(ROCKERARM,MOS)
% Create a fit.
%
% Data for 'Psychometric curve fitting MSSI-RockeArm 3D mesh' fit:
% X Input : RockerArm
% Y Output: mos
% Output:
% fitresult : a fit object representing the fit.
% gof : structure with goodness-of fit info.
%
% See also FIT, CFIT, SFIT.
% Auto-generated by MATLAB on 08-Jan-2016 15:40:41
%% Fit: 'Psychometric curve fitting MSSI-RockeArm 3D mesh'.
[xData, yData] = prepareCurveData( RockerArm, mos );
% Set up fittype and options.
ft = fittype( 'erfc((a+b*x)/sqrt(2))/2', 'independent', 'x',
'dependent', 'y' );
opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Algorithm = 'Levenberg-Marquardt';
opts.Display = 'Off';
opts.StartPoint = [0.655477890177557 0.171186687811562];
% Fit model to data.
[fitresult, gof] = fit( xData, yData, ft, opts );
% Plot fit with data.
figure( 'Name', 'Psychometric curve fitting MSSI-RockeArm 3D mesh' );
h = plot( fitresult, xData, yData);
set(h, 'Markersize',20);
legend( h, 'mos vs. RockerArm', 'Psychometric curve fitting MSSI-
RockeArm 3D mesh', 'Location', 'NorthEast' );
% Label axes
xlabel MSSI-RockerArm 3D mesh
ylabel mos
grid on
通过执行此操作,您可以使订阅者保持非case class Subscribe[A, B](f: (A, B, NotifyingMap[A,B]) => Any)
case class Event[A, B](key: A, value: B, originator: NotifyingMap[A,B])
case class RegisterObserver(actorRef: ActorRef)
/**
* Subscribes to events
*/
class Subscriber[A,B]{
def register(actorSystem: ActorSystem) = {
val actor = actorSystem.actorOf(Props(classOf[Observer[A,B]]))
actor ! Subscribe(handleEvent)
}
def handleEvent(key: A, value: B, notifyingMap: NotifyingMap[A, B]) = {
println(s"Saw key $key with value $value")
}
}
/**
* Observer of events that will call a partial function when
* an event comes in.
*/
class Observer[A, B] extends Actor{
var f: (A,B,NotifyingMap[A,B]) => Any = _
def receive = {
case x: Subscribe[A, B] =>
f = x.f
Notifier() ! RegisterObserver(self)
case e: Event[A,B] =>
f(e.key, e.value, e.originator)
}
}
/**
* Notifier that sends out the event to all registered observers.
*/
class Notifier extends Actor {
var observers = List[ActorRef]()
def receive = {
case x: RegisterObserver =>
observers = x.actorRef :: observers
case x: Event[_,_] =>
observers.foreach(_ ! Event)
}
}
/**
* Singleton notifier.
*/
object Notifier{
var notifier: ActorRef = _
def create(actorSystem: ActorSystem) =
actorSystem.actorOf(Props(classOf[Notifier]))
def apply(): ActorRef = notifier
}
/**
* Class that sends out an event when an item is put. Also allows for
* getting an item based on a key.
*/
class NotifyingMap[A, B](){
val map: TrieMap[A,B] = TrieMap[A,B]()
// Specific business logic here on when you publish the event.
def put(key: A, value: B) = {
map.put(key, value).foreach(v => Notifier() ! Event(key, v, this))
}
def get(key: A) = map.get(key)
}
类,同时仍允许其对事件做出反应。您也可以在Actor
上调用简单的旧方法,因为它只是一个类,而不是NotifyingMap
。
我个人喜欢在邮件中存储回调信息。通常,您通过在案例类中使用另一个Actor
来看到这一点。在此示例中,我们在案例类中有ActorRef
,因此我们知道事件的起源,并可以在那里适当地调用NotifyingMap
方法。
完全披露:我没有运行任何此代码。它确实编译。