简体   繁体   中英

How do I create a class that always is the same instance of itself and is used keep track of variable values?

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?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM