8

Suppose you had the following interface

public interface IUserRepository
{
    User GetByID(int userID);
}

How would you enforce implementers of this interface to throw an exception if a user is not found?

I suspect it's not possible to do with code alone, so how would you enforce implementers to implement the intended behavior? Be it through code, documentation, etc.?

In this example, the concrete implementation is expected to throw a UserNotFoundException

public class SomeClass
{
    private readonly IUserRepository _userRepository;

    public SomeClass(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public void DisplayUser()
    {
        try 
        {
            var user = _userRepository.GetByID(5);
            MessageBox.Show("Hello " + user.Name);
        }
        catch (UserNotFoundException)
        {
            MessageBox.Show("User not found");
        }
    }
}
16
  • 3
    This is really a very interesting question. I have a feeling the correct answer might involve Attributes or an IL hack. Generally, I'd assume that attempting it is a wrong choice, because each implementer should be able to throw their own types of exceptions, but it's still conceivably useful. Maybe you'd just use an abstract class with a template method?
    – Magus
    May 1, 2014 at 21:12
  • 15
    Technology doesn't solve people problems. If people violate the interface, hit them with a stick until they stop.
    – Telastyn
    May 1, 2014 at 21:16
  • 1
    @Telastyn Maybe I should build a program that beats people with sticks instead.
    – Matthew
    May 1, 2014 at 21:17
  • 2
    @Doval: So how does the Interface know whether a valid user exists or not without an implementation backing it? May 1, 2014 at 21:20
  • 1
    @matthew - then your program would have to infer "until they stop" properly, which leads back to the whole halting problem thing (if your stick wielding program could tell that people were violating its interface or not, you wouldn't need it).
    – Telastyn
    May 1, 2014 at 21:22

7 Answers 7

9

This is a language feature that was intentionally omitted from C#. Quite simply, it's entirely possible for an IUserRepository.GetByID to fail for some other reason entirely than the user not being found, so you don't want to require a specific error when such cannot happen. You have two options if for whatever reason you want to enforce this behavior:

  1. Define the User class so that it itself throws the exception if it's improperly initalized.
  2. Write unit tests for IUserRepository that explicitly test for this behavior.

Note that neither of those options is "include it in the documentation." Ideally, you should do that anyway, especially as documentation is where you can explain why you want to enforce a particular error type.

1
  • 2
    Even with checked exceptions, you can't force implementers to throw the right exception at the right time.
    – svick
    May 1, 2014 at 22:48
7

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.

3

If you work together with the developers that you want to implement the exception-throwing behaviour, then you could possibly write unittests (for them) to check if an exception is thrown if you try to get a user who does not exist.

You therefore need to know which methods to test. I'm not familiar with C# but you could maybe use reflection to search for all classes implementing the IUserRepository and test them automatically so even if a developer adds another class it will be tested when the tests are run.

But that is only what you could do in practice if you really suffer from developers doing the implementations wrong you have to work with. In theory interfaces are not for defining how the business logics has to be implemented.

I'm looking forward for other answers as this is really an interesting question!

3

You can't. That's the thing about interfaces - they allow anyone to implement them at any time. There's an infinite number of potential implementations for your interface, and you can't force any of them to be correct. By choosing an interface, you've forfeited your right to enforce that a particular implementation be used all throughout the system. Heck, what you have isn't even an interface, it's a function. You're writing code that passes functions around.

If you need to go this route then you'll simply have to clearly document the specification that implementations must conform to and trust that no one will deliberately break that specification.

The alternative is to have an abstract data type, which translates into a class in Java/C#-like languages. The problem is that mainstream languages have weak support for ADTs and you can't switch the implementation of the class without file system tomfoolery. But if you've willing to live with that, you can ensure that there's only one implementation in the system. That's a step up from interfaces, where your code may break at any time, and when it does you'll have to figure out which implementation of the interface broke your code and where it came from.

EDIT: Note that even IL hacks won't allow you to ensure the correctness of certain aspects of an implementation. For example, it's implicitly assumed that GetByID must either return a value or throw an exception. Someone could write an implementation that simply goes into an endless loop and does neither. You can't prove during runtime that the code loops forever - if you manage to do this, you've solved the Halting Problem. You might be able to detect trivial cases (e.g. recognize a while (true) {}) but not an arbitrary piece of code.

6
  • Well, an interface does prevent some wrong implementations: Those which are ill-typed (don't match the method signatures in the interface). It can't express the constraint OP wants, but few type systems can that.
    – user7043
    May 1, 2014 at 21:25
  • @delnan If a piece of code has a different type, you can hardly call it an implementation! But we're arguing semantics. I get what you mean.
    – Doval
    May 1, 2014 at 21:26
  • Another thing: An "abstract data type" is an informal notion much like documentation; to make it machine-checkable you need to construct a type system and use that type system's equivalent of interfaces.
    – user7043
    May 1, 2014 at 21:27
  • @delnan That's a misconception. See On Understanding Data Abstraction, Revisited. An ADT abstracts data like an interface, yes. But an ADT does so by obscuring the identity of an existing type. This is equivalent to a class with private fields in mainstream OOP. The obscured type is the record whose fields you've made private. An interface on the other hand is a bundle of functions, and the implementation type appears nowhere in the signatures. This is what allows multiple implementations and substitutability.
    – Doval
    May 1, 2014 at 21:29
  • This appears to be a rather different use of "abstract data type" than I'm used to; what I and many others know as abstract data type is just a tool for talking about data types in a language-agnostic manner. What you describe appears to be a (class of) type system feature(s) that interpret and implement the aforementioned notion of abstract data types.
    – user7043
    May 1, 2014 at 21:41
2

There's no way to 100% force the behavior you want, but using code contracts you could say that implementations must not return null, and perhaps that the User object has the id passed in (so an object with userid 0 would fail if 1 was passed). On top of that, documenting what you expect of implementations might help as well.

0

This is probably very late to answer this question, but would like to share my thought on this. As all answers suggest, forcing the constraint with Interface not possible. Even with hacks would be difficult.

One way to achieve this, probably specific to .NET is design the interface like below -

public interface IUserRepository
{
    Task<User> GetByID(int userId);
}

Task will not force to throw certain type of Exception, but it has provision to convey the intend of all the implementer of this interface. Tasks in .NET have certain pattern of use, like Task.Result to accessing the result, Task.Exception if there is exception. Plus, this will also user to use it asynchronously.

0

You can't do that with an interface, as many others have answered already.
But you can do that with an abstract base class:

public abstract class UserRepositoryBase
{
  public User GetByID (int userID)  // not virtual because you don't want to allow overriding
  {
    User user = null;
    try
    {
      user = GetByID_EXEC (userID);
    }
    catch (UserNotFoundException)
    {
      throw;  // rethrow exception because it's the correct type.
    }
    catch (Exception)
    {
      // do whatever you want
    }
    if (user != null)
      return user;
    throw new UserNotFoundException ();
  }

  protected abstract User GetByID_EXEC (int userID);
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.