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.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.