diff --git a/BehavioralPatterns.sln b/BehavioralPatterns.sln index 7926f0b..79c9a20 100644 --- a/BehavioralPatterns.sln +++ b/BehavioralPatterns.sln @@ -18,6 +18,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CommandPattern", "src\Comma EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "IteratorPattern", "src\IteratorPattern\IteratorPattern.xproj", "{6F3B7F9A-4D9C-4506-A5F7-3FF5CF4376BD}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MediatorPattern", "src\MediatorPattern\MediatorPattern.xproj", "{2A63BD0A-9D07-4755-9B16-5DDBEB075B80}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +42,10 @@ Global {6F3B7F9A-4D9C-4506-A5F7-3FF5CF4376BD}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F3B7F9A-4D9C-4506-A5F7-3FF5CF4376BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {6F3B7F9A-4D9C-4506-A5F7-3FF5CF4376BD}.Release|Any CPU.Build.0 = Release|Any CPU + {2A63BD0A-9D07-4755-9B16-5DDBEB075B80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A63BD0A-9D07-4755-9B16-5DDBEB075B80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A63BD0A-9D07-4755-9B16-5DDBEB075B80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A63BD0A-9D07-4755-9B16-5DDBEB075B80}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -49,5 +55,6 @@ Global {89536824-683F-4351-8789-406D7BDD922D} = {3820200F-354C-41E6-8F34-B301F5D621C2} {454B2A43-8251-4667-8DE3-67E489908DB9} = {3820200F-354C-41E6-8F34-B301F5D621C2} {6F3B7F9A-4D9C-4506-A5F7-3FF5CF4376BD} = {3820200F-354C-41E6-8F34-B301F5D621C2} + {2A63BD0A-9D07-4755-9B16-5DDBEB075B80} = {3820200F-354C-41E6-8F34-B301F5D621C2} EndGlobalSection EndGlobal diff --git a/src/BehavioralPatterns/Program.cs b/src/BehavioralPatterns/Program.cs index a193950..4f0888d 100644 --- a/src/BehavioralPatterns/Program.cs +++ b/src/BehavioralPatterns/Program.cs @@ -2,6 +2,7 @@ using ChainOfResponssibility.PurchaseExample; using CommandPattern; using IteratorPattern; +using MediatorPattern; using System; using System.Collections.Generic; using System.Diagnostics; @@ -13,8 +14,8 @@ namespace BehavioralPatterns public class Program { public static void Main(string[] args) - { - + { + //Chain of responsibillity //This is usefull when you have a request and you don't know who should process it ChainOfResponsibillityExamples.Run(); @@ -28,6 +29,9 @@ namespace BehavioralPatterns IteratorPatternExamples.Run(); Console.ReadKey(); + + MediatorPatternExamples.Run(); + } } } diff --git a/src/BehavioralPatterns/project.json b/src/BehavioralPatterns/project.json index f699cfa..f0ea6b1 100644 --- a/src/BehavioralPatterns/project.json +++ b/src/BehavioralPatterns/project.json @@ -11,6 +11,7 @@ "ChainOfResponssibility": "1.0.0-*", "CommandPattern": "1.0.0-*", "IteratorPattern": "1.0.0-*", + "MediatorPattern": "1.0.0-*", "Microsoft.NETCore.App": { "type": "platform", "version": "1.0.0-rc2-3002702" diff --git a/src/MediatorPattern/AirTrafficControl/AirTrafficControl.cs b/src/MediatorPattern/AirTrafficControl/AirTrafficControl.cs new file mode 100644 index 0000000..6247386 --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/AirTrafficControl.cs @@ -0,0 +1,59 @@ +using MediatorPattern.AirTrafficControl; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.AirTrafficControl +{ + public class AirTrafficControl : IAirTrafficControlTower + { + List lanes; + object lockLanes = new object(); + public AirTrafficControl(List lanes) + { + this.lanes = lanes; + } + public bool RequestLaneForMaintainance(MaintainerTeam team, Lane lane) + { + var requestedLane = lanes.FirstOrDefault(l => l.ID == lane.ID); + + if (requestedLane == null) + return false; + lock (lockLanes) + { + if (requestedLane.IsAvailable) + { + requestedLane.IsAvailable = false; + Console.WriteLine("Lane {0} is now used by maintainers", lane.ID); + return true; + } + else + { + Console.WriteLine("Lane {0} cannot be used by maintainers because it's already in use", lane.ID); + return false; + } + } + } + + public Optional RequestPermissionToLand(Plane plane) + { + lock (lockLanes) + { + var availableLane = lanes.FirstOrDefault(l => l.IsAvailable); + + if (availableLane != null) + { + availableLane.IsAvailable = false; + Console.WriteLine("Approved landing for plane {0} on lane {1}", plane.ID, availableLane.ID); + return Optional.Of(availableLane); + } + else + { + Console.WriteLine("Landing not approved for plane {0}", plane.ID); + return Optional.Empty; + } + } + } + } +} diff --git a/src/MediatorPattern/AirTrafficControl/AirTrafficControlExample.cs b/src/MediatorPattern/AirTrafficControl/AirTrafficControlExample.cs new file mode 100644 index 0000000..ecc2510 --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/AirTrafficControlExample.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.AirTrafficControl +{ + public class AirTrafficControlExample + { + public void Run() + { + Lane l1 = new Lane(1); + Lane l2 = new Lane(2); + Lane l3 = new Lane(3); + Lane l4 = new Lane(4, isAvailable: false); + + IAirTrafficControlTower controlTower = new AirTrafficControl(new List { l1, l2, l3, l4 }); + + MaintainerTeam m1 = new MaintainerTeam(controlTower); + m1.RequestLane(l1); + + Plane p1a = new Plane("1a", controlTower); + Plane p1b = new Plane("1b", controlTower); + Plane p1c = new Plane("1c", controlTower); + Plane p1d = new Plane("1d", controlTower); + + + p1a.RequestPermissionToLand(); + p1b.RequestPermissionToLand(); + p1c.RequestPermissionToLand(); + p1d.RequestPermissionToLand(); + + } + } +} diff --git a/src/MediatorPattern/AirTrafficControl/IAirTrafficControlTower.cs b/src/MediatorPattern/AirTrafficControl/IAirTrafficControlTower.cs new file mode 100644 index 0000000..79c80f8 --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/IAirTrafficControlTower.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.AirTrafficControl +{ + /// + /// Mediator + /// + public interface IAirTrafficControlTower + { + + Optional RequestPermissionToLand(Plane plane); + + bool RequestLaneForMaintainance(MaintainerTeam team, Lane lane); + + } +} diff --git a/src/MediatorPattern/AirTrafficControl/Lane.cs b/src/MediatorPattern/AirTrafficControl/Lane.cs new file mode 100644 index 0000000..00253ac --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/Lane.cs @@ -0,0 +1,14 @@ +namespace MediatorPattern.AirTrafficControl +{ + public class Lane + { + public Lane(int id, bool isAvailable = true) + { + ID = id; + IsAvailable = isAvailable; + } + public int ID { get; private set; } + + public bool IsAvailable { get; set; } + } +} \ No newline at end of file diff --git a/src/MediatorPattern/AirTrafficControl/MaintainerTeam.cs b/src/MediatorPattern/AirTrafficControl/MaintainerTeam.cs new file mode 100644 index 0000000..259438d --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/MaintainerTeam.cs @@ -0,0 +1,16 @@ +namespace MediatorPattern.AirTrafficControl +{ + public class MaintainerTeam + { + IAirTrafficControlTower airControlTower; + public MaintainerTeam(IAirTrafficControlTower airControlTower) + { + this.airControlTower = airControlTower; + } + + public void RequestLane(Lane lane) + { + airControlTower.RequestLaneForMaintainance(this, lane); + } + } +} \ No newline at end of file diff --git a/src/MediatorPattern/AirTrafficControl/Optional.cs b/src/MediatorPattern/AirTrafficControl/Optional.cs new file mode 100644 index 0000000..5ef700a --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/Optional.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.AirTrafficControl +{ + public class Optional + { + private Optional() { } + + public bool IsPresent { get; private set; } + + private T value; + + public T Value + { + get + { + if (!IsPresent) + throw new InvalidOperationException("Could not get value from empty"); + return value; + } + private set { this.value = value; } + } + + public static Optional Empty + { + get + { + return new Optional() { IsPresent = false }; + } + } + + public static Optional Of(T value) + { + return new Optional() { IsPresent = true, Value = value }; + } + } +} diff --git a/src/MediatorPattern/AirTrafficControl/Plane.cs b/src/MediatorPattern/AirTrafficControl/Plane.cs new file mode 100644 index 0000000..839a06a --- /dev/null +++ b/src/MediatorPattern/AirTrafficControl/Plane.cs @@ -0,0 +1,25 @@ +using System; + +namespace MediatorPattern.AirTrafficControl +{ + public class Plane + { + public string ID { get; private set; } + IAirTrafficControlTower controlTower; + public Plane(string id, IAirTrafficControlTower controlTower) + { + this.controlTower = controlTower; + ID = id; + } + + public void RequestPermissionToLand() + { + var lane = controlTower.RequestPermissionToLand(this); + + if (lane.IsPresent) + Console.WriteLine("Landing"); + else + Console.WriteLine("I will ask again in 5 minutes"); + } + } +} \ No newline at end of file diff --git a/src/MediatorPattern/MediatorPattern.xproj b/src/MediatorPattern/MediatorPattern.xproj new file mode 100644 index 0000000..420d712 --- /dev/null +++ b/src/MediatorPattern/MediatorPattern.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 2a63bd0a-9d07-4755-9b16-5ddbeb075b80 + MediatorPattern + .\obj + .\bin\ + v4.6.1 + + + + 2.0 + + + diff --git a/src/MediatorPattern/MediatorPatternExamples.cs b/src/MediatorPattern/MediatorPatternExamples.cs new file mode 100644 index 0000000..99f28dc --- /dev/null +++ b/src/MediatorPattern/MediatorPatternExamples.cs @@ -0,0 +1,50 @@ +using MediatorPattern.AirTrafficControl; +using MediatorPattern.StockExchange; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern +{ + public class MediatorPatternExamples + { + public static void Run() + { + Console.WriteLine(GetPatternDescription()); + Console.WriteLine(GetActors()); + + //GoToNextStep(); + + //StockExchangeExample stockExample = new StockExchangeExample(); + //stockExample.Run(); + + GoToNextStep(); + AirTrafficControlExample airTraficExample = new AirTrafficControlExample(); + airTraficExample.Run(); + } + + static string GetPatternDescription() + { + return @"Pattern description: +With the mediator pattern, communication between objects is encapsulated with a mediator object. +Objects no longer communicate directly with each other, but instead communicate through the mediator. +This reduces the dependencies between communicating objects, thereby lowering the coupling."; + } + + static string GetActors() + { + return @"Actors: +Mediator: interface of the mediator, that defines what messages does it mediate between colleagues. +Concrete Mediator: implementation of the interface +Colleague: objects that communicate through the mediator +"; + } + + private static void GoToNextStep() + { + Console.ReadKey(); + Console.Clear(); + } + } +} diff --git a/src/MediatorPattern/Properties/AssemblyInfo.cs b/src/MediatorPattern/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..57b7afe --- /dev/null +++ b/src/MediatorPattern/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Hewlett-Packard Company")] +[assembly: AssemblyProduct("MediatorPattern")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2a63bd0a-9d07-4755-9b16-5ddbeb075b80")] diff --git a/src/MediatorPattern/StockExchange/Buyer.cs b/src/MediatorPattern/StockExchange/Buyer.cs new file mode 100644 index 0000000..072192c --- /dev/null +++ b/src/MediatorPattern/StockExchange/Buyer.cs @@ -0,0 +1,16 @@ +namespace MediatorPattern.StockExchange +{ + public class Buyer : Trader + { + IStockExchange stockExchange; + public Buyer(string name, string symbol, int count, double price, IStockExchange stockExchange) : base(name, symbol, count, price) + { + this.stockExchange = stockExchange; + } + + public bool BuyFromStockExchange() + { + return stockExchange.Buy(this, new StockRequest { Count = Count, Price = Price, Symbol = Symbol, Requester = Name }); + } + } +} diff --git a/src/MediatorPattern/StockExchange/IStockExchange.cs b/src/MediatorPattern/StockExchange/IStockExchange.cs new file mode 100644 index 0000000..a8acc8f --- /dev/null +++ b/src/MediatorPattern/StockExchange/IStockExchange.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.StockExchange +{ + /// + /// Mediator + /// + public interface IStockExchange + { + bool Buy(Trader trader, StockRequest request); + + bool Sell(Trader trader, StockRequest request); + } +} diff --git a/src/MediatorPattern/StockExchange/Seller.cs b/src/MediatorPattern/StockExchange/Seller.cs new file mode 100644 index 0000000..39e3758 --- /dev/null +++ b/src/MediatorPattern/StockExchange/Seller.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.StockExchange +{ + public class Seller : Trader + { + IStockExchange stockExchange; + public Seller(string name, string symbol, int count, double price, IStockExchange stockExchange) : base(name, symbol, count, price) + { + this.stockExchange = stockExchange; + } + + public bool SellFromStockExchange() + { + return stockExchange.Sell(this, new StockRequest { Count = Count, Price = Price, Symbol = Symbol, Requester = Name }); + } + } +} diff --git a/src/MediatorPattern/StockExchange/StockExchange.cs b/src/MediatorPattern/StockExchange/StockExchange.cs new file mode 100644 index 0000000..39101b5 --- /dev/null +++ b/src/MediatorPattern/StockExchange/StockExchange.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.StockExchange +{ + public class StockExchange : IStockExchange + { + Dictionary> sellers; + Dictionary> buyers; + + public StockExchange() + { + sellers = new Dictionary>(); + buyers = new Dictionary>(); + + } + protected void AddSeller(Trader trader) + { + AddTrader(trader, sellers); + } + + protected void AddBuyer(Trader trader) + { + AddTrader(trader, buyers); + } + + public bool Buy(Trader trader, StockRequest request) + { + if (!sellers.ContainsKey(request.Symbol)) + { + AddBuyer(trader); + Console.WriteLine("There are no sellers for {0}", request.Symbol); + return false; + } + + List sellersOfStock = sellers[request.Symbol]; + + var couldBuyStocks = sellersOfStock.Any(s => s.AcceptSell(request)); + + if (!couldBuyStocks) + { + AddBuyer(trader); + Console.WriteLine("No one seller sells it at this price, try a higher price"); + } + else + { + Console.WriteLine("Actions bought"); + } + + return couldBuyStocks; + } + + public bool Sell(Trader trader, StockRequest request) + { + if (!buyers.ContainsKey(request.Symbol)) + { + AddSeller(trader); + Console.WriteLine("There are no buyers for {0}", request.Symbol); + return false; + } + + var couldSellStocks = buyers[request.Symbol].Any(b => b.AcceptBuy(request)); + + if (!couldSellStocks) + { + AddSeller(trader); + Console.WriteLine("No one seller buys it at this price, try a lower price"); + } + + + return couldSellStocks; + } + + private void AddTrader(Trader trader, Dictionary> traderList) + { + if (!traderList.ContainsKey(trader.Symbol)) + { + traderList.Add(trader.Symbol, new List { trader }); + } + else + { + traderList[trader.Symbol].Add(trader); + } + } + } +} + diff --git a/src/MediatorPattern/StockExchange/StockExchangeExample.cs b/src/MediatorPattern/StockExchange/StockExchangeExample.cs new file mode 100644 index 0000000..87c2a6f --- /dev/null +++ b/src/MediatorPattern/StockExchange/StockExchangeExample.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.StockExchange +{ + public class StockExchangeExample + { + public void Run() + { + IStockExchange stockExchange = new StockExchange(); + Buyer aaplBuyer = new Buyer("El Ciupi AAPL", "AAPL", 100, 15.0, stockExchange); + var buyResult = aaplBuyer.BuyFromStockExchange(); + + Seller aaplSeller = new Seller("capo di APPL", "AAPL", 100, 16.0, stockExchange); + var sellResult = aaplSeller.SellFromStockExchange(); + + aaplSeller = new Seller("capo di APPL", "AAPL", 100, 14.0, stockExchange); + sellResult = aaplSeller.SellFromStockExchange(); + + } + } +} diff --git a/src/MediatorPattern/StockExchange/StockRequest.cs b/src/MediatorPattern/StockExchange/StockRequest.cs new file mode 100644 index 0000000..682a98f --- /dev/null +++ b/src/MediatorPattern/StockExchange/StockRequest.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediatorPattern.StockExchange +{ + public class StockRequest + { + public string Symbol { get; set; } + + public int Count { get; set; } + + public double Price { get; set; } + + public string Requester { get; set; } + } +} diff --git a/src/MediatorPattern/StockExchange/Trader.cs b/src/MediatorPattern/StockExchange/Trader.cs new file mode 100644 index 0000000..3af9250 --- /dev/null +++ b/src/MediatorPattern/StockExchange/Trader.cs @@ -0,0 +1,47 @@ +using System; + +namespace MediatorPattern.StockExchange +{ + /// + /// Colleague + /// + public class Trader + { + public string Name { get; private set; } + public Trader(string name, string symbol, int count, double price) + { + Name = name; + Symbol = symbol; + Count = count; + Price = price; + } + public string Symbol { get; private set; } + + public int Count { get; private set; } + + public double Price { get; private set; } + + public virtual bool AcceptSell(StockRequest request) + { + if(request.Price >= Price) + { + Console.WriteLine("{0} will sell {1} actions of {2} at the price of {3} to {4}", + Name, request.Count, request.Symbol, request.Price, request.Requester); + return true; + } + return false; + } + + public virtual bool AcceptBuy(StockRequest request) + { + if(request.Price <= Price) + { + Console.WriteLine("{0} will buy {1} actions of {2} at the price of {3} from {4}", + Name, request.Count, request.Symbol, request.Price, request.Requester); + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/MediatorPattern/project.json b/src/MediatorPattern/project.json new file mode 100644 index 0000000..ed8608d --- /dev/null +++ b/src/MediatorPattern/project.json @@ -0,0 +1,13 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-24027" + }, + + "frameworks": { + "netstandard1.5": { + "imports": "dnxcore50" + } + } +}