https://blog.csdn.net/Seriousplus/article/details/80462722
委派和继承都是为了提高代码的复用性,只是方式不同。
- 委派:一个对象请求另一个对象的功能,捕获一个操作并将其发送到另一个对象。
- 继承:利用extends来扩展一个基类。
Delegation(委托)
委托依赖于动态绑定,因为它要求特定的方法可以在运行时调用不同的代码段,可以看下面代码:
public class A {
void foo() {
this.bar();
}
void bar() {
System.out.println("a.bar");
}
}
在B类中,不使用继承,而是利用委托结合A,达到复用A类中代码的效果:
public class B {
private A a;
public B(A a) {
this.a = a;
}
void foo() {
a.foo(); // call foo() on the a-instance }
}
void bar() {
System.out.println("b.bar");
}
}
考虑下面代码的输出:
A a = new A();
B b = new B(a);
b.foo();
b.bar();
可以很容易地看出来结果:
a.bar
b.bar
这就是一种简单的委派机制。
此外,还有另一种委派的方式:
首先写一个接口,如下:
public interface List<E> {
public boolean add(E e);
public E remove(int index);
public void clear();
…
}
如果我们想在每次调用方法前,在控制台输出点提示信息,那么就可以这样做:
public class LoggingList<E> implements List<E> {
private final List<E> list;
public LoggingList<E>(List<E> list) {
this.list = list;
}
public boolean add(E e) {
System.out.println("Adding " + e);
return list.add(e);
}
public E remove(int index) {
System.out.println("Removing at " + index);
return list.remove(index);
}
…
}
相当于重写了add方法,但是是实现接口而没有使用继承。
委派的几种类型归纳
-
Use (A use B)
-
Composition/aggregation (A owns B)
-
Association (A has B)
Dependency(依赖): 临时性的delegation
- 在这种关系中,一个类使用另一个类而不将其作为一个属性。
- 两类之间的这种关系称为“uses-a”关系。例如,它可以是一个参数,或者在一个方法中本地使用,参考下面的代码:
首先新建一个课程类:
public class Course {}
在课表类总使用课程类:
public class CourseSchedule {
List<Course> courses = new ArrayList<>();
public void add (Course c) {
courses.add(c);
}
public void remove (Course c) {
courses.remove(c);
}
}
在这里,并没有将Course类作为CourseSchedule类的属性来使用,而是作为迭代器中的元素和方法中的参数来使用。
Association(关联): 永久性的delegation
- 关联是类之间的持久关系,允许一个对象实例让另一个对象实例代表它自己做其他事。
- 这种关系属于has-a的关系,是结构化的,因为它指定了一种对象与另一种对象相连接,并且不代表行为,即不在该类的方法中使用另一个类的方法,只是简单的将不同的对象连接起来。
下面展示一下关联的代码:
class Teacher {
private Student [] students;
}
class Student {
private Teacher teacher;
Course[] selectedCourses;
}
可以看到,在两个类中(Student和Teacher)互相都有彼此的实例,而且没有使用继承,就直接将这几个不同的类相连接,这就是利用了Association方式。
Composition: 更强的delegation
- 组合是将简单对象或数据类型组合成更复杂的方法的一种方法。
- 这种关系是a-part-of关系,一个类有另一个属性或实例变量——实现了一个对象包含另一个对象。
let`s show 代码:
class Heart {}
class Person {
private Heart heart = new Heart();
public void operation () {
heart.operation();
}
}
- 这种方式理解起来就很简单了,直接在该类中实例化一个其他类,然后该调用方法调用方法,对这个实例想怎么用怎么用,十分灵活。
- 不过需要注意的是:
- 这里的实例是private的,也就是说,外界访问不到,这样的话,更改其值只能在该方法中;而且每次创建该类的对象时,就已经创建好这个类中的实例;也就是说一旦创建好该类的对象,其中的属性指向便已经创建好。
Aggregation
- 聚集:对象存在于另一个之外,是在外部创建的,所以它作为一个参数传递给构造函数。
-
这种关系是has-a的关系,区别于
让我们看一个这个例子的代码:
class Student {}
class Course {
private Student[] students;
public addStudent (Student s) {
studtents.append(s);
}
}
可以看到,在这里,内部的属性是可以在外部指定的,而不是完全依赖该类。
Composition vs. Aggregation
组合和聚集是最常用的两种delegation方式,可以说,其中的使用包括了依赖和关联方式,并在其上做了进一步的扩展。二者很相似,但又有很多不同之处,这里举一个例子看一下二者的最大不同之处:
public class WebServer {
private HttpListener listener;
private RequestProcessor processor;
public WebServer(HttpListener listener, RequestProcessor processor) {
this.listener = listener;
this.processor = processor;
}
}
public class WebServer {
private HttpListener listener;
private RequestProcessor processor;
public WebServer() {
this.listener = new HttpListener(80);
this.processor = new RequestProcessor(“/www/root”);
}
}
可以看到,第一个代码是聚集的方式,第二个代码是组合的方式,这是为什么呢?
-
在组合中,当拥有的对象被销毁时,所包含的对象也是如此。
比如:
一所大学拥有不同的部门,每个部门都有许多教授,因为是大学创建了这些部门,所以如果大学关闭,这些部门将不复存在,但这些部门的教授将继续存在。 -
但是在聚集中,这并不一定是正确的:
大学可以被看作是部门的组成,而院系则是教授的集合。一个教授可以在一个以上的部门工作,但是一个部门不能成为一所大学的一部分,部门是在大学外创建的,故大学倒闭,部门还在。
Inheritance(继承)
继承就很好说了,直接是一个类利用extends扩展其父类,而且一个类只能扩展一个父类,但是可以多层扩展。
参考下面的代码:
public class A {
public void foo() {
this.bar();
}
void bar() {
System.out.println("A.bar");
}
}
public class B extends A{
public B() {}
@Override
public void foo() {
super.foo();
}
public void bar() {
System.out.println("B.bar");
}
}
可以猜一下下面代码的输出的结果,比较有趣:
B b = new B();
b.foo();
我这里运行的结果是
B.bar
可以看到,在继承中,子类拥有父类所有的方法,而且还可以继续增加父类中没有的方法。
适用于不同场合
总而言之,委派和继承都是为了代码复用,只是方式不同。
- 委托可以被看作是对象级别的重用机制,而继承是类级别的重用机制。
- 此外,如果子类只需要复用父类中的一小部分方法,可以不需 要使用继承,而是通过委派机制来实现