diff --git a/src/BehavioralPatterns/Program.cs b/src/BehavioralPatterns/Program.cs index 292b292..feeb977 100644 --- a/src/BehavioralPatterns/Program.cs +++ b/src/BehavioralPatterns/Program.cs @@ -38,9 +38,11 @@ namespace BehavioralPatterns MementoPatternExamples.Run(); + Console.ReadKey(); ObserverPatternExamples.Run(); Console.ReadKey(); + } } } diff --git a/src/CommandPattern/CommandPatternExamples.cs b/src/CommandPattern/CommandPatternExamples.cs index 0e50ac4..d388b0e 100644 --- a/src/CommandPattern/CommandPatternExamples.cs +++ b/src/CommandPattern/CommandPatternExamples.cs @@ -31,12 +31,6 @@ namespace CommandPattern } - private static void GoToNextStep() - { - Console.ReadKey(); - Console.Clear(); - } - public static string GetPatternDescription() { return @"Patttern description: @@ -76,5 +70,11 @@ level undo/redo functionality, in practice this only works for simple examples, where all entities are at the same level. More details about undo can be seen at: http://gernotklingler.com/blog/implementing-undoredo-with-the-command-pattern/"; } + + private static void GoToNextStep() + { + Console.ReadKey(); + Console.Clear(); + } } } diff --git a/src/ObserverPattern/ObserverPatternExamples.cs b/src/ObserverPattern/ObserverPatternExamples.cs index c6c640b..de480a8 100644 --- a/src/ObserverPattern/ObserverPatternExamples.cs +++ b/src/ObserverPattern/ObserverPatternExamples.cs @@ -1,4 +1,5 @@ -using ObserverPattern.Twits; +using ObserverPattern.StockUpdateEvents; +using ObserverPattern.Twits; using System; using System.Collections.Generic; using System.Linq; @@ -11,8 +12,66 @@ namespace ObserverPattern { public static void Run() { + Console.WriteLine(GetDescription()); + Console.WriteLine(GetActors()); + Console.WriteLine(IsThisPubSub()); + + StockUpdateEventsExample stockExample = new StockUpdateEventsExample(); + stockExample.RunSimple(); + + GoToNextStep(); + Console.WriteLine("Same business logic using RX"); + stockExample.RunReactive(); + + Console.WriteLine(GetLapsedLinstenerProblem()); + ObservableTwitsExample obsTwits = new ObservableTwitsExample(); obsTwits.Run(); + + GoToNextStep(); + } + + private static string GetDescription() + { + return @" +The observer pattern is a software design pattern in which an object, called the subject, +maintains a list of its dependents, called observers, and notifies them automatically of any state changes, +usually by calling one of their methods. "; + } + + private static string IsThisPubSub() + { + return @" +Observer/Observable pattern is mostly implemented in a synchronous way, +i.e. the observable calls the appropriate method of all its observers when some event occurs. +The Publisher/Subscriber pattern is mostly implemented in an asynchronous way (using message queue). +In the Observer/Observable pattern, the observers are aware of the observable. +Whereas, in Publisher/Subscriber, publishers and subscribers don't need to know each other. +They simply communicate with the help of message queues. +"; + } + + private static string GetActors() + { + return @" +Subject -> Notifies interested observers when an event occurs +Observer -> Registers to a subject, to be notified when a specific event happens +"; + } + + + private static string GetLapsedLinstenerProblem() + { + return @" +The leak happens when a listener fails to unsubscribe from the publisher when it no longer needs to listen. +Consequently, the publisher still holds a reference to the observer which prevents it from being garbage collected +— including all other objects it is referring to — for as long as the publisher is alive, which could be until the end of the application. +This causes not only a memory leak, but also a performance degradation with an 'uninterested' observer receiving and acting on unwanted events"; + } + private static void GoToNextStep() + { + Console.ReadKey(); + Console.Clear(); } } } diff --git a/src/ObserverPattern/StockUpdateEvents/AaplObserver.cs b/src/ObserverPattern/StockUpdateEvents/AaplObserver.cs new file mode 100644 index 0000000..c83592e --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/AaplObserver.cs @@ -0,0 +1,29 @@ +using System; + +namespace ObserverPattern.StockUpdateEvents +{ + /// + /// Observer + /// + public class AaplObserver + { + double? oldValue; + public AaplObserver(StockSubject stockObservable) + { + stockObservable.StockUpdated += (obj, e) => PrintNewValue(e.Stock); + } + + private void PrintNewValue(Stock stock) + { + if (stock.Name == "AAPL") + { + if (oldValue.HasValue) + Console.WriteLine("Apple price updated from {0} to {1}", oldValue, stock.Value); + else + Console.WriteLine("Apple has a new price, new value is: {0}", stock.Value); + + oldValue = stock.Value; + } + } + } +} \ No newline at end of file diff --git a/src/ObserverPattern/StockUpdateEvents/LondonStockObserver.cs b/src/ObserverPattern/StockUpdateEvents/LondonStockObserver.cs new file mode 100644 index 0000000..adfad53 --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/LondonStockObserver.cs @@ -0,0 +1,25 @@ +using System; + +namespace ObserverPattern.StockUpdateEvents +{ + public class LondonStockObserver + { + double? oldValue; + public LondonStockObserver(StockSubject stockObservable) + { + stockObservable.StockUpdated += (obj, e) => PrintStockValue(e.Stock); + } + + private void PrintStockValue(Stock stock) + { + if (stock.Name == "FTSE") + { + if (oldValue.HasValue) + Console.WriteLine("The Financial Times Stock Exchange 100 Index price updated from {0} to {1}", oldValue, stock.Value); + else + Console.WriteLine("The Financial Times Stock Exchange 100 Index has a new price, new value is: {0}", stock.Value); + oldValue = stock.Value; + } + } + } +} \ No newline at end of file diff --git a/src/ObserverPattern/StockUpdateEvents/Stock.cs b/src/ObserverPattern/StockUpdateEvents/Stock.cs new file mode 100644 index 0000000..5abd83f --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/Stock.cs @@ -0,0 +1,10 @@ +namespace ObserverPattern.StockUpdateEvents +{ + public class Stock + { + public string Name { get; set; } + public int Quantity { get; set; } + + public double Value { get; set; } + } +} \ No newline at end of file diff --git a/src/ObserverPattern/StockUpdateEvents/StockSubject.cs b/src/ObserverPattern/StockUpdateEvents/StockSubject.cs new file mode 100644 index 0000000..db10ba8 --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/StockSubject.cs @@ -0,0 +1,18 @@ +using System; + +namespace ObserverPattern.StockUpdateEvents +{ + /// + /// Subject + /// + public class StockSubject + { + + public void UpdateStockValue(Stock s) + { + StockUpdated?.Invoke(this, new StockUpdateEventArgs(s)); + } + + public event EventHandler StockUpdated; + } +} \ No newline at end of file diff --git a/src/ObserverPattern/StockUpdateEvents/StockUpdateEventArgs.cs b/src/ObserverPattern/StockUpdateEvents/StockUpdateEventArgs.cs new file mode 100644 index 0000000..5f4d44b --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/StockUpdateEventArgs.cs @@ -0,0 +1,12 @@ +namespace ObserverPattern.StockUpdateEvents +{ + public class StockUpdateEventArgs + { + public Stock Stock { get; set; } + + public StockUpdateEventArgs(Stock stock) + { + Stock = stock; + } + } +} \ No newline at end of file diff --git a/src/ObserverPattern/StockUpdateEvents/StockUpdateEventsExample.cs b/src/ObserverPattern/StockUpdateEvents/StockUpdateEventsExample.cs new file mode 100644 index 0000000..bb2b740 --- /dev/null +++ b/src/ObserverPattern/StockUpdateEvents/StockUpdateEventsExample.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Threading.Tasks; + +namespace ObserverPattern.StockUpdateEvents +{ + public class StockUpdateEventsExample + { + public void RunSimple() + { + StockSubject stockSubject = new StockSubject(); + + LondonStockObserver londonObserver = new LondonStockObserver(stockSubject); + + AaplObserver aaplObserver = new AaplObserver(stockSubject); + + PublishUpdateStocks(stockSubject); + } + + public void RunReactive() + { + StockSubject stockSubject = new StockSubject(); + + var londonObserver = Observable.FromEventPattern( + ev => stockSubject.StockUpdated += ev, + ev => stockSubject.StockUpdated -= ev).Where(s => s.EventArgs.Stock.Name == "FTSE"); + + var aaplObserver = Observable.FromEventPattern( + ev => stockSubject.StockUpdated += ev, + ev => stockSubject.StockUpdated -= ev).Where(s => s.EventArgs.Stock.Name == "AAPL"); + + using (londonObserver.Subscribe(PrintLondonStockPriceUpdate)) + using (aaplObserver.Subscribe(PrintAaplStockPriceUpdate)) + { + PublishUpdateStocks(stockSubject); + } + } + + private void PrintAaplStockPriceUpdate(EventPattern eventP) + { + Console.WriteLine("apple updated, new value: {0}", eventP.EventArgs.Stock.Value); + } + + private void PrintLondonStockPriceUpdate(EventPattern eventP) + { + Console.WriteLine("london stock updated, new value: {0}", eventP.EventArgs.Stock.Value); + } + + private static void PublishUpdateStocks(StockSubject stockSubject) + { + stockSubject.UpdateStockValue(new Stock { Name = "AAPL", Value = 3 }); + stockSubject.UpdateStockValue(new Stock { Name = "AAPL", Value = 4 }); + stockSubject.UpdateStockValue(new Stock { Name = "AAPL", Value = 5 }); + stockSubject.UpdateStockValue(new Stock { Name = "AAPL", Value = 6 }); + + stockSubject.UpdateStockValue(new Stock { Name = "FTSE", Value = 6 }); + stockSubject.UpdateStockValue(new Stock { Name = "FTSE", Value = 12 }); + stockSubject.UpdateStockValue(new Stock { Name = "FTSE", Value = 2 }); + stockSubject.UpdateStockValue(new Stock { Name = "FTSE", Value = 3 }); + } + } +} diff --git a/src/ObserverPattern/Twits/ObservableTwitsExample.cs b/src/ObserverPattern/Twits/ObservableTwitsExample.cs index aeff52f..29d4870 100644 --- a/src/ObserverPattern/Twits/ObservableTwitsExample.cs +++ b/src/ObserverPattern/Twits/ObservableTwitsExample.cs @@ -11,16 +11,16 @@ namespace ObserverPattern.Twits { TwitObservable observable = new TwitObservable(); - TwitUser t100 = new TwitUser("t100", observable); - TwitUser r2d2 = new TwitUser("R2-D2", observable); + using (TwitUser t100 = new TwitUser("t100", observable)) + using (TwitUser r2d2 = new TwitUser("R2-D2", observable)) + { + t100.Twit("El chupacapra - BOOM BOOM"); - t100.Twit("El chupacapra - BOOM BOOM"); + r2d2.Twit("Vamos vamos mi amor"); - r2d2.Twit("Vamos vamos mi amor"); - - t100.Dispose(); - - observable.ItsGoingHomeTime(); + observable.ItsGoingHomeTime(); + } + } } } diff --git a/src/ObserverPattern/Twits/TwitObservable.cs b/src/ObserverPattern/Twits/TwitObservable.cs index 75a9ff2..c5cc320 100644 --- a/src/ObserverPattern/Twits/TwitObservable.cs +++ b/src/ObserverPattern/Twits/TwitObservable.cs @@ -20,7 +20,7 @@ namespace ObserverPattern.Twits if (!observers.Contains(observer)) observers.Add(observer); - return new Unsubscriber(observers, observer); + return new Unsubscriber(observers, observer); } public void AddTwit(Twit twit) @@ -37,23 +37,6 @@ namespace ObserverPattern.Twits { observer.OnCompleted(); } - } - - private class Unsubscriber : IDisposable - { - private List> _observers; - private IObserver _observer; - - public Unsubscriber(List> observers, IObserver observer) - { - _observers = observers; - _observer = observer; - } - - public void Dispose() - { - if (!(_observer == null)) _observers.Remove(_observer); - } } } diff --git a/src/ObserverPattern/Unsubscriber.cs b/src/ObserverPattern/Unsubscriber.cs new file mode 100644 index 0000000..6e16cee --- /dev/null +++ b/src/ObserverPattern/Unsubscriber.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ObserverPattern +{ + public class Unsubscriber : IDisposable + { + private List> _observers; + private IObserver _observer; + + public Unsubscriber(List> observers, IObserver observer) + { + _observers = observers; + _observer = observer; + } + + public void Dispose() + { + if (!(_observer == null)) _observers.Remove(_observer); + } + } +} diff --git a/src/ObserverPattern/project.json b/src/ObserverPattern/project.json index b5b5a5c..9742072 100644 --- a/src/ObserverPattern/project.json +++ b/src/ObserverPattern/project.json @@ -2,7 +2,8 @@ "version": "1.0.0-*", "dependencies": { - "NETStandard.Library": "1.6.0" + "NETStandard.Library": "1.6.0", + "System.Reactive": "3.0.0" }, "frameworks": {