54

I have a number Processor classes that will do two very different things, but are called from common code (an "inversion of control" situation).

I'm wondering what design considerations I should be cognicent (or cognizant, for you USsers) of when deciding if they should all inherit from BaseProcessor, or implement IProcessor as an interface.


7 답변


143

Generally, the rule goes something like this:

  • Inheritance describes an is-a relationship.
  • Implementing an interface describes a can-do relationship.

To put this in somewhat more concrete terms, let's look at an example. The System.Drawing.Bitmap class is-an image (and as such, it inherits from the Image class), but it also can-do disposing, so it implements the IDisposable interface. It also can-do serialization, so it implements from the ISerializable interface.

But more practically, interfaces are often used to simulate multiple inheritance in C#. If your Processor class needs to inherit from something like System.ComponentModel.Component, then you have little choice but to implement an IProcessor interface.

The fact is that both interfaces and abstract base class provide a contract specifying what a particular class can do. It's a common myth that interfaces are necessary to declare this contract, but that's not correct. The biggest advantage to my mind is that abstract base classes allow you provide default functionality for the subclasses. But if there is no default functionality that makes sense, there's nothing keeping you from marking the method itself as abstract, requiring that derived classes implement it themselves, just like if they were to implement an interface.

For answers to questions like this, I often turn to the .NET Framework Design Guidelines, which have this to say about choosing between classes and interfaces:

In general, classes are the preferred construct for exposing abstractions.

The main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for the evolution of APIs. Once you ship an interface, the set of its members is fixed forever. Any additions to the interface would break existing types implementing the interface.

A class offers much more flexibility. You can add members to classes that you have already shipped. As long as the method is not abstract (i.e., as long as you provide a default implementation of the method), any existing derived classes continue to function unchanged.

[ . . . ]

One of the most common arguments in favor of interfaces is that they allow separating contract from the implementation. However, the argument incorrectly assumes that you cannot separate contracts from implementation using classes. Abstract classes residing in a separate assembly from their concrete implementations are a great way to achieve such separation.

Their general recommendations are as follows:

  • Do favor defining classes over interfaces.
  • Do use abstract classes instead of interfaces to decouple the contract from implementations. Abstract classes, if defined correctly, allow for the same degree of decoupling between contract and implementation.
  • Do define an interface if you need to provide a polymorphic hierarchy of value types.
  • Consider defining interfaces to achieve a similar effect to that of multiple inheritance.

Chris Anderson expresses particular agreement with this last tenet, arguing that:

Abstract types do version much better, and allow for future extensibility, but they also burn your one and only base type. Interfaces are appropriate when you are really defining a contract between two objects that is invariant over time. Abstract base types are better for defining a common base for a family of types.


  • thanks - great answer... Ditto. with just one caveat: if you're going to publish a base-class as "the public type" then advise the USERS of that class to inherit from it with THERE OWN base-class... to give them somewhere to put THERE common behaviour (in future).... And thanks heaps for the Link. I've got some good reading to do ;-) - corlettk
  • It should be noted that an interface is more of a 'must-do'. - awiebe
  • This answers becomes more true in light of recent changes done in java 8 to introduce default methods in interfaces. Else there was no other way to introduce new contract/method without breaking every piece of code implementing a interface. (Though the line between abstract class and interface becomes more blurry with this change) - Harshdeep
  • Isn't this advice almost the opposite of the thrust of 'Head First Design patterns'. I'm not an expert by any means but their mantra was prefer composition over inheritance. Can anyone untangle this? - alimack
  • @ali I don't see the contradiction at all. Granted, it's been many years since I wrote this answer, but in skimming it again, I don't see anywhere where I am advocating in favor of inheritance over composition. It doesn't say anything about composition, actually. If you do not need the strong relationship modeled by inheritance, then composition is the better choice. Don't express a stronger relationship than you need. - Cody Gray

7

Richard,

Why CHOOSE between them? I'd have an IProcessor interface as the published type (for use elsewhere in the system); and if it so happens that your various CURRENT implementations of IProcessor have common-behaviour, then an abstract BaseProcessor class would be a real good place to implement that common behaviour.

This way, if you require an IProcessor in future which does NOT have been for BaseProcessor's services, it doesn't HAVE to have it (and possibly hide it)... but those that do want it can have it... cutting down in duplicated code/concepts.

Just my humble OPINION.

Cheers. Keith.


  • Thanks Keith - it's a good point. I was really wandering if there were any considerations that might come back to bite me in choosing one over the other. Implementing both covers that nicely. - probably at the beach
  • Hmm, I disagree with this. You generally shouldn't expose interfaces without concrete implementations of that interface. And another problem with exposing interface is versioning (see my answer for details). I don't think you need to do both. That's not always the best answer to design decisions. Trying to do both comes at a price of increased complexity, and I struggle to see how that's really advantageous here. There's nothing you're missing out on by simply using an abstract base class, rather than an interface. - Cody Gray

5

Interfaces are a "contract", these are ensuring some class implements a desired set of members - properties, methods and events -.

Base classes (concrete or abstract, doesn't matter) are the archetype of some entity. That's these are entities representing what's common in some actual physical or conceptual one.

When to use interfaces?

Whenever some type needs to declare that, at least, has some behaviors and properties that a consumer should care about and use them to accomplish some task.

When to use base classes (concrete and/or abstract)

Whenever a group of entities share same archetype, meaning B inherits A because B is A with differences, but B can be identified as A.


Examples:

Let's talk about tables.

  • We accept recyclable tables => This must be defined with an interface like "IRecyclableTable" ensuring that all recyclable tables are going to have a "Recycle" method.

  • We want desktop tables => This must be defined with inheritance. A "desktop table" is a "table". All tables have common properties and behaviors and desktop ones will have same ones adding such things that make a desktop table work different than other types of tables.

I could talk about associations, meaning of both cases in an object graph, but in my humild opinion, if I need to give arguments in a conceptual point of view, I would exactly answer with this argumentation.


  • I'm not really sure what a "recyclable table" is, but I would probably implement an IRecyclable interface because it seems likely that several unrelated objects would need to be recyclable (i.e., other classes that do not represent tables). And then I would still have all tables implement from the Table class, including DesktopTable and PicnicTable and FoldingTable. - Cody Gray
  • And, if you double-check my text, do you find I'm saying something different than you? :) Read how starts the thing about "recyclable tables": "Examples". It's just an example, absolutely isolated from any object graph, design or actual case. - Matías Fidemraizer
  • Anyway, thank you for any suggestion, don't be wrong with my words, hey! :) - Matías Fidemraizer

1

I am not pretty good at design choices, but if asked, I will prefer implementing an iProcessor interface if there are only members to be extended. If there are other functions which need not to be extended, inheriting from baseprocessor is better option.


  • @jitendra - thanks, could you provide some more detail around why this is a better option? - probably at the beach
  • Why do you prefer interfaces? What's the advantage of an interface over an abstract base class? I completely disagree with your instincts—I would always choose an abstract base class over an interface, unless I specifically needed to simulate multiple inheritance. - Cody Gray
  • If there are only a couple of functions, inheriting makes more sense, as you don't have to implement every function available in the base class. And it is not necessary that you have enough information about the complete list of functions and parameters of the base class. So, in that case, inheriting makes more sense. - jitendragarg
  • Also, you can always create a normal base class, instead of abstract, and inherit only the functions required, even if the base class is supposed to be used later. But, in that scenario, performance hit will be there, as interfaces are supposedly lighter than classes. Plus, in case of using interface over abstract class, other developer will not even need to implement every function, to use just a couple of functions from the base class. So, re-usability of the base class is also a factor, you should consider before making such choices. - jitendragarg
  • @cody gray If I am not wrong, you are supposed to implement the whole abstract class, which is a bad idea in case of re-usability. Again, that is just my point of view. Might be wrong. Kindly edit the comment, if wrong. - jitendragarg

1

Design purity aside, you can inherit only once so, if you need to inherit some framework class the question is moot.

If you have a choice, pragmatically you can choose the option that saves the most typing. Its usually the purist choice anyway.

EDIT:

If the base class will have some implementation then this could be useful, if it is purely abstact then it may as well be an interface.


  • This is a good point. Often, choosing a base class saves the most typing. If you have a method with several overloads that all call down to a core function, you can place all the parameter validation, bounds checking, and function overloads in the base class. That means that the derived classes only have to implement the core "meat-and-potatoes" functionality. This isn't an option with an interface, where each class that implements that interface would have to do all the work each time. - Cody Gray
  • @Cody Gray About last part, but maybe a base class implements an interface, and interface implementation is virtual, so this is a mixed case! Just kidding, nevermind. - Matías Fidemraizer

1

If whatever you choose don't matter, always choose Interface. It allows more flexibility. It might protect you from future changes (if you need to change something in the base class, the inherited classes might be affected). It also allow to encapsulate the details better. If you are using some Inversion of Control of Dependency Injection, they tend to favor interface.

Or if inheritance can't be avoided, probably a good idea to use both of them together. Create a abstract base class that implements an interface. In your case, ProcessorBase implements an IProcessor.

Similar to ControllerBase and IController in ASP.NET Mvc.


  • It's interesting that you argue interfaces are more flexible, justifying this with the case of future changes. What about if in the future, you needed to add some functionality to your interface? That would be a breaking change, because each class that implemented that interface would also have to implement the method you just added. That's not the case with a base class, as the base class could simply provide a default implementation. - Cody Gray
  • Each cases of 'future changes' has different stories. In your case then the better way to do is using an abstract class, but in doing that, I might also prefer an interfaced abstract classes (both the I- and -base). Also from different point of view, if you are the one using the 'class', not the one that design it, it is more preferrable to see two or three methods, rather than a whole bunch of them. In this case, interface is better in the separation of concerns. You can use one interface to do a thing an another to do another thing. And all those interface might actually be one single class. - Hendry Ten
  • About adding functionality to an interface: you don't generally add new functions to interface (not the case for contracts type or Repository), you can create new one (interface in c# can be derived from another interface), provided the separation of concerns is implemented well. - Hendry Ten
  • I don't like these kind of arguments. Design shouldn't be done because of refactoring reasons. A good design is easy to refactor and it's not about inheritance of interface usage. Software changes, sure, but this can be done paying attention about what kind of requirements will be resolving overtime, so, if I'm not wrong, even with inheritance, a good inheritance tree would add, modify and/or remove logic, but its meaning should remain "as is". - Matías Fidemraizer

-1

Given that the SOLID principles offer more maintainability and extensibility to your project, I'd prefer interfaces over inheritance.

Also, if you need to add "additional functionality" to your interface, best option is to create a new interface altogether, following the I in SOLID, which is Interface Seggregation Principle.

Linked


Latest