Inheritance vs. Composition in Java

This article illustrates the concepts of inheritance vs. composition in Java. It first shows an example of inheritance, and then shows how to improve the inheritance design by using composition. How to choose between them is summarized at the end.

1. Inheritance

Let‘s suppose we have an Insect class. This class contains two methods: 1) move() and 2) attack().

class Insect {
	private int size;
	private String color;

	public Insect(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public void move() {
		System.out.println("Move");
	}

	public void attack() {
		move(); //assuming an insect needs to move before attacking
		System.out.println("Attack");
	}
}

Now you want to define a Bee class, which is a type of Insect, but have different implement ations of attack() and move(). This can be done by using an inheritance design like the following:

class Bee extends Insect {
	public Bee(int size, String color) {
		super(size, color);
	}

	public void move() {
		System.out.println("Fly");
	}

	public void attack() {
		move();
		super.attack();
	}
}
public class InheritanceVSComposition {
	public static void main(String[] args) {
		Insect i = new Bee(1, "red");
		i.attack();
	}
}

The class hierarchy diagram is as simple as:

Output:

Fly
Fly
Attack

"Fly" was printed twice, which indicates move() is called twice. But it should be called only ONCE.

The problem is caused by the super.attack() method. The attack() method of Insect invokes move() method. When the subclass calls super.attack(), it also invokes the overridden move() method.

To fix the problem, we can:

  1. eliminate the subclass‘s attack() method. This will make the subclass depends on the super class‘s implementation of attack(). If the attack() method in the super class is changed later (which is out of your control), e.g., the super class‘s attack() method use another method to move, the subclass will need to be changed too. This is bad encapsulation.
  2. rewrite the attack() method like the following:

    public void attack() {
    	move();
    	System.out.println("Attack");
    }

    This would guarantee the correct result, because the subclass is not dependent on the superclass any more. However, the code is the duplicate of the superclass. (Image attack() method does complex things other than just printing a string) This does not following software engineering rule of reusing.

This inheritance design is bad, because the subclass depends on the implementation details of its superclass. If the superclass changes, the subclass may break.

2. Composition

Instead of inheritance, composition can be used in this case. Let‘s first take a look at the composition solution.

The attack function is abstracted as an interface.

interface Attack {
	public void move();
	public void attack();
}

Different kinds of attack can be defined by implementing theAttackinterface.

class AttackImpl implements Attack {
	private String move;
	private String attack;

	public AttackImpl(String move, String attack) {
		this.move = move;
		this.attack = attack;
	}

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

	@Override
	public void attack() {
		move();
		System.out.println(attack);
	}
}

Since the attack function is extracted,Insectdoes not do anything related with attack any longer.

class Insect {
	private int size;
	private String color;

	public Insect(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}

Bee is a type of Insect, it can attack.

// This wrapper class wrap an Attack object
class Bee extends Insect implements Attack {
	private Attack attack;

	public Bee(int size, String color, Attack attack) {
		super(size, color);
		this.attack = attack;
	}

	public void move() {
		attack.move();
	}

	public void attack() {
		attack.attack();
	}
}

Class Diagram:

public class InheritanceVSComposition2 {
	public static void main(String[] args) {
		Bee a = new Bee(1, "black", new AttackImpl("fly", "move"));
		a.attack();

		// if you need another implementation of move()
		// there is no need to change Insect, we can quickly use new method to attack

		Bee b = new Bee(1, "black", new AttackImpl("fly", "sting"));
		b.attack();
	}
}
fly
move
fly
sting

3. When to Use Which?

The following two items can guide the selection between inheritance and composition:

  1. If there is an IS-A relation, and a class wants to expose all the interface to another class, inheritance is likely to be preferred.
  2. If there is a HAS-A relationship, composition is preferred.

In summary, Inheritance and composition both have their uses, and it pays to understand their relative merits.

References:
1. Bloch, Joshua. Effective java. Pearson Education India, 2008.
2. http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
3. http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html

时间: 2024-10-10 06:15:35

Inheritance vs. Composition in Java的相关文章

Inheritance versus composition

One of the fundamental activities of an object-oriented design is establishing relationships between classes. Two fundamental ways to relate classes are inheritance and composition. Although the compiler and Java virtual machine (JVM) will do a lot o

Exercise 44: Inheritance Vs. Composition

class Parent(object): def __init__(self, **kwargs): if kwargs.has_key('age'): self.__age = kwargs['age'] if kwargs.has_key('sex'): self.sex = kwargs['sex'] def implicit(self): print "PARENT implicit()" def get_age(self): print self.__age class C

菜鸟译文(一)——Java中的继承和组合

阅读英文的能力对于程序员来说,是很重要的.这几年也一直在学习英文,今天心血来潮,就在网上找了一篇简短的博文翻译一下.水平一般,能力有限,还请各位看官多多指点. 译文: 本文将会举例说明Java中继承和组合的概念.首先举一个继承的例子,然后展示一下如何用组合来改善继承的设计.最后概括一下如何在它们之间做出选择. 1. 继承 假设我们有一个Insect类.这个类包含两个方法:一个是move(),一个是attack(). class Insect { private int size; private

新秀翻译(一个)——Java在继承和组合

阅读英文的程序猿的能力,这是非常重要的.过去的几年中一直在学习英语,今天心血来潮,在网上找什么鲍文简要翻译. 普通级,能力有限,看官还请大家多多指点. 译文: 本文将会举例说明Java中继承和组合的概念.首先举一个继承的样例.然后展示一下怎样用组合来改善继承的设计.最后概括一下怎样在它们之间做出选择. 1. 继承 假设我们有一个Insect类.这个类包括两个方法:一个是move().一个是attack(). class Insect { private int size; private Str

Java Interview Reference Guide--reference

Part 1 http://techmytalk.com/2014/01/24/java-interview-reference-guide-part-1/ Posted on January 24, 2014 by Nitin Kumar JAVA Object Oriented Concepts Java in based on Object Oriented concepts, which permits higher level of abstraction to solve any p

Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(八)之Reusing Classes

The trick is to use the classes without soiling the existing code. 1. composition--simply create objects of your existing class inside the new class. simply reusing the functionality of the code, not its form 2.inheritance--creates a new class as a t

some fragment of thinking in java part5

1. Delegation a relationship that is not directly supported by Java. This is a midway between inheritance and composition, because you place a number object in the class you are building(like composition), but at the same time you expose all the meth

Java Object JDK序列化总结

Java Object JDK序列化总结 @author ixenos Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一.Java序列化API允许我们将一个对象转换为流,并通过网络发送,或将其存入文件或数据库以便未来使用,反序列化则是将对象流转换为实际程序中使用的Java对象的过程.Java同步化过程乍看起来很好用,但它会带来一些琐碎的安全性和完整性问题,在文章的后面部分我们会涉及到,以下是本教程涉及的主题. Java序列化接口 使用序列化和serialVersionUID

java下包内继承时编译不过解决方案

代码源自<java核心技术一卷> 抽象表示如下: ManagerTest.java package inheritance; ... public class ManagerTest { } 存放于:inheritance/ManagerTest.java Manager.java package inheritance; ... public class Manager { } 存放于:inheritance/Manager.java Employee.java package inheri