6
interface IUser {}
interface IConcreteUser : IUser {}

abstract class UserBase : IUser {}

class ConcreteUser : UserBase, IConcreteUser {}

As you can see, ConcreteUser inherits IUser two times - one time by inherting from UserBase and second time by implementing IConcreteUser interface.

Is there something wrong with that? I feel like it should signal some sort of design problem, but I'm not sure.

  • 1
    What does your compiler tell you? – Doc Brown Dec 1 '16 at 15:57
  • @DocBrown well, there is no problem in compiling it (C# 4.0)! I'm asking from a good design point of view. – Pavel T. Dec 1 '16 at 15:59
  • All I can tell you is it may good if you want to extend, version your "base" interface with some functionality that's closely related. But it's hard give you a concise answer without a use case. – kayess Dec 1 '16 at 16:02
  • It seems pointless in your case. The interface tree is a match with your inheritence tree. The whole point of using interfaces is to address situations where your functional needs do not fit the shape of your inheritence tree. – Martin Maat Dec 2 '16 at 6:04
1

Assuming we can replace ConcreteUser with AdminUser since it is hard to think about this question is such general terms...


The real answer to this question involves the basic decision about how objects are related (see https://stackoverflow.com/a/2218970/3092298)

A copied-and-pasted quote from cletus's answer on the StackOverflow question:

This is object-oriented programming and UML terminology, not Java-specific. There are actually three cases you should be aware of:

  1. A House is a Building (inheritance);
  2. A House has a Room (composition);
  3. A House has an occupant (aggregation).

The difference between (2) and (3) is subtle yet important to differentiate. Together they are forms of association. What's the difference? Composition implies the child object cannot live out of the context of the parent (destroy the house and rooms disappear) whereas aggregation implies the child can exist on its own (destroy the house and the occupant goes elsewhere).

So, the answer to your question involves asking the questions above:

  1. Is an Admin user a User? (inheritance) - Yes.

  2. Does a User have an Admin user? (composition) - No.

  3. Does a User have one or more Admin users? (aggregation) - No.

In this case, I think an Admin user is a User, so inheritance is correct for the interfaces.

  1. Is UserBase an IUser? Yes.

  2. Is UserBase an IAdmin? No.

  3. Is AdminUser an IUser? Yes.

  4. Is AdminUser an IAdmin? Yes.

In this case, inheriting the same interface multiple times is fine.

  • Thanks, never heard of "HAS-A, IS-A" terminology before. I think it applies pretty well here. – Pavel T. Dec 2 '16 at 14:01
4

Technically no. There is no issue at a high-level OO-design perspective. This kind of thing comes up a lot when you use abstract classes to provide default implementations of interfaces.

I think it's worth mentioning that a lot of people will tell you to favor composition over inheritance. While I agree with that, providing an abstract class as a default implementation can be useful (to a degree.) It doesn't scale well meaning that it really only works OK with one level of inheritance. Anything beyond that and it will start to be a tangled mess.

2

The thing that stands out for me is the interface inheriting from another interface.

interface IConcreteUser : IUser {}

I find it hard to imagine the situation where you would not be better splitting IConcreteUser into whatever its extra functionality is and the already existing IUser. Then having your Concrete class implement both interfaces

interface IUser {
    bool Login();
}

interface IAdmin {
    bool RebootServer();
}

abstract class UserBase : IUser {}

class AdminUser : UserBase, IAdmin {}

Or, if the Admin functionality is always used with the User functionality, this would suggest that you might be better putting it all together in IUser.

I don't say its never a sensible thing to have the interface inherit. You could for example have a two types of objects which where both admin and user and want to put them in a list

class AdminUser : IUser, IAdmin

class UserAdmin : IUser, IAdmin

List<INeedAnInterface> AdminUsers_and_UserAdmin;

But if you are able to change the interfaces then you could also change the objects inheritance.

class BaseUserAdmin : IUser, IAdmin

class AdminUser : BaseUserAdmin

class UserAdmin : BaseUserAdmin

So I guess what I'm saying is, Yes your code does look a bit weird. But not broken wierd

  • In that case, if some code has an IAdmin object (i. e. an AdminUser which it only knows via its interface), it won't be able to treat it as a regular user and call Login() on it. You cannot declare a pointer to point to more than a single type in virtually all OOP languages, so the user code would always need to decide whether it needs an IUser or an IAdmin, even though all IAdmin implementations also implement IUser. That might be cases where you want this restriction, but the general case is that interface inheritance can be a very useful thing to do. – cmaster Dec 1 '16 at 18:06
  • exactly what @cmaster said - in my first iteration, I had my interfaces declared like you suggested, but then I realised I actually need IUser functionality in my IAdmin interface (and it's also sounds logical, doesn't it?) – Pavel T. Dec 1 '16 at 18:16
1

There is no apparent design problem.

One might fear to run into the "diamond problem" in the the context of multiple inheritance. But this will only appear in programming languages where multiple (implementation) inheritance is allowed, with just multiple interface inheritance, like in Java or C#, there is no way to run into the usual ambiguity problems.

  • Now that Java has default methods, you might not have a true ambiguity problem but it's something that might create some challenges. – JimmyJames Dec 1 '16 at 16:09
  • @JimmyJames: Yep, but I am sure to add a paragraph "except for Java 8 with default methods" and going into the gory details won't improve my answer. – Doc Brown Dec 1 '16 at 16:28
  • Yeah. I think it was correct to add this to the language but it does complexify things a bit. – JimmyJames Dec 1 '16 at 16:44
1

With interfaces, no... there isn't a problem because the interfaces only declare abstract methods and require the implementations to supply those actions.

Just because both IConcreteUser is based on IUser, there is an assumption that IConcreteUser provides additional methods that define it as a unique interface.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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