Reputation: 443
So I want to outsource all important variables to a new class. A non working example to showcase what I'm trying to do would be this:
public class GameState{
public bool ToggleA{ get; set; }
public bool ToggleB{ get; set; }
public int a{ get; set; }
public int b{ get; set; }
}
How can I "keep this class alive" once a value was set and make everything refer to the same instance? My google searches led me Singleton patterns (Link, however that didn't bring me further, either I didn't understand how to implement what I want to do into them properly, in that case, an in-depth example would be neat, or I need to use something else?
Upvotes: 1
Views: 89
Reputation: 1157
The best way to implement this is indeed using the singleton design pattern. It will allow your code to remain testable.
Given the following interface:
public interface IGameState
{
bool ToggleA { get; set; }
bool ToggleB { get; set; }
int A { get; set; }
int B { get; set; }
}
The following is a singleton implementation of it:
public class GameState : IGameState
{
// This instance variable will only ever have one copy instantiated
private static GameState _instance = new GameState();
// Private constructor so the class cannot be instantiated outside of the class.
// This ensures that no other class can create an instance of the class.
private GameState() { }
public static GameState Instance { get { return _instance; } }
public bool ToggleA { get; set; }
public bool ToggleB { get; set; }
public int A { get; set; }
public int A { get; set; }
}
You can now inject this singleton wherever you need it, which will maintain the test-ability of your code.
public class SomeObject
{
private IGameState _gameState;
public SomeObject(IGameState gameState)
{
_gameState = gameState;
}
public void SomeOperationOnGameState()
{
_gameState.ToggleA = true;
}
}
EDIT: Updating to explain test related comments
Imagine that your GameState object evolves has a complex method for performing a calculation, and that you want to ensure that this method is called when necessary. If you are using a global static class, there is no way to verify this behavior. If you are programming to an interface however you can mock out this dependency using a mocking framework and verify that the complex method was indeed called.
For example, we are going to extend the IGameState
interface to the following:
public interface IGameState
{
bool ToggleA { get; set; }
bool ToggleB { get; set; }
int A { get; set; }
int B { get; set; }
int ComplexStateAlteringMethod();
}
Now imagine you have some sort of Controller class, that manages key presses:
public class Controller
{
private IGameState _gameState;
public Controller(IGameState gameState)
{
_gameState = gameState;
}
public void KeyPressed(string key)
{
// Implementation details go here, say you want to call the complex method when the 'A' key is pressed
if (key == "A")
{
_gameState.ComplexStateAlteringMethod();
}
}
}
You want to ensure that when the 'A' key is pressed, the IGameState.ComplexStateAlteringMethod()
is called.
Without explaining mocks, i'll create a Stub IGameState
:
public class StubGameState : IGameState
{
public bool ToggleA { get; set; }
public bool ToggleB { get; set; }
public int A { get; set; }
public int B { get; set; }
public bool WasStateAlteringMethodCalled { get; private set; }
public void ComplexStateAlteringMethod()
{
WasStateAltermingMethodCalled = true;
}
}
Now you can use this stub class can be used to verify the behavior you are looking for (Assuming NUnit is the testing framework):
[TestFixture]
public class ControllerTests
{
[Test]
public void KeyPressed_WhenAIsPressed_ShouldCallStateAlteringMethod()
{
// Arrange
var stub = new StubGameState();
var controller = new Controller(stub);
// Act
controller.KeyPressed("A");
// Assert
Assert.IsTrue(stub.WasStateAlteringMethodCalled);
}
[Test]
public void KeyPressed_WhenBIsPressed_ShouldNotCallStateAlteringMethod()
{
// Arrange
var stub = new StubGameState();
var controller = new Controller(stub);
// Act
controller.KeyPressed("B");
// Assert
Assert.IsFalse(stub.WasStateAlteringMethodCalled);
}
}
Upvotes: 1
Reputation: 1319
Make it a static class.
public static class GameState ...
Static classes only have a single instance. You don't new
them. You access them via the class name:
GameState.ToggleA = true;
DoStuff(GameState.b);
etc.
Each of your properties have to be static too:
public static bool ToggleA {get;set;} ...
Static classes are essentially equivalent to the Singleton pattern in this case.
Upvotes: 2