It is possible with dynamic subscription. You will need a mapper class that will help you achieve this. lets say you have a Publisher class that broadcast financial data periodically - Its keeps on running and whenever it has some new information it just publishes it:
for the broadcasting it has a delegate defined like following:
public delegate void BroadcastData(string ticker, string command, decimal value);
Now there could be many subscribers interested in these broadcasts - but they may or may not be interested in the exact piece of information that is being published.
Lets assume that Subscriber
S1 is interested in Ticker
A, B and
C and Command
PriceClose where as
S2 is interested in Ticker
X, Y and
Z and Command
PriceClose & PriceOpen.
To solve this problem you need to put a mapper class - could be a singleton object.
See the example code - Here Subscriber gets the update from Publisher but not directly. Its the mapper who manages things between.
public delegate void BroadcastData(string ticker, string command, decimal value);
public delegate void MarketUpdate(List<FinancialData> fData);
public class BroadcastingService {
public static readonly BroadcastingService Instance = new BroadcastingService();
public event BroadcastData OnDataUpdate;
private void Publish()
{
if (OnDataUpdate != null)
OnDataUpdate("SomeTicker", "SomeCommand", 1000.00m);
}
}
public class Subscriber
{
public Subscriber() {
Mapper.Register("IBM,VOD".Split(',').ToList(), "Priceclose,priceopen".Split(',').ToList(), HandleUpdate);
}
private void HandleUpdate(List<FinancialData> financialUpdate) {
}
}
public class FinancialData {
public string ticker;
public string command;
public decimal value;
}
public static class Mapper {
static Dictionary<MarketUpdate, KeyValuePair<List<string>, List<string>>> Listners =
new Dictionary<MarketUpdate, KeyValuePair<List<string>, List<string>>>();
static List<FinancialData> allUpdate = new List<FinancialData>();
static System.Threading.Timer timer = null;
static Mapper() {
BroadcastingService.Instance.OnDataUpdate += new BroadcastData(Instance_OnDataUpdate);
timer = new System.Threading.Timer(st => {
NotifySubscribers();
});
timer.Change(1000, 1000);
}
static void Instance_OnDataUpdate(string ticker, string command, decimal value)
{
var fData = allUpdate.Find(fd => fd.ticker == ticker && fd.command == command);
if (fData == null)
{
fData = new FinancialData() { ticker = ticker, command = command, value = value };
allUpdate.Add(fData);
}
else {
fData.value = value;
}
}
public static void Register(List<string> tickers, List<string> commands, MarketUpdate callback)
{
Listners.Add(callback, new KeyValuePair<List<string>,List<string>>(tickers, commands));
}
public static void UnRegister(MarketUpdate callback) {
Listners.Remove(callback);
}
public static void NotifySubscribers(){
foreach (var lt in Listners)
{
List<FinancialData> financialUpdate = new List<FinancialData>();
foreach (string tkr in lt.Value.Key)
{
foreach (string cmd in lt.Value.Value)
{
var fData = allUpdate.Find(fd => fd.ticker == tkr && fd.command == cmd);
if (fData != null) {
financialUpdate.Add(fData);
}
}
}
lt.Key.BeginInvoke(financialUpdate, null, null);
}
}
}
We can have such loose links; But to get update of course in the chain somewhere the reference of publisher has to be maintained. Its just that - it's a global Instance here who does this instead of subscriber doing so by itself.