What is the accepted way to enforce proper value in property which depends on other properties of the same class?

.net, c#, oop, properties

Solution

The Framework Design Guidelines recommend that properties should not depend on each other. It should always be possible to set the values of multiple properties in arbitrary order.

Here are the options that is see:

In this specific case:

public class Travel
{
  public int MinAirportArrival { get; set; }
  public int MinFlightTime { get; set; }
  public int AdditionalTravelTime { get; set; }
  public int TotalTravelTime
  {
    get { return MinAirportArrival + MinFlightTime + AdditionalTravelTime; }
  }
}

This takes advantage of the effect that the TotalTravelTime can be reconstructed from the three other values which can be indivually set without dependencies.

A solution for the general case is to accept any value and validate the values when, for example, the instance is sent to storage.

public class Travel
{
  public int MinAirportArrival { get; set; }
  public int MinFlightTime { get; set; }
  public int TravelTime { get; set; }
  public void Save()
  {
    // validate TravelTime > MinAirportArrival + MinFlightTime 
  }
}

Another option is to make the properties read-only and provide a method to batch update the values of the properties.

public class Travel
{
  public int MinAirportArrival { get; private set; }
  public int MinFlightTime { get; private set; }
  public int TravelTime { get; private set; }
  public void UpdateTimes(
    int minAirportArrival, int minFlightTime, int travelTime)
  {
    // validate travelTime > minAirportArrival + minFlightTime 
    MinAirportArrival = minAirportArrival;
    MinFlightTime = minFlightTime;
    TravelTime = travelTime;
  }
}

Alternatively, you can make Travel objects immutable and use a constructor, factory method or mutable builder object to create instances.

Constructor:

public class Travel
{
  public Travel(int minAirportArrival, int minFlightTime, int travelTime)
  {
    // validate travelTime > minAirportArrival + minFlightTime 
  }
  public int MinAirportArrival { get; }
  public int MinFlightTime { get; }
  public int TravelTime { get; }
}

Factory method:

public class Travel
{
  public static Travel CreateTravel(
    int minAirportArrival, int minFlightTime, int travelTime)
  {
    // validate travelTime > minAirportArrival + minFlightTime 
    return new Travel(minAirportArrival, minFlightTime, travelTime);
  }
  private Travel(int minAirportArrival, int minFlightTime, int travelTime);
  public int MinAirportArrival { get; }
  public int MinFlightTime { get; }
  public int TravelTime { get; }
}

Builder class:

public class TravelBuilder
{
  public int MinAirportArrival { get; set; }
  public int MinFlightTime { get; set; }
  public int TravelTime { get; set; }
  public Travel BuildTravel()
  {
    // validate TravelTime > MinAirportArrival + MinFlightTime 
    return new Travel(MinAirportArrival, MinFlightTime, TravelTime);
  }
}

Examples of all three options can be found in the .NET framework.

Problem

Let's say I have a class which has three properties as below. ``` public class Travel { public int MinAirportArrival { get; set; } public int MinFlightTime { get; set; } public int TotalTravelTime { get; set; } } ``` TotalTravelTime must be at least the sum of MinAirportArrival and MinFlightTime but could also be more in the event there is a stopover or something of the sort. It is clear to me that I can put logic in the setter for TotalTravelTime. My question is regarding the changing of MinFlightTime and MinAirportArrival. Is it right to expect that TotalTravelTime be increased first and if not to throw an exception when one of the others will make the sum larger that TotalTravelTime? What are my other options for controlling this in a reasonable fashion? Should I just leave this to the object responsible for saving the state to check a valid property on the class? I may have other logic to put in there as well. EDIT I am not storing anywhere an amount for the extra time if there is any so this is not just a matter of adding up a few properties. Just to clarify this class is just a contrived example of the issue I am facing but I think it matches the problem pretty well.

Original source