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()