如何创建一个始终与自身实例相同并用于跟踪变量值的类

本文关键字:实例 用于 跟踪 变量值 创建 何创建 一个 | 更新日期: 2023-09-27 17:59:00

所以我想把所有重要的变量外包给一个新的类。一个不起作用的例子来展示我试图做的事情是:

public class GameState{
    public bool ToggleA{ get; set; }
    public bool ToggleB{ get; set; }
    public int a{ get; set; }
    public int b{ get; set; }
}

一旦设置了值,我如何"保持这个类的活力",并使所有内容都引用同一个实例?我在谷歌上的搜索让我找到了辛格尔顿模式(链接,但这并没有让我走得更远,要么我不知道如何正确地将我想做的事情实现到它们中,在这种情况下,一个深入的例子会很好,要么我需要使用其他东西?

如何创建一个始终与自身实例相同并用于跟踪变量值的类

使其成为一个静态类。

public static class GameState ...

静态类只有一个实例。你没有new他们。您可以通过类名访问它们:

GameState.ToggleA = true;
DoStuff(GameState.b);

等等。

您的每个属性也必须是静态的:

public static bool ToggleA {get;set;} ...

在这种情况下,静态类本质上等同于Singleton模式。

实现这一点的最佳方式实际上是使用单例设计模式。它将允许您的代码保持可测试性。

给定以下接口:

public interface IGameState
{
  bool ToggleA { get; set; }
  bool ToggleB { get; set; }
  int A { get; set; }
  int B { get; set; }
}

以下是它的单例实现:

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

现在,您可以在任何需要的地方注入这个singleton,这将保持代码的测试能力。

public class SomeObject
{
  private IGameState _gameState;
  public SomeObject(IGameState gameState)
  {
    _gameState = gameState;
  }
  public void SomeOperationOnGameState()
  {
    _gameState.ToggleA = true;
  }
}

编辑:更新以解释测试相关评论

想象一下,您的GameState对象evolution有一个用于执行计算的复杂方法,并且您希望确保在必要时调用该方法。如果您使用的是全局静态类,则无法验证此行为。然而,如果您正在对接口进行编程,则可以使用mocking框架来模拟这种依赖关系,并验证是否确实调用了复杂方法。

例如,我们将把IGameState接口扩展到以下内容:

public interface IGameState
{
  bool ToggleA { get; set; }
  bool ToggleB { get; set; }
  int A { get; set; }
  int B { get; set; }
  int ComplexStateAlteringMethod();
}

现在假设您有某种Controller类,用于管理按键:

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

您要确保按下"A"键时,会调用IGameState.ComplexStateAlteringMethod()

在不解释mock的情况下,我将创建一个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;
  }
}

现在您可以使用这个存根类来验证您正在寻找的行为(假设NUnit是测试框架):

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