Java:代码复用

介绍

  复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情。
  实现代码复用的第一种方法:只需在新的类中产生现有类的对象。由于新的类是由现有类的对象所组成,所以这种方法称为组合。
  实现代码复用的第二种方法:按照现有类的类型来创建新类。无需改变现有类的形式,采用现有类的形式并在其中添加新代码。这种方式称为继承

组合

  将对象引用置于新类中即可。例如我现在有两个类:

1
2
3
4
5
6
7
8
9
10
11
class Wheel {
void run(){
System.out.println("run"); // 车轮类的功能是run
}
}

class Light {
void lit() {
System.out.println("lit"); // 车灯类的功能是lit
}
}

  我现在有一个新的类Car,需要同时有run和lit的功能,此时应当使用组合,这样我就可以在Car类中同时拥有两个类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Car {
private Wheel wheel;
private Light light;

public Car(Wheel wheel, Light light) {
this.wheel = wheel;
this.light = light;
}

public void operation() {
wheel.run();
light.lit();
}

public static void main(String[] args) {
Wheel wheel = new Wheel();
Light light = new Light();
Car car = new Car(wheel, light);
car.operation();
}
}

继承与代理

  Java中的继承是一个类继承另一个类的性质的过程。比如一个新的类我们称之为衍生类或子类,继承了之前就存在的我们称之为父类、超类或者基类的类的属性和行为。
  代理仅仅是把任务传递给别的类。

  • 代理可以是继承的替代品
  • 代理意味着你将其他类的对象作为实例变量传递信息给指定的实例
  • 很多情况下代理比继承好因为代理让你去考虑你所传递的每个信息,因为实例属于一个已知的类而不是一个新的类,并且不强迫你去接受父类的所有方法:你可以只提供有用的方法
  • 代理可以被看作对象之间的关系因为一个对象转发相应的方法调用给另一个对象,这叫做代理
  • 代理的主要优势是运行时间的灵活性——代理可以很容易地改变运行时间。但是和继承不同,代理并不被主流的面向对象语言所支持,而且它不易于动多态。
    例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class RealPrinter{
    void print() {
    System.out.println("The delegate");
    }
    }

    class Printer {
    RealPrinter p = new RealPrinter();

    void print() {
    p.print();
    }
    }
    public class Tester {
    public static void main(String[] args) {
    Printer printer = new Printer();
    printer.print();
    }
    }

输出:

1
The delegate

  当使用代理时,只需要调用一些必须要用的类,不用关心是怎么实现的,只需要知道你调用的那个类知道要做什么。
  同样的代码用继承来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class RealPrinter {
void print() {
System.out.println("Printing Data");
}
}

class Printer extends RealPrinter {
void print() {
super.print();
}
}
public class Tester {
public static void main(String[] args) {
Printer printer = new Printer();
printer.print();
}
}

输出:

1
Printing Data

什么时候该用什么?
这里举几个例子来说明什么时候用代理什么时候用继承:
假设你的类叫B,是A的子类,那么如果:

  • 你想表达(is-a)关系,用继承
  • 你想把类直接传递到一个现有的需要A的API那么就用继承
  • 你想改进A,但是A已经是终点并且不能再被继承了,那你就要用组合和代理

个人理解

  首先想一想,组合是怎么工作的。我们把对象引用放在新的类里,这就意味着我们可以通过组合语法,在一个类中拥有多个指向其他类的引用。
  其次想一想继承是如何工作的,继承通过extends关键词,可以接过父类中所有的属性和方法,但是由于Java是不支持多继承的,所以一个类只能继承一个父类。
  根据特性我们可以总结一下继承和组合的优缺点:
继承
优点:

  • 支持扩展;
  • 被复用的代码易于修改

缺点:

  • 父类的实现细节都暴露给了子类,破坏了封装性;
  • 当父类代码修改时,子类也要修改,增加了维护的难度;
  • 子类缺乏独立性,与父类的耦合度高;
  • 不支持动态拓展,在编译期就决定了父类。

组合
优点:

  • 被包括的对象内部实现细节对外不可见,封装性好;
  • 整体类与局部类松耦合,相互独立;
  • 支持扩展;
  • 每个类只负责一项业务;
  • 支持动态扩展,可在运行时根据具体对象选择不同类型的组合对象(扩展性比继承好)

缺点:

  • 创建整体类对象时,需要创建所有局部类对象。导致系统对象很多。

  关于代理,《Java编程思想》中对代理有这样一句描述:这是继承与组合之间的中庸之道。在代理类中可以创建某功能的类,调用类的一些方法获得该类的部分特性。比如说我有一个飞机类,飞机有向各个方向运动的方法,还有发射导弹的方法。我可以通过在代理类中只调用(就像组合)运动方法的方法来避免暴露发射导弹的方法,同时代理类中暴露了其他运动的方法(就像继承)。

参考资料

csdn