There is no way to require an implementation to throw an exception through an interface, even in languages like Java where you can declare that a method could throw an exception.
There may be a way to ensure (to some degree but not absolutely) that an exception is thrown. You can create an abstract implementation of your interface. You can then implement the GetUser
method as final in the abstract class, and use the strategy pattern to call another, protected member of the subclass and throw an exception if it returns anything other than a valid user (like null). This can still fall down if, say, the other developer returns a null object type User
, but they would really have to work to subvert the intent here. They could also just reimplement your interface, also bad, so you may consider replacing the interface entirely with the abstract class.
(Similar results can be achieved using delegation instead of subclassing with something like a wrapping decorator.)
Another option could be to create a conformance test suite that all implementing code must pass to be included. How effective this is depends on how much control you have over the other code's linking into yours.
I also agree with others that clear documentation and communication are a given when a requirement like this is expected but cannot be completely enforced in code.
Code examples:
Subclass method:
public abstract class ExceptionalUserRepository : IUserRepository
{
public sealed User GetUser(int user_id)
{
User u = FindUserByID(user_id);
if(u == null)
{
throw new UserNotFoundException();
}
return u;
}
// subclasses implement this method instead
protected abstract User FindUserByID(int user_id);
// More code here
}
Decorator method:
public sealed class DecoratedUserRepository : IUserRepository
{
private readonly IUserRepository _userRepository;
public DecoratedUserRepository(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int user_id)
{
User u = _userRepository.GetUser(user_id);
if(u == null)
{
throw new UserNotFoundException();
}
return u;
}
// More code here
}
public class SomeClass
{
private readonly IUserRepository _userRepository;
// They now *have* to pass in exactly what you want
public SomeClass(DecoratedUserRepository userRepository)
{
_userRepository = userRepository;
}
// More code
}
A last quick point I want to make that I forgot before is that by doing any of these, you are tying yourself to a more specific implementation, meaning implementing developers get that much less freedom.