From 529a15d5f8b051d566809ad8d0ece7ca432dacd5 Mon Sep 17 00:00:00 2001 From: syneffort Date: Fri, 16 Sep 2022 13:45:51 +0900 Subject: [PATCH] state pattern (vending machine example) --- DesignPattern/DesignPattern.sln | 6 ++ .../State-VendingMachineExample/App.config | 6 ++ .../State-VendingMachineExample/Client.cs | 21 +++++++ .../DispenseChangeState.cs | 38 ++++++++++++ .../DispenseItemState.cs | 39 ++++++++++++ .../HasMoneyState.cs | 46 ++++++++++++++ .../State-VendingMachineExample/Product.cs | 36 +++++++++++ .../State-VendingMachineExample/Program.cs | 18 ++++++ .../Properties/AssemblyInfo.cs | 36 +++++++++++ .../State-VendingMachineExample/ReadyState.cs | 33 ++++++++++ .../State-VendingMachineExample.csproj | 61 +++++++++++++++++++ .../State-VendingMachineExample/VMState.cs | 17 ++++++ .../VendingMachine.cs | 47 ++++++++++++++ 13 files changed, 404 insertions(+) create mode 100644 DesignPattern/State-VendingMachineExample/App.config create mode 100644 DesignPattern/State-VendingMachineExample/Client.cs create mode 100644 DesignPattern/State-VendingMachineExample/DispenseChangeState.cs create mode 100644 DesignPattern/State-VendingMachineExample/DispenseItemState.cs create mode 100644 DesignPattern/State-VendingMachineExample/HasMoneyState.cs create mode 100644 DesignPattern/State-VendingMachineExample/Product.cs create mode 100644 DesignPattern/State-VendingMachineExample/Program.cs create mode 100644 DesignPattern/State-VendingMachineExample/Properties/AssemblyInfo.cs create mode 100644 DesignPattern/State-VendingMachineExample/ReadyState.cs create mode 100644 DesignPattern/State-VendingMachineExample/State-VendingMachineExample.csproj create mode 100644 DesignPattern/State-VendingMachineExample/VMState.cs create mode 100644 DesignPattern/State-VendingMachineExample/VendingMachine.cs diff --git a/DesignPattern/DesignPattern.sln b/DesignPattern/DesignPattern.sln index 93d7f88..512e39d 100644 --- a/DesignPattern/DesignPattern.sln +++ b/DesignPattern/DesignPattern.sln @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Template", "Template\Templa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "State", "State\State.csproj", "{008D8C56-8546-4EB1-83C6-68FAF2051895}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "State-VendingMachineExample", "State-VendingMachineExample\State-VendingMachineExample.csproj", "{ED14B6E2-04BB-4F77-80E7-C5F0E945B750}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -123,6 +125,10 @@ Global {008D8C56-8546-4EB1-83C6-68FAF2051895}.Debug|Any CPU.Build.0 = Debug|Any CPU {008D8C56-8546-4EB1-83C6-68FAF2051895}.Release|Any CPU.ActiveCfg = Release|Any CPU {008D8C56-8546-4EB1-83C6-68FAF2051895}.Release|Any CPU.Build.0 = Release|Any CPU + {ED14B6E2-04BB-4F77-80E7-C5F0E945B750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED14B6E2-04BB-4F77-80E7-C5F0E945B750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED14B6E2-04BB-4F77-80E7-C5F0E945B750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED14B6E2-04BB-4F77-80E7-C5F0E945B750}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DesignPattern/State-VendingMachineExample/App.config b/DesignPattern/State-VendingMachineExample/App.config new file mode 100644 index 0000000..aee9adf --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DesignPattern/State-VendingMachineExample/Client.cs b/DesignPattern/State-VendingMachineExample/Client.cs new file mode 100644 index 0000000..fa56b80 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/Client.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class Client + { + public static void HowToTest() + { + VendingMachine vm = new VendingMachine(); + vm.AddMoney(1); + vm.AddMoney(1); + vm.AddMoney(1); + vm.AddMoney(1); + vm.SelectItem(101); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/DispenseChangeState.cs b/DesignPattern/State-VendingMachineExample/DispenseChangeState.cs new file mode 100644 index 0000000..bd7b262 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/DispenseChangeState.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class DispenseChangeState : VMState + { + public DispenseChangeState(VendingMachine context) + { + vendingMachine = context; + } + + public override void AddMoney(decimal money) + { + throw new ApplicationException(); + } + + public override void ReturnChange(decimal money) + { + // Return charge here + if (vendingMachine.Money > 0) + { + Console.WriteLine($"Return charge ${money}"); + vendingMachine.Money -= money; + } + + vendingMachine.State = vendingMachine.ReadyState; + } + + public override void SelectItem(int itemId) + { + throw new ApplicationException(); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/DispenseItemState.cs b/DesignPattern/State-VendingMachineExample/DispenseItemState.cs new file mode 100644 index 0000000..79ce757 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/DispenseItemState.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class DispenseItemState : VMState + { + public DispenseItemState(VendingMachine context) + { + vendingMachine = context; + } + + public override void AddMoney(decimal money) + { + throw new ApplicationException(); + } + + public override void ReturnChange(decimal money) + { + throw new ApplicationException(); + } + + public override void SelectItem(int itemId) + { + decimal? price = vendingMachine.GetPrice(itemId).Value; + + // Dispense item here + Console.WriteLine($"Dispense Item#{itemId} ({price.Value})"); + + vendingMachine.Money -= price.Value; + + vendingMachine.State = vendingMachine.DispenseChangeState; + vendingMachine.State.ReturnChange(vendingMachine.Money); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/HasMoneyState.cs b/DesignPattern/State-VendingMachineExample/HasMoneyState.cs new file mode 100644 index 0000000..63684ca --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/HasMoneyState.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class HasMoneyState : VMState + { + public HasMoneyState(VendingMachine context) + { + vendingMachine = context; + } + + public override void AddMoney(decimal money) + { + vendingMachine.Money += money; + Console.WriteLine($"Add ${money}, Balance: {vendingMachine.Money}"); + } + + public override void ReturnChange(decimal money) + { + throw new ApplicationException(); + } + + public override void SelectItem(int itemId) + { + decimal? price = vendingMachine.GetPrice(itemId).Value; + if (!price.HasValue) + { + Console.WriteLine($"{itemId} not found"); + return; + } + + if (vendingMachine.Money < price.Value) + { + Console.WriteLine("Insufficient money"); + return; + } + + vendingMachine.State = vendingMachine.DispenseItemState; + vendingMachine.State.SelectItem(itemId); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/Product.cs b/DesignPattern/State-VendingMachineExample/Product.cs new file mode 100644 index 0000000..f7b150f --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/Product.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class Product + { + private List items; + + public Product() + { + items = new List() + { + new Item() { Id = 101, Price = 3.50M }, + new Item() { Id = 201, Price = 4.50M }, + new Item() { Id = 301, Price = 4.50M } + }; + } + + public decimal? GetPrice(int itemId) + { + Item item = items.SingleOrDefault(x => x.Id == itemId); + + return item == null ? null : (decimal?)item.Price; + } + + private class Item + { + public int Id { get; set; } + public decimal Price { get; set; } + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/Program.cs b/DesignPattern/State-VendingMachineExample/Program.cs new file mode 100644 index 0000000..2cc6c27 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/Program.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + internal class Program + { + static void Main(string[] args) + { + Client.HowToTest(); + + Console.ReadKey(); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/Properties/AssemblyInfo.cs b/DesignPattern/State-VendingMachineExample/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8f62eee --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 +// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 +// 이러한 특성 값을 변경하세요. +[assembly: AssemblyTitle("State-VendingMachineExample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("State-VendingMachineExample")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 +// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 +// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. +[assembly: ComVisible(false)] + +// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. +[assembly: Guid("ed14b6e2-04bb-4f77-80e7-c5f0e945b750")] + +// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. +// +// 주 버전 +// 부 버전 +// 빌드 번호 +// 수정 버전 +// +// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를 +// 기본값으로 할 수 있습니다. +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DesignPattern/State-VendingMachineExample/ReadyState.cs b/DesignPattern/State-VendingMachineExample/ReadyState.cs new file mode 100644 index 0000000..d40e7a1 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/ReadyState.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + // Concrete State + internal class ReadyState : VMState + { + public ReadyState(VendingMachine context) + { + vendingMachine = context; + } + + public override void AddMoney(decimal money) + { + vendingMachine.State = vendingMachine.HasMoneyState; + vendingMachine.State.AddMoney(money); + } + + public override void ReturnChange(decimal money) + { + throw new ApplicationException(); + } + + public override void SelectItem(int itemId) + { + throw new ApplicationException(); + } + } +} diff --git a/DesignPattern/State-VendingMachineExample/State-VendingMachineExample.csproj b/DesignPattern/State-VendingMachineExample/State-VendingMachineExample.csproj new file mode 100644 index 0000000..2b31148 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/State-VendingMachineExample.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {ED14B6E2-04BB-4F77-80E7-C5F0E945B750} + Exe + State_VendingMachineExample + State-VendingMachineExample + v4.8.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DesignPattern/State-VendingMachineExample/VMState.cs b/DesignPattern/State-VendingMachineExample/VMState.cs new file mode 100644 index 0000000..4d47328 --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/VMState.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + // State 인터페이스 + internal abstract class VMState + { + protected VendingMachine vendingMachine; + public abstract void AddMoney(decimal money); + public abstract void SelectItem(int itemId); + public abstract void ReturnChange(decimal money); + } +} diff --git a/DesignPattern/State-VendingMachineExample/VendingMachine.cs b/DesignPattern/State-VendingMachineExample/VendingMachine.cs new file mode 100644 index 0000000..576e08b --- /dev/null +++ b/DesignPattern/State-VendingMachineExample/VendingMachine.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace State_VendingMachineExample +{ + // Context + internal class VendingMachine + { + private Product product = new Product(); + internal decimal Money { get; set; } + + internal VMState State { get; set; } + + internal VMState ReadyState { get; private set; } + internal VMState HasMoneyState { get; private set; } + internal VMState DispenseItemState { get; private set; } + internal VMState DispenseChangeState { get; private set; } + + public VendingMachine() + { + this.Money = 0; + this.ReadyState = new ReadyState(this); + this.HasMoneyState = new HasMoneyState(this); + this.DispenseItemState = new DispenseItemState(this); + this.DispenseChangeState = new DispenseChangeState(this); + this.State = this.ReadyState; + } + + public void AddMoney(decimal money) + { + this.State.AddMoney(money); + } + + public void SelectItem(int itemId) + { + this.State.SelectItem(itemId); + } + + internal decimal? GetPrice(int itemId) + { + return product.GetPrice(itemId); + } + } +}