4

I read some posts about "composition over inheritance","where to use composition/inheritance" , "Is-a relationship..." or "Liskov substitution principle" for some time, but I am not sure if I get the right idea about "composition over inheritance".

Alternatively, in my experience, it seems just one simple golden rule to check if we should use composition or inheritance:

If a parent class never appears in my codes except in its child class, it should be composition instead of inheritance

for example, suppose I have a parent class Fruit:

public class Fruit{
    public String printName(){
        System.out.println("Fruit");
    }
}

and child class Orange:

public class Orange extends Fruit{
    public String printName(){
        System.out.println("Orange");
    }
}

and Grape:

public class Grape extends Fruit{
    public String printName(){
        System.out.println("Grape");
    }
}

If I really needs inheritance, in my source code there should be something like

Vector<Fruit> fruitList;

,

Fruit fruit=new Orange();

or

((Fruit)f).printName();

which needs "Fruit" to compile, so not counting Orange and Grape (child class),if my source codes never have "Fruit", or if my codes can compile without "Fruit", then Orange and Grape should not be extended from "Fruit" and hence misusing inheritance, it that true?

  • 2
    What you're describing fits somewhat the notion of an abstract class, which is a valid construct for many situations, and doesn't necessarily indicate that composition would be better. – Erik Eidt Mar 22 '16 at 2:47
  • 2
    You haven't shown an example of composition yet. All of your examples are inheritance. – Adam Zuckerman Mar 22 '16 at 3:15
  • 2
    I don't really understand what you are asking. Offhand, I'd say the obvious answer to your question is "no." Choosing between one programming technique or another is always an exercise in tradeoffs; there are no absolute rules (except the one that I just stated). – Robert Harvey Mar 22 '16 at 3:22
  • Well, you work with a virtual base class, but how is that composition? – Pieter B Mar 22 '16 at 13:19
9

Technically, in your example, you're not making a decision between composition and inheritance, but between using inheritance or not using inheritance. In other words, creating two unrelated Grape and Orange classes. That's why you're getting some pedantic comments. However, your rule of thumb is an apt one, and would still apply were you add composition into the mix. It's certainly an easier rule of thumb to apply than the vague "prefer composition over inheritance" one.

Unfortunately, object-oriented design has a grand tradition of using inheritance to model real-world is-a relationships, whether or not that relationship is actually useful in code. It also has a grand tradition of creating inheritance hierarchies with a laser focus on only the classes themselves, without consideration of the calling code you would need to think about to apply your rule of thumb. This leads to a lot of people thinking they need the Fruit parent class when they really don't. From experience, I know you'll get pushback if you try to teach people this way, but that doesn't mean you shouldn't use the rule of thumb for yourself.

  • Great answer! Conceptuals are important, but it's important not to forget that the purpose of code is to create a working application. Something else along this vein... "inheritance is not for code reuse." And it is important to understand that inheritance is not for code reuse... but once in a while, it just works :P – TheCatWhisperer Mar 22 '16 at 13:00
1

Anything inheritance can do composition can do. And composition can do more things. Nothing in life is free. Composition costs you typing and reading.

If a parent class never appears in your other code except in its child class you should rewrite your other code because that isn't what inheritance or composition are for.

If I have a duck that can quack, a dog that can bark, and a cat that can meow I could model them as animals that can speak. My Animal class could use inheritance or composition. It might just be an interface. The rest of your code doesn't need to know. It shouldn't want to know. It's much easier to think of a duck as just something you can tell to speak. That it quacks when you tell it to do this isn't any of your business.

Code this way and your outside code will be blisfully unaware of whither Fruit is a wrapper, a parent, an interface, abstract, or concreet.

How then should you decide between inheritance and composision? Inheritance is the lazy brittle option. Lazy is good. Brittle is bad. Pick your poison.

Personally, I favor inheritance only when creating annonymous inner classes.

Also, why do your printName() methods return a String? Seems silly and unexpected. Especially when there is no return.

This also seems like a silly example but I can't stand seeing use code and construction code forced to live together. Thus here's my pointless composision example.

public class Grape {
  Fruit fruit;
  public Grape(Fruit fruit) {
    this.fruit = fruit;
  }
  public void printName(){
    fruit.printName();
  }
}

f = new Grape(new Fruit());
f.printName()

Here you see Grape delegating printName to Fruit. Grape has the option to intervine and prevent or augment the fruit.printName() call but so far hasn't been coded to do so.

You may be thinking, I could do that with inheritance, and you'd be right. What you can't do is this:

public class Grape {
  Fruit fruit;
  public Grape(Fruit fruit) {
    this.fruit = fruit;
  }      
  public void printName(){
    fruit.printName();
  }
  public void setFruit(Fruit fruit){
    this.fruit = fruit;
  }
}

f = new Grape(new Fruit());
f.printName()
f.setFruit(new RottenFruit());
f.printName()

Just try doing that trick with inheritance.

  • isn't there a reason why most languages prevent changing the runtime type of an object? I'd personally prefer to live in code without such setters. and another thing (which I guess an interface + a bunch of boiler plate could solve) is that your composition code doesn't have polymorphism. one cannot treat a grape as a generic fruit. writing this delegation code isn't exactly pretty either. I'm sure it has other advantages, but it seems far from obvious that it's better to me. – sara Mar 22 '16 at 7:31
  • I also find it weird that the name of a grape isn't "grape". If I call makeGrape() and call printName() on it, and it prints "fruit", then I'm gonna be confused and upset. or even worse, now it could print "rotten banana" – sara Mar 22 '16 at 7:32
  • This isn't a change of runtime type. There is no cast here. It's a change of state. Setters are good at changing state at runtime. Immutability is good but not everything must be immutable. It is polymorphic. The behavior of printName() changes depending on fruit without ever testing fruit. You are absolutely right, semantically Fruit is a Grape is nutty. However, this is composition. If Grape adds methods it must be on the outside not the inside. The way around this would be to add interfaces. Then you can just treat it as NamedFood. – candied_orange Mar 22 '16 at 8:36
  • No, delegation code isn't pretty. But I whined about that already. Nothing worthwhile is happening anywhere here so no, nothing is better. This is only showing you options. For example nothing says I can't print some Grape name before, after, or instead of calling fruit.printName(). It's called the decorator pattern. You'll find inheritance can do a very similar trick as well. – candied_orange Mar 22 '16 at 8:36
1

Even if you never directly use "Fruit" in your application, if you have several identical methods between your actual Orange, Grape, Banana, Dragonfruit, (etc etc) classes, then an abstract base class for Fruit to contain them would be perfectly appropriate to satisfy DRY concerns. The below example is C# but hopefully it makes sense for Java/etc as well.

public abstract class Fruit
{
    public readonly int CaloriesPerServing;

    public Fruit(int caloriesPerServing)
    {
        CaloriesPerServing = caloriesPerServing;
    }

    public void SayFruit()
    {
        Console.WriteLine("fruit!");
    }

    public string GetCalorieStatementLabel()
    {
        return "This item contains " + CaloriesPerServing + " calories per serving.";
    }
}

public class Orange : Fruit
{
    public Orange() : base(200) { }
}

Liskov itself just means that if you have a collection of object, all of which inherit from Fruit, calling a common method, or using each of them as a parameter in a new method, will have common results. Not necessarily identical, but just logically common or related. For example, here's a violation of Liskov, because it handles a certain kind of Fruit very differently:

static void AddfFruitToFruitSalad(FruitSalad fruitSalad, Fruit fruit)
{
    if (fruit is Tomato)
        throw new InvalidFruitException("ARE YE MAD?!?!");

    fruitSalad.Add(fruit);
}
0

Composition

Composition is when you implement an object as a child of another object. So, using your example of Fruit as a base class, you would create a Grape object like so:

public class Grape {
  internal Fruit fruit;
  public Grape() {
    fruit = new Fruit();
    // set fruit's values
  }
  public void printName(){
    System.out.println(fruit.name);
  }
}

When you use composition in this fashion, you cannot pass the whole object as if it were a Fruit (I am speaking in terms of C# as you didn't specify a language).

Inheritance

Inheritance is when you implement an object as an extended version of another object.

public class Grape : Fruit {
  public Grape() : base() {
    // Set some values including fruit object values
  }
  public void printName(){
    System.out.println(base.name);
  }
}

One (of many) advantages of using inheritance in this fashion is that the Grape object can be passed in and used anywhere a Fruit object would be used.

0

The short answer is "Yes." That's a good rule to live by in general, keeping in mind that other people in the future might want to Vector<Fruit> fruitList; and if you don't have the Fruit class because you personally didn't need it, that could be a problem. (Many modern languages have ways of retroactively creating the Fruit interface and asserting conformance of some classes to it.)

There are two reasons to use inheritance, because you want type conformance (or type substitutability) or because you want to reuse code. If the only objects in your system that refer to the base class are the sub-classes, then you are only subclassing for code reuse and you should seriously consider refactoring the base class out of existence.

Your particular example is even worse because you aren't even trying to reuse code in the subclasses, so it looks like you are using sub-classing for type conformance, but you aren't even doing that.

0

I think you are focussing on technical rules too much. The is-a relationship is a good guideline but what you should really be asking is whether fruit has meaning in your problem domain or not. Sure, an orange is fruit and an apple is fruit, but do you care? If you are using fruit instances as juggle items then it may be meaningless to classify them as fruit. If you have to deal with a tax rate that applies to fruit and one that applies to vegetables, you have a good reason to classify them as fruit. There should be some dependency to fruit in your problem domain. If there is not, inheritance could still be good but you might as well descend apple and orange from object, or JuggleItem if that would apply.

Your rule is an after-the-fact check: you only know when you're done. But you could have known upfront if you would have asked my questions.

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.