diff --git a/BehavioralPatterns.sln b/BehavioralPatterns.sln index eed44f8..469ce7f 100644 --- a/BehavioralPatterns.sln +++ b/BehavioralPatterns.sln @@ -31,6 +31,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TemplatePattern", "src\Temp EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "VisitorPattern", "src\VisitorPattern\VisitorPattern.xproj", "{CDDB889F-3038-4796-95B1-47E1834DA93D}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BehavioralPatterns", "src\BehavioralPatterns\BehavioralPatterns.xproj", "{E3092EE0-1282-4AB4-9FA2-0338348D8FD1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +79,10 @@ Global {CDDB889F-3038-4796-95B1-47E1834DA93D}.Debug|Any CPU.Build.0 = Debug|Any CPU {CDDB889F-3038-4796-95B1-47E1834DA93D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDDB889F-3038-4796-95B1-47E1834DA93D}.Release|Any CPU.Build.0 = Release|Any CPU + {E3092EE0-1282-4AB4-9FA2-0338348D8FD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3092EE0-1282-4AB4-9FA2-0338348D8FD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3092EE0-1282-4AB4-9FA2-0338348D8FD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3092EE0-1282-4AB4-9FA2-0338348D8FD1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,5 +98,6 @@ Global {01B9D869-AF89-4919-8445-79206848FB5F} = {3820200F-354C-41E6-8F34-B301F5D621C2} {E657BF85-C23A-46DE-B837-6939D51C3321} = {3820200F-354C-41E6-8F34-B301F5D621C2} {CDDB889F-3038-4796-95B1-47E1834DA93D} = {3820200F-354C-41E6-8F34-B301F5D621C2} + {E3092EE0-1282-4AB4-9FA2-0338348D8FD1} = {3820200F-354C-41E6-8F34-B301F5D621C2} EndGlobalSection EndGlobal diff --git a/src/StatePattern/ScrumExample/ScrumMotivationalExample.cs b/src/StatePattern/ScrumExample/ScrumMotivationalExample.cs index 7696578..6a9f602 100644 --- a/src/StatePattern/ScrumExample/ScrumMotivationalExample.cs +++ b/src/StatePattern/ScrumExample/ScrumMotivationalExample.cs @@ -39,7 +39,7 @@ namespace StatePattern.ScrumExample /// User story states: New, Active, Resolved, Closed, Removed /// Actions on user stories: /// Create - creates a new user story in state New - /// RemoveFromBacklog - moves a user story from state New to State removed + /// RemoveFromBacklog - moves a user story from state New to State Removed /// StartImplementation - moves user story from state New to state Active /// MoveToBacklog - moves user story from state Active/Removed to state New /// CodeFinishedAnUnitTestsPassed - move user story from state Active to state Resolved diff --git a/src/StatePattern/ScrumExample/ScrumStatePatternExample.cs b/src/StatePattern/ScrumExample/ScrumStatePatternExample.cs index db7338c..c9cb06b 100644 --- a/src/StatePattern/ScrumExample/ScrumStatePatternExample.cs +++ b/src/StatePattern/ScrumExample/ScrumStatePatternExample.cs @@ -25,10 +25,13 @@ namespace StatePattern.ScrumExample //but leaving that asside, assuming we will have to add another transition availalable that can be executed only from one state //we will have to modify all the existing states, just to throw the exception. - + //To view what are the allowed transitions, is very hard when using state pattern with roles } } + /// + /// State interface + /// public interface IScrumState { void RemoveFromBacklog(); @@ -41,11 +44,25 @@ namespace StatePattern.ScrumExample void AcceptanceTestsPassed(); - void CodeFinishedAnUnitTestsPassed(); + void CodeFinishedAndUnitTestsPassed(); } + /// + /// Context + /// User story states: New, Active, Resolved, Closed, Removed + /// Actions on user stories: + /// Create - creates a new user story in state New + /// RemoveFromBacklog - moves a user story from state New to State removed + /// StartImplementation - moves user story from state New to state Active + /// MoveToBacklog - moves user story from state Active/Removed to state New + /// CodeFinishedAndUnitTestsPassed - move user story from state Active to state Resolved + /// AcceptanceTestsFail - move user story from state Resolved to state Active + /// AcceptanceTestsPassed - moves user story from state Resolved to state Closed + /// public class UserStory { + public IScrumState State { get; set; } + public IScrumState New { get; private set; } public IScrumState Active { get; private set; } public IScrumState Resolved { get; private set; } @@ -62,7 +79,6 @@ namespace StatePattern.ScrumExample State = New; } - public IScrumState State { get; set; } public int Name { get; internal set; } public void AcceptanceTestsFail() @@ -89,12 +105,15 @@ namespace StatePattern.ScrumExample State.StartImplementation(); } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { - State.CodeFinishedAnUnitTestsPassed(); + State.CodeFinishedAndUnitTestsPassed(); } } + /// + /// Concrete State + /// internal class ScrumStateNew : IScrumState { private UserStory userStory; @@ -116,7 +135,7 @@ namespace StatePattern.ScrumExample Console.WriteLine("Development didn't even started, you should check your tests"); } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { //throw the exception Console.WriteLine("Before you can finish the code, you should have started implementation"); @@ -130,8 +149,8 @@ namespace StatePattern.ScrumExample public void RemoveFromBacklog() { - //throw the exception Console.WriteLine("User story removed"); + userStory.State = userStory.Removed; } public void StartImplementation() @@ -141,6 +160,9 @@ namespace StatePattern.ScrumExample } } + /// + /// Concrete State + /// internal class ScrumStateActive : IScrumState { private UserStory userStory; @@ -162,7 +184,7 @@ namespace StatePattern.ScrumExample Console.WriteLine("Development is not yet done"); } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { Console.WriteLine("I'll notify the testers!"); userStory.State = userStory.Resolved; @@ -170,8 +192,8 @@ namespace StatePattern.ScrumExample public void MoveToBacklog() { - //throw the exception Console.WriteLine("Moved userstory to backlog"); + userStory.State = userStory.New; } public void RemoveFromBacklog() @@ -187,6 +209,9 @@ namespace StatePattern.ScrumExample } } + /// + /// Concrete State + /// internal class ScrumStateResolved : IScrumState { private UserStory userStory; @@ -208,7 +233,7 @@ namespace StatePattern.ScrumExample userStory.State = userStory.Closed; } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { //throw the exception Console.WriteLine("The item was already resolved"); @@ -233,6 +258,9 @@ namespace StatePattern.ScrumExample } } + /// + /// Concrete State + /// internal class ScrumStateClosed : IScrumState { private UserStory userStory; @@ -254,7 +282,7 @@ namespace StatePattern.ScrumExample Console.WriteLine("User story is already closed"); } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { //throw the exception Console.WriteLine("Item was already closed"); @@ -262,6 +290,7 @@ namespace StatePattern.ScrumExample public void MoveToBacklog() { + //throw the exception Console.WriteLine("Item was already closed, cannot move to new state"); } @@ -278,6 +307,9 @@ namespace StatePattern.ScrumExample } } + /// + /// Concrete State + /// internal class ScrumStateRemoved : IScrumState { private UserStory userStory; @@ -299,9 +331,9 @@ namespace StatePattern.ScrumExample Console.WriteLine("Item was removed, you can only move it to backlog again"); } - public void CodeFinishedAnUnitTestsPassed() + public void CodeFinishedAndUnitTestsPassed() { - + //throw the exception } public void MoveToBacklog() diff --git a/src/StatePattern/ScrumWithRoleStates/ScrumStateWithRoleInterfacesExample.cs b/src/StatePattern/ScrumWithRoleStates/ScrumStateWithRoleInterfacesExample.cs new file mode 100644 index 0000000..485c884 --- /dev/null +++ b/src/StatePattern/ScrumWithRoleStates/ScrumStateWithRoleInterfacesExample.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Reflection; + +namespace StatePattern.ScrumWithRoleStates +{ + public class ScrumStateWithRoleInterfacesExample + { + public static void Run() + { + UserStory userStory = new UserStory(); + userStory.RemoveFromBacklog(); + userStory.MoveToBacklog(); + userStory.StartImplementation(); + userStory.AcceptanceTestsFail(); + userStory.StartImplementation(); + userStory.AcceptanceTestsPassed(); + userStory.StartImplementation(); + + userStory.CodeFinishedAnUnitTestsPassed(); + + userStory.AcceptanceTestsPassed(); + + Console.WriteLine("User story state: {0}", userStory.State.Name); + + } + + /// + /// Context + /// User story states: New, Active, Resolved, Closed, Removed + /// Actions on user stories: + /// Create - creates a new user story in state New + /// RemoveFromBacklog - moves a user story from state New to State Removed + /// StartImplementation - moves user story from state New to state Active + /// MoveToBacklog - moves user story from state Active/Removed to state New + /// CodeFinishedAndUnitTestsPassed - move user story from state Active to state Resolved + /// AcceptanceTestsFail - move user story from state Resolved to state Active + /// AcceptanceTestsPassed - moves user story from state Resolved to state Closed + /// + public class UserStory + { + public IScrumState State { get; set; } + + public UserStoryStates States { get; private set; } + + public string Name { get; set; } + + public UserStory() + { + States = new UserStoryStates(this); + + State = States.New; + } + + public void MoveToBacklog() + { + ICanMoveToBacklog state = GetState(); + + if(state == null) + { + Console.WriteLine("Cannot move to backlog from state: {0}", State.Name); + return; + } + + state.MoveToBacklog(); + } + + public void StartImplementation() + { + ICanStartImplementation state = GetState(); + + if (state == null) + { + Console.WriteLine("Cannot start implementation from state: {0}", State.Name); + return; + } + + state.StartImplementation(); + } + + public void CodeFinishedAnUnitTestsPassed() + { + ICanFinishCode state = GetState(); + + if (state == null) + { + Console.WriteLine("Cannot mark the code as done from state: {0}", State.Name); + return; + } + + state.CodeFinishedAndUnitTestsPassed(); + } + + public void AcceptanceTestsFail() + { + ICanRunAcceptanceTests state = GetState(); + + if (state == null) + { + Console.WriteLine("Cannot fail the acceptance tests from state: {0}", State.Name); + return; + } + + state.AcceptanceTestsFail(); + } + + public void AcceptanceTestsPassed() + { + ICanRunAcceptanceTests state = GetState(); + + if (state == null) + { + Console.WriteLine("Cannot mark the code as done from state: {0}", State.Name); + return; + } + + state.AcceptanceTestsPassed(); + } + + public void RemoveFromBacklog() + { + ICanRemoveFromBacklog state = GetState(); + + if (state == null) + { + Console.WriteLine("Cannot remove from backlog from state: {0}", State.Name); + return; + } + + state.RemoveFromBacklog(); + } + + private TTransition GetState() + { + if(State is TTransition) + { + return (TTransition)State; + } + + return default(TTransition); + + + } + } + + /// + /// State interface + /// + public interface IScrumState + { + string Name { get; } + } + + public class UserStoryStates + { + public UserStoryStates(UserStory userStory) + { + New = new ScrumStateNew(userStory); + Active = new ScrumStateActive(userStory); + Resolved = new ScrumStateResolved(userStory); + Closed = new ScrumStateClosed(userStory); + Removed = new ScrumStateRemoved(userStory); + + + //Defined but not used, we could use this array to get from what state we can do an operation, by just checking which implements the required interface + AllStates = new IScrumState[] { New, Active, Resolved, Closed, Removed }; + } + public IScrumState[] AllStates { get; private set; } + + public IScrumState New { get; private set; } + + public IScrumState Active { get; private set; } + + public IScrumState Resolved { get; private set; } + + public IScrumState Closed { get; private set; } + + public IScrumState Removed { get; private set; } + } + + + #region role interfaces + internal interface ICanMoveToBacklog + { + void MoveToBacklog(); + } + + + internal interface ICanStartImplementation + { + void StartImplementation(); + } + + internal interface ICanFinishCode + { + void CodeFinishedAndUnitTestsPassed(); + } + + internal interface ICanRunAcceptanceTests + { + void AcceptanceTestsFail(); + + void AcceptanceTestsPassed(); + } + + internal interface ICanRemoveFromBacklog + { + void RemoveFromBacklog(); + } + + #endregion + + #region concrete state implementations + public class ScrumStateNew : IScrumState, ICanRemoveFromBacklog, ICanStartImplementation + { + private UserStory userStory; + + public ScrumStateNew(UserStory userStory) + { + this.userStory = userStory; + } + + public string Name { get { return "New"; } } + + public void RemoveFromBacklog() + { + Console.WriteLine("User story removed"); + userStory.State = userStory.States.Removed; + } + + public void StartImplementation() + { + Console.WriteLine("Started work on User story: {0}", userStory.Name); + userStory.State = userStory.States.Active; + } + } + + public class ScrumStateRemoved : IScrumState, ICanMoveToBacklog + { + private UserStory userStory; + + public ScrumStateRemoved(UserStory userStory) + { + this.userStory = userStory; + } + + public string Name { get { return "Removed"; } } + + public void MoveToBacklog() + { + Console.WriteLine("Moved userstory to backlog"); + userStory.State = userStory.States.New; + } + } + + public class ScrumStateActive : IScrumState, ICanMoveToBacklog, ICanFinishCode + { + private UserStory userStory; + + public ScrumStateActive(UserStory userStory) + { + this.userStory = userStory; + } + + public string Name { get { return "Active"; } } + + public void CodeFinishedAndUnitTestsPassed() + { + Console.WriteLine("I'll notify the testers!"); + userStory.State = userStory.States.Resolved; + } + + public void MoveToBacklog() + { + Console.WriteLine("Moved userstory to backlog"); + userStory.State = userStory.States.New; + } + } + + public class ScrumStateResolved : IScrumState, ICanRunAcceptanceTests + { + private UserStory userStory; + + public ScrumStateResolved(UserStory userStory) + { + this.userStory = userStory; + } + + public string Name { get { return "Resolved"; } } + + public void AcceptanceTestsFail() + { + Console.WriteLine("We'll notify the devs, that they did a bad job"); + userStory.State = userStory.States.Active; + } + + public void AcceptanceTestsPassed() + { + Console.WriteLine("Cool, we'll close the us"); + userStory.State = userStory.States.Closed; + } + } + + public class ScrumStateClosed : IScrumState + { + private UserStory userStory; + + public ScrumStateClosed(UserStory userStory) + { + this.userStory = userStory; + } + + public string Name { get { return "Closed"; } } + } + #endregion + } + + +} diff --git a/src/StatePattern/StatePatternExamples.cs b/src/StatePattern/StatePatternExamples.cs index 70d9a9a..89ccf13 100644 --- a/src/StatePattern/StatePatternExamples.cs +++ b/src/StatePattern/StatePatternExamples.cs @@ -1,5 +1,6 @@ using StatePattern.FanExample; using StatePattern.ScrumExample; +using StatePattern.ScrumWithRoleStates; using StatePattern.TVExample; using System; using System.Collections.Generic; @@ -32,6 +33,9 @@ namespace StatePattern GoToNextStep(); ScrumStatePatternExample.Run(); + + GoToNextStep(); + ScrumStateWithRoleInterfacesExample.Run(); } private static void GoToNextStep()