diff --git a/BehavioralPatterns.sln b/BehavioralPatterns.sln
index 4b15cf4..cb539ee 100644
--- a/BehavioralPatterns.sln
+++ b/BehavioralPatterns.sln
@@ -14,6 +14,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BehavioralPatterns", "src\B
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ChainOfResponssibility", "src\ChainOfResponssibility\ChainOfResponssibility.xproj", "{89536824-683F-4351-8789-406D7BDD922D}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CommandPattern", "src\CommandPattern\CommandPattern.xproj", "{454B2A43-8251-4667-8DE3-67E489908DB9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -28,6 +30,10 @@ Global
{89536824-683F-4351-8789-406D7BDD922D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89536824-683F-4351-8789-406D7BDD922D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89536824-683F-4351-8789-406D7BDD922D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {454B2A43-8251-4667-8DE3-67E489908DB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {454B2A43-8251-4667-8DE3-67E489908DB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {454B2A43-8251-4667-8DE3-67E489908DB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {454B2A43-8251-4667-8DE3-67E489908DB9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -35,5 +41,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{E3092EE0-1282-4AB4-9FA2-0338348D8FD1} = {3820200F-354C-41E6-8F34-B301F5D621C2}
{89536824-683F-4351-8789-406D7BDD922D} = {3820200F-354C-41E6-8F34-B301F5D621C2}
+ {454B2A43-8251-4667-8DE3-67E489908DB9} = {3820200F-354C-41E6-8F34-B301F5D621C2}
EndGlobalSection
EndGlobal
diff --git a/src/BehavioralPatterns/Program.cs b/src/BehavioralPatterns/Program.cs
index a8c7f30..9cd1b02 100644
--- a/src/BehavioralPatterns/Program.cs
+++ b/src/BehavioralPatterns/Program.cs
@@ -1,5 +1,6 @@
using ChainOfResponssibility;
using ChainOfResponssibility.PurchaseExample;
+using CommandPattern;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -16,6 +17,8 @@ namespace BehavioralPatterns
//This is usefull when you have a request and you don't know who should process it
ChainOfResponsibillityExamples.Run();
Console.ReadKey();
+ CommandPatternExamples.Run();
+ Console.ReadKey();
}
}
}
diff --git a/src/BehavioralPatterns/project.json b/src/BehavioralPatterns/project.json
index f111bfd..ba86bcd 100644
--- a/src/BehavioralPatterns/project.json
+++ b/src/BehavioralPatterns/project.json
@@ -6,6 +6,7 @@
"dependencies": {
"ChainOfResponssibility": "1.0.0-*",
+ "CommandPattern": "1.0.0-*",
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0-rc2-3002702"
diff --git a/src/CommandPattern/CommandPattern.xproj b/src/CommandPattern/CommandPattern.xproj
new file mode 100644
index 0000000..4eff81a
--- /dev/null
+++ b/src/CommandPattern/CommandPattern.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 454b2a43-8251-4667-8de3-67e489908db9
+ CommandPattern
+ .\obj
+ .\bin\
+ v4.6.1
+
+
+
+ 2.0
+
+
+
diff --git a/src/CommandPattern/CommandPatternExamples.cs b/src/CommandPattern/CommandPatternExamples.cs
new file mode 100644
index 0000000..f174510
--- /dev/null
+++ b/src/CommandPattern/CommandPatternExamples.cs
@@ -0,0 +1,45 @@
+using CommandPattern.StocksExample;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern
+{
+ public class CommandPatternExamples
+ {
+ public static void Run()
+ {
+ Console.WriteLine(GetPatternDescription());
+ GoToNextStep();
+
+ StockExampleRunner stockExampleRunner = new StockExampleRunner();
+ Console.WriteLine(stockExampleRunner.GetDescriptionOfExample());
+ GoToNextStep();
+ stockExampleRunner.Run();
+ stockExampleRunner.Run();
+ stockExampleRunner.Run();
+ stockExampleRunner.Run();
+ }
+
+ private static void GoToNextStep()
+ {
+ Console.ReadKey();
+ Console.Clear();
+ }
+
+ public static string GetPatternDescription()
+ {
+ return @"
+command pattern is a behavioral design pattern in which an object is used to encapsulate
+all information needed to perform an action or trigger an event at a later time
+Uses:
+1. Macro recording: f all user actions are represented by command objects, a program can record a
+sequence of actions simply by keeping a list of the command objects as they are executed.
+2. Undo
+3. GUI buttons and menu items
+4. Parallel processing
+5. Transactional behavior ";
+ }
+ }
+}
diff --git a/src/CommandPattern/Program.cs b/src/CommandPattern/Program.cs
new file mode 100644
index 0000000..49f01ca
--- /dev/null
+++ b/src/CommandPattern/Program.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ }
+ }
+}
diff --git a/src/CommandPattern/Properties/AssemblyInfo.cs b/src/CommandPattern/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d534ac9
--- /dev/null
+++ b/src/CommandPattern/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("CommandPattern")]
+[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("454b2a43-8251-4667-8de3-67e489908db9")]
diff --git a/src/CommandPattern/StocksExample/Agent.cs b/src/CommandPattern/StocksExample/Agent.cs
new file mode 100644
index 0000000..5b67170
--- /dev/null
+++ b/src/CommandPattern/StocksExample/Agent.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern.StocksExample
+{
+ ///
+ /// Invoker. The invoker will invoke the command
+ ///
+ public class Agent
+ {
+ Stack ordersNotExecuted;
+ FixedSizedQueue ordersPlaced;
+ StockSchedule stockSchedule;
+
+ public Agent(StockSchedule stockSchedule)
+ {
+ ordersNotExecuted = new Stack();
+ ordersPlaced = new FixedSizedQueue(10);
+ this.stockSchedule = stockSchedule;
+ stockSchedule.StockExchangedOpened += StockSchedule_StockExchangedOpened;
+ }
+
+ private void StockSchedule_StockExchangedOpened(object sender, EventArgs e)
+ {
+ while (ordersNotExecuted.Any())
+ {
+ PlaceOrder(ordersNotExecuted.Pop());
+ }
+ }
+
+ public void PlaceOrder(Order order)
+ {
+ if(stockSchedule.IsStockOpen())
+ {
+ order.Execute();
+ ordersPlaced.Enqueue(order);
+ }
+ else
+ {
+ Console.WriteLine("Market is not opened, so the order was saved for later");
+ ordersNotExecuted.Push(order);
+ }
+ }
+ }
+}
diff --git a/src/CommandPattern/StocksExample/Commands/BuyStockOrder.cs b/src/CommandPattern/StocksExample/Commands/BuyStockOrder.cs
new file mode 100644
index 0000000..8d70918
--- /dev/null
+++ b/src/CommandPattern/StocksExample/Commands/BuyStockOrder.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern.StocksExample.Commands
+{
+ //Concrete Command
+ public class BuyStockOrder : Order
+ {
+ StocksAPI stocksAPI;
+ Stock stock;
+ public BuyStockOrder(StocksAPI stocksAPI, Stock stock)
+ {
+ this.stocksAPI = stocksAPI;
+ this.stock = stock;
+ }
+
+ public void Execute()
+ {
+ stocksAPI.Buy(stock);
+ }
+ }
+}
diff --git a/src/CommandPattern/StocksExample/Commands/SellStockOrder.cs b/src/CommandPattern/StocksExample/Commands/SellStockOrder.cs
new file mode 100644
index 0000000..9cabb88
--- /dev/null
+++ b/src/CommandPattern/StocksExample/Commands/SellStockOrder.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern.StocksExample.Commands
+{
+ public class SellStockOrder : Order
+ {
+ StocksAPI stocksAPI;
+ Stock stock;
+ public SellStockOrder(StocksAPI stocksAPI, Stock stock)
+ {
+ this.stocksAPI = stocksAPI;
+ this.stock = stock;
+ }
+
+ public void Execute()
+ {
+ stocksAPI.Sell(stock);
+ }
+ }
+}
diff --git a/src/CommandPattern/StocksExample/FixedSizedQueue.cs b/src/CommandPattern/StocksExample/FixedSizedQueue.cs
new file mode 100644
index 0000000..18b2cb4
--- /dev/null
+++ b/src/CommandPattern/StocksExample/FixedSizedQueue.cs
@@ -0,0 +1,29 @@
+using System.Collections.Concurrent;
+
+namespace CommandPattern.StocksExample
+{
+ public class FixedSizedQueue : ConcurrentQueue
+ {
+ private readonly object syncObject = new object();
+
+ public int Size { get; private set; }
+
+ public FixedSizedQueue(int size)
+ {
+ Size = size;
+ }
+
+ public new void Enqueue(T obj)
+ {
+ base.Enqueue(obj);
+ lock (syncObject)
+ {
+ while (Count > Size)
+ {
+ T outObj;
+ TryDequeue(out outObj);
+ }
+ }
+ }
+ }
+}
diff --git a/src/CommandPattern/StocksExample/Order.cs b/src/CommandPattern/StocksExample/Order.cs
new file mode 100644
index 0000000..82079d9
--- /dev/null
+++ b/src/CommandPattern/StocksExample/Order.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern.StocksExample
+{
+ ///
+ /// Command interface
+ ///
+ public interface Order
+ {
+ void Execute();
+ }
+}
diff --git a/src/CommandPattern/StocksExample/Stock.cs b/src/CommandPattern/StocksExample/Stock.cs
new file mode 100644
index 0000000..251b74b
--- /dev/null
+++ b/src/CommandPattern/StocksExample/Stock.cs
@@ -0,0 +1,8 @@
+namespace CommandPattern.StocksExample
+{
+ public class Stock
+ {
+ public string Name { get; set; }
+ public int Quantity { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/CommandPattern/StocksExample/StockExampleRunner.cs b/src/CommandPattern/StocksExample/StockExampleRunner.cs
new file mode 100644
index 0000000..d51efb6
--- /dev/null
+++ b/src/CommandPattern/StocksExample/StockExampleRunner.cs
@@ -0,0 +1,30 @@
+using CommandPattern.StocksExample.Commands;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CommandPattern.StocksExample
+{
+ public class StockExampleRunner
+ {
+ public void Run()
+ {
+ StocksAPI stocksAPI = new StocksAPI();
+ Agent agent = new Agent(new StockSchedule());
+
+ Stock stock = new Stock { Name = "AAPL", Quantity = 20 };
+
+ agent.PlaceOrder(new BuyStockOrder(stocksAPI, stock));
+ agent.PlaceOrder(new SellStockOrder(stocksAPI, stock));
+ }
+
+ public string GetDescriptionOfExample()
+ {
+ return @"
+Buy or sell a stock on the market.
+If the market is closed save the orders for when the market opens again.
+When the market opens place all the orders.";
+ }
+ }
+}
diff --git a/src/CommandPattern/StocksExample/StockSchedule.cs b/src/CommandPattern/StocksExample/StockSchedule.cs
new file mode 100644
index 0000000..1e33321
--- /dev/null
+++ b/src/CommandPattern/StocksExample/StockSchedule.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Threading;
+
+namespace CommandPattern.StocksExample
+{
+ public class StockSchedule
+ {
+ TimeSpan openingTime;
+ public StockSchedule()
+ {
+ CheckForOpeningOfStockExchange();
+ openingTime = new TimeSpan(9, 0, 0);
+ }
+
+ public event EventHandler StockExchangedOpened;
+
+ public bool IsStockOpen()
+ {
+ return new Random().NextDouble() > 0.5;
+ }
+
+ private void OnStockExchangedOpened(object state)
+ {
+ StockExchangedOpened?.Invoke(this, null);
+ Thread.Sleep(1);
+ CheckForOpeningOfStockExchange();
+
+ }
+
+ private void CheckForOpeningOfStockExchange()
+ {
+ var t = new Timer(new TimerCallback(OnStockExchangedOpened), null, Timeout.Infinite, Timeout.Infinite);
+
+ // Figure how much time until opening market
+ DateTime now = DateTime.Now;
+ DateTime openingTime = DateTime.Today.Add(this.openingTime);
+
+ // If it's already past opening time, wait until opening time tomorrow
+ if (now > openingTime)
+ {
+ openingTime = openingTime.AddDays(1.0);
+ }
+
+ int msUntilOpeningHour = (int)((openingTime - now).TotalMilliseconds);
+
+ // Set the timer to elapse only once, at opening time.
+ t.Change(msUntilOpeningHour, Timeout.Infinite);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CommandPattern/StocksExample/StocksAPI.cs b/src/CommandPattern/StocksExample/StocksAPI.cs
new file mode 100644
index 0000000..1c48457
--- /dev/null
+++ b/src/CommandPattern/StocksExample/StocksAPI.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace CommandPattern.StocksExample
+{
+ ///
+ /// Receiver of the command. Concrete command is executing code from this class
+ ///
+ public class StocksAPI
+ {
+ public void Buy(Stock stock)
+ {
+ Console.WriteLine("Stock [ Name: {0}, Quantity: {1} bought", stock.Name, stock.Quantity);
+ }
+
+ public void Sell(Stock stock)
+ {
+ Console.WriteLine("Stock [ Name: {0}, Quantity: {1} sold", stock.Name, stock.Quantity);
+ }
+ }
+}
diff --git a/src/CommandPattern/project.json b/src/CommandPattern/project.json
new file mode 100644
index 0000000..10c7989
--- /dev/null
+++ b/src/CommandPattern/project.json
@@ -0,0 +1,19 @@
+{
+ "version": "1.0.0-*",
+ "buildOptions": {
+ "emitEntryPoint": true
+ },
+
+ "dependencies": {
+ "Microsoft.NETCore.App": {
+ "type": "platform",
+ "version": "1.0.0-rc2-3002702"
+ }
+ },
+
+ "frameworks": {
+ "netcoreapp1.0": {
+ "imports": "dnxcore50"
+ }
+ }
+}