Lesson Weekend

So far our applications have generally consisted of two classes: a custom class for our logic and a Program class that holds UI logic in a Main() method. As our applications become more interactive, it can be tempting to put more logic in the Main() method. However, we should always endeavor to keep our application logic in one place.

Let's use a simple game as an example. Below is the code for a Game class. The class is simple but there are a few new techniques that are very helpful for more logic-intensive applications.

public class Game
{
  // Private fields
  private string _name;
  private int _pointTotal;
  private string _stateOfMind;

  // Constructor
  public Game(string name)
  {
    _name = name;
    _pointTotal = 0;
    _stateOfMind = "neutral";
  }

  // Getters and Setters for fields
  public string GetName()
  {
    return _name;
  }

  public void SetName(string name)
  {
    _name = name;
  }

  public int GetPointTotal()
  {
    return _pointTotal;
  }

  public void SetPointTotal(int pointTotal)
  {
    _pointTotal = pointTotal;
  }

  public string GetStateOfMind()
  {
    return _stateOfMind;
  }

  public void SetStateOfMind(string stateOfMind)
  {
    _stateOfMind = stateOfMind;
  }

  // Public method
  public void DetermineNextSteps()
  {
    IncreasePoints();
    UpdateMood();
    IsGameOver();
  }

  // Private methods
  private void IncreasePoints()
  {
    _pointTotal++;
  }

  private void UpdateMood()
  {
    if (_pointTotal < 3)
    {
      _stateOfMind = "neutral";
    }
    else if (_pointTotal < 6)
    {
      _stateOfMind = "happy";
    }
    else
    {
      _stateOfMind = "thrilled";
    }
  }

  private string IsGameOver()
  {
    if (_stateOfMind == "thrilled")
    {
      return "You win!";
    }
    else
    {
      return "Keep playing...";
    }
  }
}

In the code above, we use a public method called DetermineNextSteps(). This method in turn calls three other private methods. The DetermineNextSteps() method is the only way an outside user can access the class outside of object instantiation. Calling an instance method within another is very simple. There's no need to use a keyword like this or to call the method on the specific instance. That's because the methods inside DetermineNextSteps() are implicitly being called on the same instance that uses DetermineNextSteps(). Let's look at an example:

Game gameOne = new Game("Lucy");
gameOne.DetermineNextSteps()

All methods called inside gameOne.DetermineNextSteps() will be called on gameOne.

Our DetermineNextSteps() method provides a clean way to separate our code further instead of having a large amount of code inside a single method. This follows the principle of separation of concerns. Each method should ideally have only a single concern. Our private IncreasePoints() method increments points but doesn't care about other aspects of the game state. UpdateMood() modifies the _stateOfMind field. An IsGameOver() method determines whether the game is over or not.

Note that each of these three private methods interacts with a field in some way. Some modify a field while others execute code based on the value of a field.

Just as with methods, we can get or set the field without specifying which instance the field belongs to. Our instance method already knows which instance we want. In the example above, it will look at the fields of the gameOne instance.

These tips are helpful for building more complex C# applications. We always want to keep our methods as short and simple as possible. Each method should ideally have just one concern and be easy to read. Also, when we want a method to be called only inside a class, we should always make it private. For instance, we'd never want a user to be able to manually increment the points of a game so that should be private. Ultimately, as our code becomes more complex, we should always look for ways to keep it simple.

Lesson 9 of 10
Last updated more than 3 months ago.