简体   繁体   中英

Advantages of composition over inheritance in Java

The below paragraph is quoted from the book, "Thinking in Java" by Bruce Eckel.

Composition comes with a great deal of flexibility. The member objects of your new class are typically private, making them inaccessible to the client programmers who are using the class. This allows you to change those members without disturbing existing client code. You can also change the member objects at run time, to dynamically change the behavior of your program. Inheritance, which is described next, does not have this flexibility since the compiler must place compile-time restrictions on classes created with inheritance .

I don't understand, how the member object can be changed dynamically, and how it is an advantage over inheritance. Could somebody please explain this

If your class extends ArrayList , a user of your class has access to all the public methods of ArrayList . You can't change your class to extend LinkedList, since that may break existing code.

 public class Inheritence extends ArrayList<String>
 {
     // here your class depends on ArrayList implementation
 }

If, on the other hand, your class has a private member whose type is List , the users of your class can't call methods of that member directly. And you can decide in runtime whether to assign an ArrayList instance to that member, or assign a different List implementation ( LinkedList or something else).

public class Composition
{
    private List<String> member;

    public Composition ()
    {
        // here you decide the type of your member during runtime
        if (someCondition) {
            member = new ArrayList<String>();
        } else {
            member = new LinkedList<String>();
        }
    }
}

If you use inheritance, you hardly see the whole picture and you must study the code hell a lot to be able to understand what's going on. If you change one petite thing in parent class, you have trouble. What about all the descendants? Did this change affect them?

And also consider testing. Parent class can not be separated, you have to test everything and the tests are complicated and often not made at all, because the effort is just too big to make testing sense.

On the other hand, when you use composition, you can easily separate functionalities, you can use dependency injection, when reading other people's code, you can focus on smaller piece of functionality. If higher level class, which uses composition needs to replace some minor functionality by other implementation, then it is far easier, because you much better know, what's going on in the code. The scattered code in several levels of inheritance is not encapsulated and very often totally unreadable.

Inheritance is also dangerous. Assume example, when you want to count, how many items did you add to HashSet. You can override methods add and addAll, but you don't know, if addAll calls method add for every item or not. See this example

To answer your original question - you can pass the inner object by a constructor or setter.

The broader way of looking at composition and inheritance is, composition is a design technique to implement has-a relationship whereas inheritance is a technique to implement is-a relationship. Let's get into Object Orientation perspective to understand this in a better way.

When I say, Dog is a Mammal and Snake is a Reptile but both are Animals , The design can be like, there should be a super class called Animal and two child classes called Mammal and Reptile which Dog and Snake should be extending. Respective code is as below:

public abstract class Animal {
    public abstract void move();
    public abstract void sleep();
    public abstract void reproduce();

}

Any animal can perform these three actions.

public abstract class Mammal extends Animal{
    @Override
    public void reproduce(){
        System.out.println("Gives birth!!");
    }

}

public abstract class Reptile extends Animal{
    @Override
    public void reproduce(){
        System.out.println("Lays Eggs!!");
    }
}

All Mammals/Reptile will perform this defined function in common way.

public class Dog extends Mammal{

    @Override
    public void move() {
        System.out.println("Uses it's limbs");

    }

    @Override
    public void sleep() {
        System.out.println("Sleeps on it's tummy");

    }

}


public class Snake extends Reptile{

    @Override
    public void move() {
        System.out.println("Crawls");

    }

    @Override
    public void sleep() {
        System.out.println("");

    }

}

As you can observe in this example, Dog extends Mammal and Snake extends Reptile . Hence at any given point of time, Dog will always be a Mammal and a Snake will always be a Reptile . Hence, this is-a relationship builds a strong bond between two entities. During the course of execution, as long as Dog extends Mammal , it cannot be a Reptile or anything else.

Whereas coming back to has-a relationship (Composition), I can say that a Person has-a Car . I'm not specifying the type of the Car that this person has. Let's get back to OO perspective to understand this better.

Consider this Person class

public class Person {
    private String name;
    private String address;

    private Car car;

    public Person(String name, String address, Car car) {
        super();
        this.name = name;
        this.address = address;
        this.car = car;
    }


}

It can be seen here that, a Person has-a name, address and car. The code for Car and related classes goes below:

public class Car {
    //Car related methods
}

public class Ferrari extends Car{
    //Ferrari specific methods, attributes
}

public class Bmw extends Car{
    //BMW specific attributes, methods.
}

It can be seen that BMW and Ferrari are types of Car . Now coming back to the statement "Person has-a Car" , the Car could be any Car. It could be a ferrari or BMW or anything else. This can be passed in during creation of Person object.

Person john = new Person("John", "#81, 2nd Street,..", new Ferrari());

It can be seen that John takes in a Ferrari Car . Here John can take in any type of Car being passed during object creation (dynamic nature). Hence that level of flexibility is provided by has-a (composition) relation.

I hope this clears your doubt.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM