Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
920 views
in Technique[技术] by (71.8m points)

oop - Can inheritance be replaced completely by composition?

This question is NOT question like "inheritence vs composition".

I understand completely how inheritance differs from composition, I know the Liskov substitution principle, the diamond problem, advantages and disadvantages both of them and both concepts seem to be simple. But there is so many questions everywhere about inheritance and composition, that i thought, maybe I misunderstand something in this simple idea.

Lets focus on Go. Go is a language from Google and everybody is excited it has no inheritance, it has no classes, but it has composition and this is cool. For me, the composition in Go gives you exactly the same functionality as inheritance in other languages (C++, Java, ...) - component methods are automatically exposed and available as methods of later structs, like here:

package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    Car   
}

func main() {
    var c MyCar
    fmt.Print(c.move())
}

So to sum everything up, composition is better than inheritance because:

  1. is more flexible (allows you to change the component in runtime, so you can affect the way "classes" work.
  2. is free from diamond problem (but diamond problem is solvable, so this is not strong advantage)

And if you consider Go and its interfaces (every object, that has methods defined by an interface, implements this interface implicite) do you have the ultimate solution? Can we say that composition with some syntactic sugar can replace inheritance?

Such design agrees with Liskov substitution principle. Do I miss something or inheritance (known from any language) has no advantages over composition (and interfaces) known from Go?

===== edit1 =====

For clarification, it is possible in Go to use "standard" composition mechanism, like this (this example behaves like the previous one):

package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    car Car
}

func (c *MyCar) move() bool { 
    return c.car.move()
} 

func main() {
    var c MyCar
    fmt.Print(c.move())
}

But if you use it like in the previous example, all the methods are available implicite "in MyCar class".

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The Short Answer

It's really not as black and white as that. In short, yes. Any situation that can be solved with inheritance can be solved, near enough, by composition. So in short, the answer to your question is yes; inheritance can be replaced by composition.

Why it's not that simple

When to use Inheritance
It's not a matter of whether you CAN swap them out. It depends on the context that you're programming in, and it becomes more of a question of whether you SHOULD swap them out. Take this simple example in Java:

public class Person
{
    // Assume every person can speak.
    public void speak()
    {
    }
}

Now, let's say we have another class, Dave. Dave IS a person.

public class Dave extends Person
{
     public void speak() { System.out.println("Hello!"); }
     public void killSomeone() {} // Dave is a violent Guy.
}

Now would it make more sense for the class Dave to look like this?

public class Dave
{
     private Person p;
     // Composition variant.

     public void speak() { p.speak(); }
     public void killSomeone() {} // Dave is a violent Guy.
}

This code implies Dave has a person. It's not as simple and doesn't explain itself as well. Also, anything a Person can do, Dave can do, so it makes sense that we assert Dave is a "Person".

When to use Composition

We use Composition when we only want to expose part of the class' interface. Following our previous example, let's say Dave has a Guitar. The guitar has a more complex interface:

public class Guitar
{
     public Color color;
     // Guitar's color.
     public Tuning tuning;
     // Guitar's tuning.

     public void tuneGuitar()
     {}

     public void playChord()
     {}

     public void setColor()
     {}
}

Now, if we were to inherit this class, what would the outcome be?

Well, class Dave would now have attributes color and tuning. Does Dave have a tuning? I think not! This is where inheritance makes no sense. We don't want to expose the entire Guitar interface along with the Dave interface. We only want the user to be able to access what Dave needs to access, so in this case we would use some composition:

public class Dave extends Person
{
     private Guitar guitar;
     // Hide the guitar object. Then limit what the user can do with it.

     public void changeGuitarColor(Color newColor)
     {
         // So this code makes a lot more sense than if we had used inheritance.
         guitar.setColor(newColor);
     }


     public void speak() { System.out.println("Hello!"); }
     public void killSomeone() {} // Dave is a violent Guy.
}

Conclusion

It's really not a case of what can replace the other. It's about the situation that you are implementing the techniques in. Hopefully, by the end of the example you'll see that inheritance is for situations where one object IS A object, and composition is used when one object HAS A object.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...