C# Interface Design – How to Ensure Interface Implementations Meet Expectations

cdesign-patternsinterfaces

Let's say there is a member SomeMethod in an interface ISomeInterface as follows:

public interface ISomeInterface
{
    int SomeMethod(string a);
}

For the purposes of my program, all consumers of ISomeInterface act upon the assumption that the returned int is greater than 5.

Three ways come to mind to solve this –

1) For every object that consumes ISomeInterface, they assert that the returned int > 5.

2) For every object that implements ISomeInterface, they assert that the int they're about to return is > 5.

Both the above two solutions are cumbersome since they require the developer to remember to do this on every single implementation or consumption of ISomeInterface. Furthermore this is relying upon the implementation of the interface which isn't good.

3) The only way I can think to do this practically is to have a wrapper that also implements ISomeInterface, and returns the underlying implementation as follows:

public class SomeWrapper : ISomeInterface
{
    private ISomeInterface obj;

    SomeWrapper(ISomeInterface obj)
    {
        this.obj = obj;
    }

    public int SomeMethod(string a)
    {
        int ret = obj.SomeMethod("hello!");
            if (!(ret > 5))
            throw new Exception("ret <= 5");
        else
            return ret;
    }
}

The problem though now is that we're again relying on an implementation detail of ISomeInterface via what the SomeWrapper class does, although with the benefit that now we've confined it to a single location.

Is this the best way to ensure an interface is implemented in the expected manner, or is there a better alternative? I understand interfaces may not be designed for this, but then what is the best practice to use some object under the assumption that it behaves a certain way more than what I can convey within its member signatures of an interface without needing to do assertions upon every time it's instanced? An interface seems like a good concept, if only I could also specify additional things or restrictions it's supposed to implement.

Best Answer

Instead of returning an int, return a value object that has the validation hard-coded. This is a case of primitive obsession and its fix.

// should be class, not struct as struct can be created without calling a constructor
public class ValidNumber
{
    public int Number { get; }

    public ValidNumber(int number)
    {
        if (number <= 5)
            throw new ArgumentOutOfRangeException("Number must be greater than 5.")
        Number = number;
    }
}

public class Implementation : ISomeInterface
{
    public ValidNumber SomeMethod(string a)
    {
        return new ValidNumber(int.Parse(a));
    }
}

This way, the validation error would happen inside the implementation, so it should show up when developer tests this implementation. Having the method return a specific object makes it obvious that there might be more to it than just returning a plain value.

Related Topic