3

I've been asked to explain why it may be bad to have an abstract class that implements most of the methods (as an extreme case, only one abstract method) and has many sub-classes.

Apart from having to look at a lot of code if you change the signature of that method I couldn't think of anything else. My answer didn't seem to satisfy the questioner.

Is there any documented downside in using abstract classes this way?

UPDATE: I'm changing the question's title to make it less opinion-based.

closed as primarily opinion-based by Ixrec, Jörg W Mittag, gnat, user22815, Daenyth Apr 13 '16 at 12:52

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 4
    We cannot read the questioner's mind, sorry. – Jörg W Mittag Apr 13 '16 at 11:16
  • 1
    @JörgWMittag, whilst it's unfortunate that the questioner didn't bother to share what they wanted to hear, that doesn't mean this question can't be answered. – David Arno Apr 13 '16 at 11:47
  • 1
    @DavidArno: Considering that this is a very common, very powerful, and widely-used pattern, I would hazard a guess that the questioner has something very specific in mind, or might even be mis-informed. There's no way to know what he was thinking of without asking him. – Jörg W Mittag Apr 13 '16 at 11:57
  • @Jörg W Mittag Ok, but this does not explains, what is wrong with David Arno´s answer. – tak3shi Apr 13 '16 at 12:06
4

Some disadvantages with your approach:

  1. You are coupling a large number of sub classes to that one base class.
  2. You are risking running into the fragile base class problem as it will be difficult to be sure that changing the base class will not break a derived class without checking all of them.
  3. You are spreading the functionality over a number of classes, risking "fragmented responsibility".

You could address all of those by having a single class that takes a function/method/delegate/command object (use as appropriate for your specific language) as a parameter to the constructor. Then rather than having a large number of classes, you have one class, a collection of functions (potentially grouped together) and then use eg a factory to create objects with different functions injected as required.

1

There is nothing bad about this. In fact, it is a quite powerful and widely-used pattern. I have heard it called a "lever API", because by implementing just one (usually pretty simple) method, you get a large amount of functionality in return. In other words, just like with a lever, you can turn a little effort into a lot of result.

Some examples of where this is used in the wild, are:

  • Ruby's Enumerable API: as of Ruby 2.3, Enumerable has 48 concrete methods (map, flat_map, find, reduce, … all of the methods you would expect from an iterable object), all of which are implemented in terms of one single abstract method: each. Subclasses, such as Array, Hash, Set, Enumerator, etc, only need to implement a way to iterate over themselves (i.e. each) and gain all the other methods for free. Pretty much anything that is even remotely collection-like inherits from Enumerable.
  • Ruby's Comparable API: Subclasses only need to implement <=> (the combined comparison operator), and gain <, <=, >, >=, and between? for free. Pretty much anything inherits from Comparable.
  • Scala's FunctionN API: you only need to implement apply, and your object can be treated as a function. Lots of things inherit from FunctionN, for example Set inherits from Function1[T, Boolean] and Array inherits from Function1[Int, T]. FunctionN provides a number of concrete methods for composing and transforming functions, all of which rely on apply.
  • Scala's Traversable API: similar to Ruby's Enumerable, you only need to implement foreach, and get almost 100 concrete methods for free. Pretty much all collections implement Traversable.
  • The same applies to Iterable: implement iterator and get everything else for free. (In fact: Iterable inherits from Traversable and implements foreach in terms of iterator as well.)

And, of course, generally, a class with a single abstract method is isomorphic to a function type, and is often used that way. E.g.

  • in Python, every object with a __call__ method is considered a "callable" (i.e. function-like) and the __call__ method can be called with syntax sugar like foo(),
  • in Ruby, every object with a call method is considered to be function-like and the call method can be called with syntax sugar like foo.(),
  • in Scala, every object with an apply method is considered to be function-like and the apply method can be called with syntax sugar like foo(), and
  • in Java, every type with a single abstract method is considered to be a function type, and instances of that type can be constructed using lambda literal syntax
  • "There is nothing bad about this...". You are mistaken. Whilst it is indeed a "quite powerful and widely-used pattern", it does have it's problems, as I've listed in my answer. – David Arno Apr 13 '16 at 12:51
  • Those seem to me more like general issues with inheritance of abstract classes, or in fact implementation inheritance in general. They don't seem to be specific to having a small number of abstract methods. – Jörg W Mittag Apr 13 '16 at 14:25

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