35

My coworker and I have different opinions on the relationship between base classes and interfaces. I'm of the belief that a class should not implement an interface unless that class can be used when an implementation of the interface is required. In other words, I like to see code like this:

interface IFooWorker { void Work(); }

abstract class BaseWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker, IFooWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

The DbWorker is what gets the IFooWorker interface, because it is an instantiatable implementation of the interface. It completely fulfills the contract. My coworker prefers the nearly identical:

interface IFooWorker { void Work(); }

abstract class BaseWorker : IFooWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

Where the base class gets the interface, and by virtue of this all inheritors of the base class are of that interface as well. This bugs me but I can't come up with concrete reasons why, outside of "the base class cannot stand on its own as an implementation of the interface".

What are the pros & cons of his method vs. mine, and why should one be used over another?

5
  • Your suggestion very closely resembles to diamond inheritance, which may cause a lot of confusion further down.
    – Spoike
    Commented Sep 6, 2012 at 8:54
  • @Spoike: How to see that?
    – NingW
    Commented Apr 2, 2019 at 14:53
  • @Niing the link I provided in the comment has a simple class diagram and a plain one-liner explanation to what that is. its called a "diamond" problem because the inheritance structure is basically drawn like a diamond shape (i.e. ♦️).
    – Spoike
    Commented Apr 15, 2019 at 11:57
  • @Spoike: why this question will cause diamond inheritance?
    – NingW
    Commented Apr 15, 2019 at 12:13
  • 1
    @Niing: Inheriting BaseWorker and IFooWorker with the same method called Work is the diamond inheritance issue (in that case they both fulfill a contract over the Work method). In Java you're required to implement the interface's methods so the issue of which Work method the program should use is avoided in that way. Languages such as C++ however doesn't disambiguate this for you.
    – Spoike
    Commented Apr 23, 2019 at 8:05

10 Answers 10

24

I'm of the belief that a class should not implement an interface unless that class can be used when an implementation of the interface is required.

BaseWorker fulfills that requirement. Just because you can't directly instantiate a BaseWorker object doesn't mean you can't have a BaseWorker pointer that fulfills the contract. Indeed, that's pretty much the whole point of abstract classes.

Also, it's difficult to tell from the simplified example you posted, but part of the problem may be that the interface and the abstract class are redundant. Unless you have other classes implementing IFooWorker that do not derive from BaseWorker, you don't need the interface at all. Interfaces are just abstract classes with some additional limitations that make multiple inheritance easier.

Again being difficult to tell from a simplified example, the use of a protected method, explicitly referring to the base from a derived class, and the lack of an unambiguous place to declare the interface implementation are warning signs that you are inappropriately using inheritance instead of composition. Without that inheritance, your whole question becomes moot.

5
  • 1
    +1 for pointing out that just because BaseWorker isn't instantiable directly , doesn't mean a given BaseWorker myWorker doesn't implement IFooWorker. Commented Sep 4, 2012 at 19:17
  • 2
    I disagree that interfaces are just abstract classes. Abstract classes usually define some implementation details (otherwise an interface would have been used). If those details are not to your liking, you must now change every usage of the base class to something else. Interfaces are shallow; they define the "plug-and-socket" relationship between dependencies and dependents. As such, tight coupling to any implementation detail is no longer a concern. If a class inheriting the interface is not to your liking, strip it out and put in something else entirely; your code could care less.
    – KeithS
    Commented Sep 4, 2012 at 21:10
  • 5
    They both have advantages, but typically the interface should always be used for the dependency, whether or not there is an abstract class defining base implementations. The combination of interface and abstract class gives the best of both worlds; the interface maintains the shallow surface plug-and-socket relationship, while the abstract class provides the useful common code. You can strip out and replace any level of this underneath the interface at will.
    – KeithS
    Commented Sep 4, 2012 at 21:14
  • By "pointer" do you mean an instance of an object (whom a pointer would reference)? Interfaces are not classes, they are types, and can act as an incomplete substitute for multiple inheritance, not a replacement - that is, they "include behavior from multiple sources in a class".
    – samus
    Commented Jun 26, 2018 at 18:50
  • The assumption being made here is that there is only one base abstract class. Suppose there were two: BasePartTimeWorker and BaseFullTimeWorker. Both are still abstract, but both implement IFooWorker. And, a child implementation of either would still be valid substitutes for IFooWorker (as per LSP). Commented Dec 14, 2022 at 20:55
48

I'd have to agree with your coworker.

In both examples you give, BaseWorker defines the abstract method Work(), which means that all subclasses are capable of meeting IFooWorker's contract. In this case, I think BaseWorker should implement the interface, and that implementation would be inherited by its subclasses. This will save you from having to explicitly indicate that each subclass is indeed an IFooWorker (the DRY principle).

If you weren't defining Work() as a method of BaseWorker, or if IFooWorker had other methods that subclasses of BaseWorker wouldn't want or need, then (obviously) you'd have to indicate which subclasses actually implement IFooWorker.

6
  • 12
    +1 would have posted the same thing. Sorry insta, but I think your coworker is right in this case as well, if something guarantees to fulfill a contract, it should inherit that contract, whether the guarantee is fulfilled by an interface, an abstract class, or a concrete class. Simply put: The guarantor should inherit the contract it guarantees. Commented Sep 4, 2012 at 17:44
  • 2
    I generally agree, but would like to point out that words like "base", "standard", "default", "generic", etc. are code smells in a class name. If an abstract base class has almost the same name as an interface but with one of those weasel words included, it's often a sign of incorrect abstraction; interfaces define what something does, type inheritance defines what it is. If you have an IFoo and a BaseFoo then it implies that either IFoo is not an appropriately fine-grained interface, or that BaseFoo is using inheritance where composition might be a better choice.
    – Aaronaught
    Commented Sep 5, 2012 at 2:44
  • @Aaronaught Good point, although it may be that there really is something that all or most of the implementations of IFoo need to inherit (such as a handle to a service or your DAO implementation). Commented Sep 5, 2012 at 4:49
  • 2
    Then shouldn't the base class be called MyDaoFoo or SqlFoo or HibernateFoo or whatever, to indicate that it's just one possible tree of classes implementing IFoo? It's even more of a smell to me if a base class is coupled to a specific library or platform and there's no mention of this in the name...
    – Aaronaught
    Commented Sep 6, 2012 at 0:24
  • @Aaronaught - Yes. I agree completely. Commented Sep 6, 2012 at 0:58
11

I generally agree with your coworker.

Let's take your model: the interface is implemented only by the child classes, even though the base class also enforces the exposure of IFooWorker methods. First off, it's redundant; whether the child class implements the interface or not, they are required to override the exposed methods of BaseWorker, and likewise any IFooWorker implementation must expose the functionality whether they get any help from BaseWorker or not.

This additionally makes your hierarchy ambiguous; "All IFooWorkers are BaseWorkers" is not necessarily a true statement, and neither is "All BaseWorkers are IFooWorkers". So, if you want to define an instance variable, parameter, or return type that could be any implementation of either IFooWorker or BaseWorker (taking advantage of the common exposed functionality which is one of the reasons to inherit in the first place), neither of these is guaranteed to be all-encompassing; some BaseWorkers won't be assignable to a variable of type IFooWorker, and vice versa.

Your coworker's model is much easier to use and to replace. "All BaseWorkers are IFooWorkers" is now a true statement; you can give any BaseWorker instance to any IFooWorker dependency, no problems. The opposite statement "All IFooWorkers are BaseWorkers" is not true; that allows you to replace BaseWorker with BetterBaseWorker and your consuming code (which depends on IFooWorker) won't have to tell the difference.

2
  • I like the sound of this answer but don't quite follow the last part. You're suggesting that BetterBaseWorker would be an independent abstract class that implements IFooWorker, so that my hypothetical plugin could simply say "oh boy! The Better Base Worker is finally out!" and change any BaseWorker references to BetterBaseWorker and call it a day (maybe play with the cool new returned values that let me strip out huge chunks of my redundant code, etc)?
    – Anthony
    Commented May 15, 2015 at 11:26
  • More or less, yes. The big advantage is that you will reduce the number of references to BaseWorker that will have to change to be BetterBaseWorkers instead; most of your code won't reference the concrete class directly, instead using IFooWorker, so when you change what is assigned to those IFooWorker dependencies (properties of classes, parameters of constructors or methods) the code using the IFooWorker shouldn't see any difference in usage.
    – KeithS
    Commented May 20, 2015 at 16:10
6

I need to add something of a warning to these answers. Coupling the base class to the interface creates a force in the structure of that class. In your basic example, it's a no brainer that the two should be coupled, but that may not hold true in all cases.

Take Java's collection framework classes:

abstract class AbstractList
class LinkedList extends AbstractList implements Queue

The fact that the Queue contract is implemented by LinkedList did not push the concern into AbstractList.

What's the distinction between the two cases? The purpose of BaseWorker was always (as communicated by its name and interface) to implement operations in IWorker. The purpose of AbstractList and that of Queue are divergent, but a descenant of the former can still implement the latter contract.

5
  • This happens half the time. He always prefers to implement the interface on the base class, and I always prefer to implement it on the final concrete. The scenario you presented happens often and is part of the reason interfaces on the base bothers me.
    – Bryan B
    Commented Sep 5, 2012 at 18:28
  • 1
    Right, insta, and I regard the use of interfaces in this manner as a facet of the inheritance overuse that we see in many development environments. Like the GoF said in their design pattern book, prefer composition over inheritance, and keeping the interface out of the base class is one of the ways of promoting that very principle. Commented Sep 6, 2012 at 23:20
  • 1
    I get the warning, but you'll note that the OP's abstract class included abstract methods matching the interface: a BaseWorker is implicitly an IFooWorker. Making it explicit makes this fact more usable. There a numerous methods included in Queue that are not included in AbstractList, however, and as such, an AbstractList is not a Queue, even if its children could be. AbstractList and Queue are orthogonal. Commented Apr 8, 2019 at 16:35
  • Thank you for this insight; I do agree that the OP's case happens to fall into that category, but I sensed there was a bigger discussion in search of a deeper rule, and I wanted to share my observations about a tendency that I've read about (and even had myself for a brief time) of overusing inheritance, perhaps because that's one of the tools in OOP's toolbox. Commented Apr 9, 2019 at 13:01
  • Dang, it's been six years. :) Commented Apr 9, 2019 at 13:04
2

I would ask the question, what happens when you change IFooWorker, such as adding a new method?

If BaseWorker implements the interface, by default it will have to declare the new method, even if it's abstract. On the other hand, if it doesn't implement the interface, you'll only get compile errors on the derived classes.

For that reason, I'd make the base class implement the interface, since I might be able to implement all the functionality for the new method in the base class without touching the derived classes.

1

First think of what an abstract base class is, and what an interface is. Consider when you would use one or the other and when you would not.

It's common for people to think of both being very similar concepts, in fact it's a common interview question (difference between the two is??)

So an abstract base class gives you something interfaces can't which is default implementations of methods. (There's other stuff, in C# you can have static methods on an abstract class, not on an interface for example).

As an example, one common use of this is with IDisposable. The abstract class implements IDisposable's Dispose method, which means any derived version of the base class will automatically be disposable. You can then play with several options. Make Dispose abstract, forcing derived classes to implement it. Provide a virtual default implementation, so they don't, or make it neither virtual or abstract and have it call virtual methods called things like BeforeDispose, AfterDispose, OnDispose or Disposing for example.

So any time all derived classes need to support the interface, it goes on the base class. If only one or some need that interface it would go on the derived class.

That's all actually a gross over simplification. Another alternative is to have derived classes not even implement the interfaces, but provide them via an adapter pattern. An example of this I saw recently was in IObjectContextAdapter.

1

I am still years away from fully grasping the distinction between an abstract class and an interface. Every time I think I get a handle on the basic concepts, I go looking on stackexchange and I'm two steps back. But a few thoughts on the topic and the OPs question:

First:

There are two common explanations of an interface:

  1. An interface is a list of methods and properties that any class can implement, and by implementing an interface, a class guarantees those methods (and their signatures) and those properties (and their types) will be available when "interfacing" with that class or an object of that class. An interface is a contract.

  2. Interfaces are abstract classes that don't/can't do anything. They're useful because you can implement more than one, unlike those mean parent classes. Like, I could be an object of class BananaBread, and inherit from BaseBread, but that doesn't mean I can't also implement both the IWithNuts and the ITastesYummy interfaces. I could even implement the IDoesTheDishes interface, because I'm not just bread, yknow?

There are two common explanations of an abstract class:

  1. An abstract class is, yknow, the thing not what the thing can do. It's like, the essence, not really a real thing at all. Hang on, this will help. A boat is an abstract class, but a sexy Playboy Yacht would be a sub class of BaseBoat.

  2. I read a book on abstract classes, and maybe you should read that book, because you probably don't get it and will do it wrong if you haven't read that book.

By the way, the book Citers always seem impressive, even if I still walk away confused.

Second:

On SO, someone asked a simpler version of this question, the classic, "why use interfaces? What's the difference? What am I missing?" And one answer used an air force pilot as a simple example. It didn't quite land, but it sparked some great comments, one of which mentioned the IFlyable interface having methods like takeOff, pilotEject, etc. And this really clicked for me as a real-world example of why interfaces are not just useful, but crucial. An interface makes an object/class intuitive, or at least gives the sense that it is. An interface isn't for the benefit of the object or the data, but for something that needs to interact with that object. The classic Fruit->Apple->Fuji or Shape->Triangle->Equilateral examples of inheritance are a great model for taxonomically understanding a given object based on its descendants. It informs the consumer and the processors about its general qualities, behaviors, whether the object is a group of things, will hose your system if you put it in the wrong place, or describes sensory data, a connector to a specific data store, or financial data needed to make payroll.

A specific Plane object may or may not have a method for an emergency landing, but I'm going to be pissed off if I assume its emergencyLand like every other flyable object only to wake up covered in DumbPlane and learn that the developers went with quickLand because they thought that it was all the same. Just like how I would be frustrated if every screw manufacturer had their own interpretation of righty tighty or if my TV didn't have the volume increase button above the volume decrease button.

Abstract classes are the model that establishes what any descendent object must have to qualifiy as that class. If you don't have all the qualities of a duck, it doesn't matter that you have the IQuack interface implemented, your just a weird penguin. Interfaces are the things that make sense even when you can't be sure of anything else. Jeff Goldblum and Starbuck were both able to fly alien spaceships because the interface was reliably similar.

Third:

I agree with your coworker, because sometimes you need to enforce certain methods at the very start. If you're creating an Active Record ORM, it needs a save method. This isn't up to the subclass that can be instantiated. And if the ICRUD interface is portable enough to not be exclusively coupled to the one abstract class, it can be implemented by other classes to make them reliable and intuitive for anyone already familiar with any of the descendant classes of that abstract class.

On the other hand, there was a great example earlier of when to not jump to tying an interface to the abstract class, because not all list types will (or should) implement a queue interface. You said this scenario happens half the time, which means you and your coworker are both wrong half the time, and thus the best thing to do is argue, debate, consider, and if they turn out to be right, acknowledge and accept the coupling. But don't become a developer who follows a philosophy even when it isn't not the best one for the job at hand.

0

There is another aspect to why the DbWorker ought to implement IFooWorker, as you suggest.

In case of your coworker’s style, if for some reason a later refactoring occurs and the DbWorker is deemed not to extend the abstract BaseWorker class, DbWorker loses the IFooWorker interface. This could, but not necessarily, have an impact on clients consuming it, if they expect the IFooWorker interface.

-1

To start off, I like to define interfaces and abstract classes differently:

Interface: a contract that each class must fulfill if they are to implement that interface
Abstract class: a general class (i.e vehicle is an abstract class, while porche911 is not)

In your case, all workers implement work() because that is what the contract requires them to. Therefore your base class Baseworker should implement that method either explicitly or as a virtual function. I would suggest you to put this method in your base class because of the simple fact that all workers need to work(). Therefore it is logical that your baseclass (even though you cannot create a pointer of that type) includes it as a function.

Now, DbWorker does satisfy the IFooWorker interface, but if there is a specific way that this class does work(), then it really needs to overload the definition inherited from BaseWorker. The reason it should not implement the interface IFooWorker directly is that this is not something special to DbWorker. If you did that everytime you implemented classes similar to DbWorker then you would be violating DRY.

If two classes implement the same function in different ways, you could start looking for the greatest common superclass. Most of the time you will find one. If not, either keep looking or give up and accept that they don't have sufficient things in common to make a base class.

-3

Wow, all these answers and none pointing out that the interface in the example serves no purpose whatsoever. You don't use inheritance and an interface for the same method, it is pointless. You use one or the other. There are plenty questions on this forum with answers explaining the benefits of each and the senarios that call for one or the other.

4
  • 4
    That's a patent falsehood. The interface is a contract by which the system is expected to behave, without regard to the implementation. The inherited base class is one of many possible implementations for this interface. A large enough scale system will have multiple base classes that satisfy the various interfaces (usually closed with generics), which is exactly what this setup did and why it was helpful to split them.
    – Bryan B
    Commented Oct 4, 2016 at 1:36
  • It is a really bad example from an OOP perspective. As you say "the base class is just one implementation" (it should be or there would be no point for the interface), you are saying there will be non-worker classes implementing the IFooWorker interface. Then you will have a base class that is a specialized fooworker (we should assume there could be barworkers as well) and you would have other fooworkers that are not even basic workers! That is backward. In more than one way. Commented Oct 4, 2016 at 5:29
  • 1
    Perhaps you're stuck on the "Worker" word in the interface. What about "Datasource"? If we have an IDatasource<T>, we can trivially have a SqlDatasource<T>, RestDatasource<T>, MongoDatasource<T>. The business decides which closures of the generics interface go onto the closing implementations of the abstract datasources.
    – Bryan B
    Commented Oct 5, 2016 at 2:42
  • It can make sense to use inheritance just for the sake of DRY and put some common implementation in a base class. But you would not align any overridden methods with any interface methods, the base class would contain general support logic. If they would line up, that would show inheritance solves your problem just fine and the interface would serve no purpose. You use an interface in cases inheritance does not solve your problem because the common characteristics you want to model do not fit a singke class tree. And yes, the wording in the example makes it all the more wrong. Commented Oct 5, 2016 at 5:44

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