Add command pattern

This commit is contained in:
Petrutiu Mihai
2016-06-22 16:24:25 +03:00
parent 90fd5764d4
commit 1ab706016e
17 changed files with 375 additions and 0 deletions

View File

@@ -14,6 +14,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BehavioralPatterns", "src\B
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ChainOfResponssibility", "src\ChainOfResponssibility\ChainOfResponssibility.xproj", "{89536824-683F-4351-8789-406D7BDD922D}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ChainOfResponssibility", "src\ChainOfResponssibility\ChainOfResponssibility.xproj", "{89536824-683F-4351-8789-406D7BDD922D}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CommandPattern", "src\CommandPattern\CommandPattern.xproj", "{454B2A43-8251-4667-8DE3-67E489908DB9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{89536824-683F-4351-8789-406D7BDD922D}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -35,5 +41,6 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{E3092EE0-1282-4AB4-9FA2-0338348D8FD1} = {3820200F-354C-41E6-8F34-B301F5D621C2} {E3092EE0-1282-4AB4-9FA2-0338348D8FD1} = {3820200F-354C-41E6-8F34-B301F5D621C2}
{89536824-683F-4351-8789-406D7BDD922D} = {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 EndGlobalSection
EndGlobal EndGlobal

View File

@@ -1,5 +1,6 @@
using ChainOfResponssibility; using ChainOfResponssibility;
using ChainOfResponssibility.PurchaseExample; using ChainOfResponssibility.PurchaseExample;
using CommandPattern;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; 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 //This is usefull when you have a request and you don't know who should process it
ChainOfResponsibillityExamples.Run(); ChainOfResponsibillityExamples.Run();
Console.ReadKey(); Console.ReadKey();
CommandPatternExamples.Run();
Console.ReadKey();
} }
} }
} }

View File

@@ -6,6 +6,7 @@
"dependencies": { "dependencies": {
"ChainOfResponssibility": "1.0.0-*", "ChainOfResponssibility": "1.0.0-*",
"CommandPattern": "1.0.0-*",
"Microsoft.NETCore.App": { "Microsoft.NETCore.App": {
"type": "platform", "type": "platform",
"version": "1.0.0-rc2-3002702" "version": "1.0.0-rc2-3002702"

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>454b2a43-8251-4667-8de3-67e489908db9</ProjectGuid>
<RootNamespace>CommandPattern</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -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 ";
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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")]

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CommandPattern.StocksExample
{
/// <summary>
/// Invoker. The invoker will invoke the command
/// </summary>
public class Agent
{
Stack<Order> ordersNotExecuted;
FixedSizedQueue<Order> ordersPlaced;
StockSchedule stockSchedule;
public Agent(StockSchedule stockSchedule)
{
ordersNotExecuted = new Stack<Order>();
ordersPlaced = new FixedSizedQueue<Order>(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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Concurrent;
namespace CommandPattern.StocksExample
{
public class FixedSizedQueue<T> : ConcurrentQueue<T>
{
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);
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CommandPattern.StocksExample
{
/// <summary>
/// Command interface
/// </summary>
public interface Order
{
void Execute();
}
}

View File

@@ -0,0 +1,8 @@
namespace CommandPattern.StocksExample
{
public class Stock
{
public string Name { get; set; }
public int Quantity { get; set; }
}
}

View File

@@ -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.";
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace CommandPattern.StocksExample
{
/// <summary>
/// Receiver of the command. Concrete command is executing code from this class
/// </summary>
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);
}
}
}

View File

@@ -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"
}
}
}