12

Empty interfaces are generally consider bad practice, as far as I can tell - especially where things like attributes are supported by the language.

However, is an interface considered 'empty' if it inherits from other interfaces?

interface I1 { ... }
interface I2 { ... } //unrelated to I1

interface I3
    : I1, I2
{
    // empty body
}

Anything that implements I3 will need to implement I1 and I2, and objects from different classes that inherit I3 can then be used interchangeably (see below), so is it right to call I3 empty? If so what would be a better way to architecture this?

// with I3 interface
class A : I3 { ... }
class B : I3 { ... }

class Test {
    void foo() {
        I3 something = new A();
        something = new B();
        something.SomeI1Function();
        something.SomeI2Function();
    }
}

// without I3 interface
class A : I1, I2 { ... }
class B : I1, I2 { ... }

class Test {
    void foo() {
        I1 something = new A();
        something = new B();
        something.SomeI1Function();
        something.SomeI2Function(); // we can't do this without casting first
    }
}
  • 1
    Being unable to specify several interfaces as type of a parameter/field etc. is a design flaw in C# / .NET. – CodesInChaos Jul 21 '15 at 16:58
  • I think the better way to architecture this part should go into a separate question. Right now the accepted answer has nothing to do with the question title. – Kapol Jul 21 '15 at 22:14
  • @CodesInChaos No, its not as there are two ways to do this already. One is in this question, the other way would be to specify a generic type parameter for the method argument and the use the where restriction to specify both interfaces. – Andy Jul 22 '15 at 1:28
  • Does it make sense that a class that implements I1 and I2, but not I3, should not be able to be used by foo? (If the answer is "yes", then go ahead!) – user253751 Jul 22 '15 at 2:16
  • @Andy Whilst I don't entirely agree with CodesInChaos's claim that it's a flaw as such, I don't think that generic type parameters on the method are a solution - it pushes the responsibility of knowing a type used in the method's implementation onto whatever is calling the method, which feels wrong. Perhaps some syntax like var : I1, I2 something = new A(); for local variables would be nice (if we ignore the good points raised in Konrad's answer) – Kai Jul 22 '15 at 13:38
27

Anything that implements I3 will need to implement I1 and I2, and objects from different classes that inherit I3 can then be used interchangeably (see below), so is it right to call I3 empty? If so what would be a better way to architecture this?

"Bundling" I1 and I2 into I3 offers a nice (?) shortcut, or some sort of syntax sugar for wherever you'd like both to be implemented.

The problem with this approach is that you can't stop other developers from explicitly implementing I1 and I2 side by side, but it doesn't work both ways - while : I3 is equivalent of : I1, I2, : I1, I2 is not an equivalent of : I3. Even if they're functionally identical, a class that supports both I1 and I2 will not be detected as supporting I3.

This means that you're offering two ways of accomplishing the same thing - "manual" and "sweet" (with your syntax sugar). And it can have a bad impact on code consistency. Offering 2 different ways to accomplish the same thing here, there, and in another place results in 8 possible combinations already :)

void foo() {
    I1 something = new A();
    something = new B();
    something.SomeI1Function();
    something.SomeI2Function(); // we can't do this without casting first
}

OK, you can't. But perhaps it's actually a good sign?

If I1 and I2 imply different responsibilities (otherwise there would be no need to separate them in the first place), then maybe foo() is wrong to try and make something perform two different responsibilities in one go?

In other words, it could be that foo() itself violates the principle of Single Responsibility, and the fact that it requires casting and runtime type checks to pull it off is a red flag alarming us about it.

EDIT:

If you insisted on ensuring that foo takes something that implements I1 and I2, you could pass them as separate parameters:

void foo(I1 i1, I2 i2) 

And they could be the same object:

foo(something, something);

What does foo care if they're the same instance or not?

But if it does care (eg. because they're not stateless), or you just think this looks ugly, one could use generics instead:

void Foo<T>(T something) where T: I1, I2

Now generic constraints take care of the desired effect while not polluting the outside world with anything. This is idiomatic C#, based on compile-time checks, and it feels more right overall.

I see I3 : I2, I1 somewhat as an effort to emulate union types in a language that doesn't support it out of the box (C# or Java don't, whereas union types exist in functional languages).

Trying to emulate a construct that's not really supported by the language is often clunky. Similarly, in C# you can use extension methods and interfaces if you want to emulate mixins. Possible? Yes. Good idea? I'm not sure.

4

If there were particular importance in having a class implement both interfaces, then I see nothing wrong with creating a third interface that inherits from both. However, I would be careful to not fall into the trap of overengineering things. A class could inherit from one or the other, or both. Unless my program had plenty of classes in these three cases, it is a little much to create an interface for both, and certainly not if those two interfaces had no relation to one another (no IComparableAndSerializable interfaces please).

I remind you that you can create an abstract class as well that inherits from both interfaces, and you can use that abstract class in place of I3. I think it would be wrong to say that you shouldn't do it, but you should at the very least have a good reason.

  • Huh? You certainly can implement two interfaces in a single class. You don't inherit interfaces, you implement them. – Andy Jul 22 '15 at 1:35
  • @Andy Where did I say that a single class can't implement two interfaces? For what concerns the use of the word "inherit" in the place of "implement", semantics. In C++, inherit would work perfectly fine since the closest thing to interfaces are abstract classes. – Neil Jul 22 '15 at 6:49
  • Inheriting and interface implementation are not the same concepts. Classes inheriting multiple subclasses can lead to some weird issues, which is not the case with implementing multiple interfaces. And C++ lacking interfaces doesn't mean the difference goes away, it means that C++ is limited in what it can do. Inheritance is an "is-a" relationship, while interfaces specify "can-do." – Andy Jul 22 '15 at 14:41
  • @Andy Weird issues caused by collisions between methods implemented in said classes, yes. However, that is no longer an interface not in name nor in practice. Abstract classes which declare but not implement methods is in all sense of the word except name, an interface. Terminology changes between languages, but that doesn't mean that a reference and a pointer aren't the same concept. It's a pedantic point, but if you insist on this, then we'll have to agree to disagree. – Neil Jul 23 '15 at 6:10
  • "Terminology changes between languages" No, it doesn't. OO terminology is consistent across languages; I've never heard an abstract class meaning anything else in each OO language I've used. Yes, you can have the semantics of an interface in C++, but the point is there's nothing preventing someone from going and adding behavior to an abstract class in that language and breaking those semantics. – Andy Jul 23 '15 at 18:26
2

Empty interfaces are generally consider bad practice

I disagree. Read about marker interfaces.

Whereas a typical interface specifies functionality (in the form of method declarations) that an implementing class must support, a marker interface need not do so. The mere presence of such an interface indicates specific behavior on the part of the implementing class.

  • I imagine the OP is aware of them, but they're actually a bit controversial. While some authors recommend them (eg. Josh Bloch in "Effective Java"), some claim they're an antipattern and legacy from times when Java had no annotations yet. (Annotations in Java are roughly the equivalent of attributes in .NET). My impression is that they're much more popular in Java world than .NET. – Konrad Morawski Jul 22 '15 at 12:46
  • 1
    I use them once in a while, but it feels kinda hackish - the downsides, off top of my head, are that you can't help the fact they're inherited, so once you mark a class with one, all its children get marked as well whether you want it or not. And they seem to encourage bad practice of using instanceof instead of polymorphism – Konrad Morawski Jul 22 '15 at 12:52

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.