有条件的EventHandling

时间:2015-05-26 16:56:02

标签: c# design-patterns

我相信我有一个设计问题,我希望得到你的意见。我制作了一个小程序来说明我的问题。基本上,我的程序包括一个无线电系统,可以在建筑物的每个房间听到。声音取决于接收端,取决于房间是否将自己注册到无线电系统。

我的问题是,即使房间没有注册,每个房间也会触发发送的信息。我希望在消息发送之前执行该条件,而不是在接收端。通过这样做,我可以节省自己不必要的流量。任何人都可以给我一个想法或正确的方法来解决这种情况吗?

仅仅为了记录,我宁愿不在收音机里有多个活动处理程序,因为我不知道会有多少个房间。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Radio
{
#region Speakers
public interface ISound
{
    string sound { get; set; }
}
public abstract class RoomSpeaker : ISound
{
    public string sound { get; set; }
}
public class Room1Speaker : RoomSpeaker
{
}
public class Room2Speaker : RoomSpeaker
{
}
public class BuildingSpeaker : RoomSpeaker
{
}
#endregion
#region Rooms
public abstract class Room
{
    public Radio radioPlayer;
    public string name;
    public HashSet<Type> registeredSpeakers = new HashSet<Type>();

    public virtual void RoomPlayer(string lyrics)
    {
        registeredSpeakers.Add(typeof(BuildingSpeaker));
        Console.WriteLine(lyrics);
    }
}
public class Room1 : Room
{
    public Room1(Radio radioPlayer)
    {
        this.radioPlayer = radioPlayer;
        name = "Room1";
        registeredSpeakers.Add(typeof(Room1Speaker));
        radioPlayer.onRadio += radioPlayer_onRadio;
    }

    // This is what I don't think I like.  It will only do something if it's registered. That's fine.
    // But on any radio message out, this room will get called regardless.  Should I NOT be doing this? Should I go back to
    // making an eventHandler for every room? rather then having one even handler for all the rooms and have a condition on the receiving end.

    void radioPlayer_onRadio(object sender, ISound e)
    {
        if (registeredSpeakers.Contains(e.GetType()))
            RoomPlayer(name + e.sound);
    }
}
public class Room2 : Room
{
    public Room2(Radio radioPlayer)
    {
        this.radioPlayer = radioPlayer;
        name = "Room2";
        registeredSpeakers.Add(typeof(Room2Speaker));
        radioPlayer.onRadio += radioPlayer_onRadio;
    }

    void radioPlayer_onRadio(object sender, ISound e)
    {
        // same problem as in Room1. 
        if (registeredSpeakers.Contains(e.GetType()))
            RoomPlayer(name + e.sound);
    }
}
#endregion

public class Radio
{
    public event EventHandler<ISound> onRadio;

    public void PlayRoom1()
    {
        onRadio(this, new Room1Speaker() { sound = "Test" });
    }
    public void PlayRoom2()
    {
        onRadio(this, new Room2Speaker() { sound = "Test" });
    }
    public void PlayAllRooms()
    {
        onRadio(this, new BuildingSpeaker() { sound = "Test All Rooms" });
    }
}

class Program
{
    static void Main(string[] args)
    {
        var radio = new Radio();
        var room1 = new Room1(radio);
        var room2 = new Room2(radio);

        radio.PlayRoom1();
        radio.PlayRoom2();
        radio.PlayAllRooms();
        Console.ReadLine();
    }
}
}

2 个答案:

答案 0 :(得分:1)

好的,你正在看的是发布 - 订阅模式(AKA eventbus)。在eventbus模式中,您有一个注册侦听器并发送消息的类。听众告诉事件总线“我正在听事件X”。当事件总线“发送”事件X时,它会查询该事件的侦听器列表,如果已注册,则执行侦听器告诉它执行的方法。

public class EventBus
{
    private Dictionary<Type, List<Action<IEvent>>> actions = new Dictionary<Type, List<Action<IEvent>>>();

    public void Listen<T>(Action<IEvent> callback) where T : IEvent
    {
        if (!actions.ContainsKey(typeof(T)))
        {
            actions.Add(typeof(T), new List<Action<IEvent>>());
        }

        actions[typeof(T)].Add(callback);
    }

    public void ClearCallbacks<T>() where T : IEvent
    {
        actions[typeof (T)] = null;
    }

    public void Send<T>(T @event) where T : IEvent
    {
        if (!actions.ContainsKey(typeof(T)))
        {
            return;
        }

        foreach (var action in actions[typeof(T)])
        {
            action(@event);
        }
    }
}

public interface IEvent
{
}

用法:

public static void main () {
    var eventBus = new EventBus();

    var aRoom = new NoisyRoom(eventBus);
    var bRoom = new NoisyRoom(eventBus);
    var cRoom = new NoisyRoom(eventBus);
    var dRoom = new QuietRoom(eventBus);

    eventBus.Send(new NoisyEvent()); //sends to a,b,c room
}

public class EasyListeningEvent : IEvent
{
}
public class QuietRoom
{
    public QuietRoom(EventBus eventBus)
    {
        eventBus.Listen<EasyListeningEvent>(BringTheNaps);
    }

    private void BringTheNaps(IEvent @event)
    {
        //its been brought!
    }
}

class NoisyEvent : IEvent
{
}
public class NoisyRoom
{
    public NoisyRoom(EventBus eventBus)
    {
        eventBus.Listen<NoisyEvent>(BringTheNoise);
    }

    private void BringTheNoise(IEvent @event)
    {
        //its been brought!
    }
}

答案 1 :(得分:0)

尝试一下这样的事情:

编辑:请注意,这只是一个正确方向的开始。你可以进一步采取这一点。基本上你所拥有的是声源和声音发射器。显然,收音机是声源,扬声器是声音发射器,但像房间这样的东西可能都是。收音机不应该知道扬声器或房间是什么,它应该只知道发射器,它应该只向他们发送声音。基于此,房间应该有一组发射器(可能是扬声器),当一个房间从收音机发出声音时,它会简单地将它传递给它注册的任何发射器。也没有什么可以阻止你直接将收音机注册到收音机。此代码应该有助于说明如何实现所有这些。

public class Radio
{
    private HashTable<string, EventHandler<ISound>> rooms = new ...;

    public void RegisterRoom(string room, EventHandler<ISound> onSound)
    {
        rooms[room] = onSound;
    }

    public void UnregisterRoom(string room)
    {
        rooms.Remove(room);
    }

    public void PlayRoom(string room)
    {
        EventHandler<ISound> onSound;

        if (rooms.TryGetValue(room, out onSound))
        {
            onSound(this, new BuildingSpeaker() { sound = "Test" });
        }
    }

    public void PlayAllRooms()
    {
        if (rooms.Count == 0)
        {
            return;
        }

        var speaker = new BuildingSpeaker() { sound = "Test All Rooms" };

        foreach (var room in rooms)
        {
            room.Value(this, speaker);
        }
    }
}