Tell, don’t Ask

The most languages i develop in these days are OOP (Object Oriented Programming) languages and i try to use the SOLID-principles as much as possible.

The first principle in SOLID is the Single Responsibility Principle (SRP), which states that a class should have only one reason to change. In other words, it should only have one responsibility.

The “Tell, don´t ask” principle is a good rule of thumb when it comes to SRP. Rather than asking an object for data and acting on that data, we should instead tell an object what to do. This will move behavior into an object that is maintaining the data.

This makes it easier to maintain and to test.

Imagine the following code where we “ask” for data:

public class MonitorThreshold
{
    public int MonitorThresholdId { get; set; }
    public int CurrentThreshold { get; set; }
    public int MaxThreshold { get; set; }

    public MonitorThreshold(int monitorThresholdId, int currentThreshold, int maxThreshold)
    {
        MonitorThresholdId = monitorThresholdId;
        CurrentThreshold = currentThreshold;
        MaxThreshold = maxThreshold;

    }
}

public class MonitorService
{
    public void SetMonitorThreshold(int thresholdId, int thresholdValue)
    {
        var repository = new MonitorThresholdRepository();
        var threshold = repository.GetById(thresholdId);

        if (thresholdValue > threshold.MaxThreshold)
            threshold.CurrentThreshold = threshold.MaxThreshold;
        else
            threshold.CurrentThreshold = thresholdValue;

        repository.Save(threshold);

    }
}

public class MonitorThresholdRepository
{
    public MonitorThreshold GetById(int thresholdId)
    {
        return new MonitorThreshold(thresholdId, 1, 5);
    }

    public void Save(MonitorThreshold th)
    {
        // Save the threshold to repository
    }
}

public class Tests
{
    [Fact]
    public void CannotSetMoreThanMaxThresHold()
    {
        // Mock the repository
        // Create monitor service with injected repository
        // Run the method SetMonitorThreshold
        // Check if the saved threshold is set to correct value
    }
}

The MonitorThreshold class is just a dataholder with properties. To validate if a new CurrentThreshold is less or equal to MaxThreshold we need to handle that in the MonitorService.

If i need a MinThreshold property i have to modify both MonitorThreshold and the MonitorService classes, and that does not comply with the single responsibility principle. Also, as you can see, the testing of this will be very cumbersome.

Instead we “tell” the MonitorThreshold what to to. Like this.

public class MonitorThreshold
{
    public int MonitorThresholdId { get; private set; }
    public int CurrentThreshold { get; private set; }
    public int MaxThreshold { get; private set; }

    public MonitorThreshold(int monitorThresholdId, int currentThreshold, int maxThreshold)
    {
        MonitorThresholdId = monitorThresholdId;
        CurrentThreshold = currentThreshold;
        MaxThreshold = maxThreshold;

    }

    public void SetThreshold(int threshold)
    {
        if (threshold > MaxThreshold)
            CurrentThreshold = MaxThreshold;
        else
            CurrentThreshold = threshold;
    }

}

public class MonitorService
{
    public void SetMonitorThreshold(int thresholdId, int thresholdValue)
    {
        var repository = new MonitorThresholdRepository();
        var threshold = repository.GetById(thresholdId);
        threshold.SetThreshold(thresholdValue);
        repository.Save(threshold);
    }
}

public class MonitorThresholdRepository
{
    public MonitorThreshold GetById(int thresholdId)
    {
        return new MonitorThreshold(thresholdId, 1, 5);
    }

    public void Save(MonitorThreshold th)
    {
        // Save the threshold to repository
    }
}

public class Tests
{
    [Fact]
    public void CannotSetMoreThanMaxThresHold()
    {
        var sut = new MonitorThreshold(1, 1, 10);
        sut.SetThreshold(11);

        Assert.Equal(10, sut.CurrentThreshold);
    }
}

This makes the MonitorThreshold class easy to test and it will also comply with the single responsibility principle. Adding a property of MinThreshold and use that in the validation will not affect any other class.

Leave a Comment

Your email address will not be published. Required fields are marked *