1. Using composition when you want the functionality of an existing class inside your new class, but not its interface. That is, embed a (usually private) object to implement features in your new class. The user of your new class sees the interface you've defined for new class rather than the interface from the embedded object.
Sometimes it makes sense to allow the class user to directly access the composition of your new class. For this, make the member objects public (you can think of this as a kind of "semi-delegation"). The member objects use implementaion hiding themselves, so this is safe. When the user knows you're assembling a bunch of parts, it makes the interface easier to understand. A car object is a good example:
// reuse/Car.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Composition with public objects
class Engine {
public void start() {}
public void rev() {}
public void stop() {}
}
class Wheel {
public void inflate(int psi) {}
}
class Window {
public void rollup() {}
public void rolldown() {}
}
class Door {
public Window window = new Window();
public void open() {}
public void close() {}
}
public class Car {
public Engine engine = new Engine();
public Wheel[] wheel = new Wheel[4];
public Door left = new Door(), right = new Door(); // 2-door
public Car() {
for (int i = 0; i < 4; i++) wheel[i] = new Wheel();
}
public static void main(String[] args) {
Car car = new Car();
car.left.window.rollup();
car.wheel[0].inflate(72);
}
}
Making the members public assists the client programmer’s understanding of how to use the class and requires less code complexity for the creator of the class. However, keep in mind this is a special case. In general, make fields private .
2. When you inherit, you take an existing class and make a special version of it. This usually means taking a general-purpose class and specializing it for a particular need. With a little thought, you’ll see it would make no sense to compose a car using a vehicle object—a car doesn’t contain a vehicle, it is a vehicle. The is-a relationship is expressed with inheritance, and the has-a relationship is expressed with composition.
Although it's possible to create protetected fields, the best approach is to leave the fields private and always preserve your right to change the underlying implementation.
One of the clearest ways to determine whether to use composition or inheritance is to ask whether you'll ever upcast from your new class to the base class. If you must upcast, inheritance is necessary, buf if you don't upcast, look closely at whether you need inheritance.
A general guideline is “Use inheritance to express differences in behavior, and fields to express variations in state.” In the following example, both are used; two different classes are inherited to express the difference in the act() method, and Stage uses composition to allow its state to be changed. Here, that change in state happens to produce a change in behavior.
// polymorphism/Transmogrify.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Dynamically changing the behavior of an object
// via composition (the "State" design pattern)
class Actor {
public void act() {}
}
class HappyActor extends Actor {
@Override
public void act() {
System.out.println("HappyActor");
}
}
class SadActor extends Actor {
@Override
public void act() {
System.out.println("SadActor");
}
}
class Stage {
private Actor actor = new HappyActor();
public void change() {
actor = new SadActor();
}
public void performPlay() {
actor.act();
}
}
public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
/* Output:
HappyActor
SadActor
*/
references:
1. On Java 8 - Bruce Eckel
2. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/reuse/Car.java
3. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/polymorphism/Transmogrify.java