每当我感到饥饿时,我会发布我饿了。这将通知服务提供商说( MealsService,FruitService,JuiceService )。(这些服务提供商知道要提供什么服务)。
但服务优先权是关注点。这里的优先级意味着我的第一选择是 MealsService 当有足够的膳食可用时我的需要以MealsService结束。要验证足够的膳食是否可用 MealsService 引发事件“updateMeTheStockStatus “到”MealsServiceStockUpdateListener“。
“MealsServiceStockUpdateListener”只会回复“MealsService”。 “MealsServiceStockUpdateListener”将通知其他服务提供商( FruitService,JuiceService )。如果没有足够的库存,则只有 MealsService 通过通知 JuiceService (因为这是第二优先)。像往常一样,它检查库存。如果库存不足,它会将消息传递给 FruitService ,所以流程继续像此
(有人!请重新构建它以获得良好的可读性)。
更新:在此模型中,“StackUpdateListener”和“我”之间没有直接通信。只有“服务提供商”才会与我沟通。
答案 0 :(得分:1)
不是向提供商发布“我很饿”的消息,而是发布“我需要了解当前库存”。然后倾听,直到您有足够的信息向正确的食品服务部门提出您所需要的信息。这样,什么让我吃饱的逻辑并没有在食品服务中传播......对我来说似乎更清洁。
答案 1 :(得分:1)
消息传递不会直接传入.NET,您需要手动实现自己的消息转发。幸运的是,“责任链设计模式”专门针对您尝试解决的问题而设计,即将消息转发到链中,直到有人能够处理它为止。
有用的资源:
答案 2 :(得分:1)
我不确定你是否真的需要优先事件。无论如何,我们假设我们想要编写那些只是为了好玩。
.NET Framework不支持这种特殊的构造。让我展示一种可能的方法来实现它。
第一步是为事件代表创建自定义商店(如所描述的here);
在内部,自定义事件存储可以像priority queue一样工作;
使用的具体EventArgs
将是HandledEventArgs
(或其子类)。这将允许事件提供程序在其中一个事件将事件设置为Handled
后停止调用处理程序;
下一步是最难的。怎么说告诉事件提供者正在添加的事件处理程序的优先级是什么?
让我澄清一下这个问题。通常,添加处理程序是这样的:
eater.GotHungry += mealsService.Someone_GotHungry;
eater.GotHungry += juiceService.Someone_GotHungry;
eater.GotHungry += fruitService.Someone_GotHungry;
+=
运算符只会收到一个代表。传递第二个priority
参数是不可能的。这个问题可能有几种可能的解决方案。一种方法是在事件处理程序方法中定义自定义属性集中的优先级。第二种方法在question中讨论。
与dofactory.com上的责任链实施相比,这种方法有一些优势。首先,处理者(您的食品服务)不需要彼此了解。此外,可以随时动态添加和删除处理程序。当然,您也可以实施具有这种优势的责任链的变体。
答案 3 :(得分:1)
我不认为代表是您问题的正确解决方案。委托是C#提供的低级服务,用于组件之间相对紧密耦合的事件。如果我理解你的问题(措辞有点奇怪,所以我不确定我是否清楚地理解你的问题),那么我认为你需要的是一个中介的消费者/提供者。
让消费者直接消费膳食,果汁和水果供应商,让他们向中央调解员索取食品。然后,调解员将负责确定可用的内容以及应向消费者提供的内容。调解员将成为所有三种服务发布的事件的订户。每当在膳食,果汁或水果服务中添加/更新库存时,他们都会将其当前库存发布给所有订户。作为订户的调解员将自己跟踪当前的库存减少量,并且能够在收到食品请求时自行确定是否向食品消费者发送膳食,果汁或水果。
例如:
|---------- (GetFoodResponse) ----------------
V |
FoodConsumer ---- (GetFoodRequest) ------> FoodProvider <-----> [ Local Stock Data ]
^
|
|
MealService ---- (PublishStockMessage) ----------|
^
JuiceService --- (PublishStockMessage) ----------|
^
FruitService --- (PublishStockMessage) ----------|
这种解决方案的好处是可以减少耦合,适当地分离责任并解决您的问题。首先,您的消费者只需要使用单一服务...... FoodProvider。 FoodProvider订阅其他三种服务的出版物,并负责确定向消费者提供的食物。这三种食品服务不对与食品消费者的饥饿有关的任何事情负责,他们只负责提供食品和跟踪他们提供的食品库存。您还可以分发各种组件。如果需要,您的消费者,食品供应商以及三种食品服务中的每一种都可以托管在不同的物理机器上。
但是,要实现上述优势,您的解决方案将变得更加复杂。你有更多的部件,他们需要正确地相互连接。您必须发布和订阅消息,这需要某种支持基础设施(WCF,MSMQ,一些第三方ESB,自定义解决方案等)。您还有数据重复,因为食品提供商另外跟踪库存每种食品服务,这可能导致可用库存的不连续性。如果您正确管理库存,可以减轻这种情况,但这也会增加复杂性。
如果能够处理额外的复杂性,最终,像这样的解决方案比在本地部署方案中使用组件和C#事件的更紧密连接的解决方案更具灵活性和适应性(如在原始示例中那样)。
答案 4 :(得分:1)
与其他回答者一样,我并不完全相信事件是前进的方向,但暂时让我们继续这样做。
在我看来,与MealsServiceStockUpdateListener的业务确实是一个红色的鲱鱼 - 你只是试图执行一些事件处理程序而不是其他人。当你有一个“BeforeXXX”事件允许取消或者某种异常处理事件时,这种事情会在其他地方出现。
基本上,您需要分别使用每个处理程序。有两种不同的方法 - 您可以使用普通的多播委托并调用GetInvocationList()
,也可以更改事件声明以明确保留处理程序列表:
private List<EventHandler> handlers = new List<EventHandler>();
public event EventHandler MealRequired
{
add { handlers.Add(value); }
remove
{
int index = handlers.LastIndexOf(value);
if (index != -1)
{
handlers.RemoveAt(index);
}
}
}
这两种方法并不完全相同 - 如果您订阅了一个已经是复合委托的委托实例,GetInvocationList
会将其展平,但List
方法不会。我可能会自己去GetInvocationList
。
现在,第二个问题是如何检测用餐时间。同样,有两种方法。第一种是使用普通事件处理程序模式,使有问题的EventArgs
子类可变。这是HandledEventArgs
采用的方法。第二种方法是打破正常事件模式,并使用一个委托,该委托返回一个值,该值可用于指示成功或失败(以及可能的其他信息)。这是ResolveEventHandler
采用的方法。无论哪种方式,您依次执行代理,直到其中一个代表满足您的要求。这是一个简短的例子(不使用事件本身,而是使用复合委托):
using System;
public class Test
{
static void Main(string[] args)
{
Func<bool> x = FirstProvider;
x += SecondProvider;
x += ThirdProvider;
Execute(x);
}
static void Execute(Func<bool> providers)
{
foreach (Func<bool> provider in providers.GetInvocationList())
{
if (provider())
{
Console.WriteLine("Done!");
return;
}
}
Console.WriteLine("No provider succeeded");
}
static bool FirstProvider()
{
Console.WriteLine("First provider returning false");
return false;
}
static bool SecondProvider()
{
Console.WriteLine("Second provider returning true");
return true;
}
static bool ThirdProvider()
{
Console.WriteLine("Third provider returning false");
return false;
}
}
答案 5 :(得分:0)
我在理解你的类比时遇到了一些麻烦,这听起来像是在掩盖软件的实际意图,但我想我已经做了类似你所描述的事情。
在我的情况下,软件是电话营销软件,每个电话推销员都有一个呼叫队列。当该队列提出事件表明它接近空时,程序将获取一个可供呼叫的人员列表,然后通过一系列责任将其推送到电话推销员的队列中,如下所示:
链中的每个元素都充当优先级过滤器:链中的第一个链接将抓住之前从未被调用过的所有人,如果它已经完成(即通过所有从未有过的人)如果没有填满队列,它将通过剩余的人员列表来调用链中的下一个链接 - 这将应用另一个过滤器/搜索。这一直持续到链中的最后一个链接,它只是向管理员发送一封电子邮件,表明没有可用的人被呼叫,人们需要在电话推销员无需工作之前快速介入。