理解继承、组合和委托的关系

类与类之间的关系,除了继承关系,还有组合(Composition)关系和委托关系:

继承的含义自不必说,组合的含义更像是一个对象(类)由各方面构成,这些方面并非来自于继承,但有时候却是必不可少的。如果说继承是垂直结构,那么组合是横向结构。

比如,房子是一个封闭的结构,有房顶,有四面墙,有大门,假设这些属性都来自于继承。但房子还有房间,有厨房,有卫生间,如果这些不是继承而来,那就需要通过组合来提供:对于房子来说,把一个或多个房间组合进来构成自己的一部分,再把一个厨房组合进来,还可以把一个卫生间也组合进来构成自己的一部分。也可以认为,房间、厨房和卫生间都是房子的一部分组件,而房子是组件的承载体。而且,房间、厨房和卫生间对象都依赖于房子对象而存在,房子对象消亡时这个房子对象中的组合对象也都将消亡

对于委托,类与类之间或对象与对象之间可以没有任何逻辑上的关系(比如继承关系和组合关系),仅仅只是委托方和被委托方的关系。不过,继承而来的方法本就会自动查找,所以这些方法不需要委托。而组合经常会结合委托一起使用,或者说组合的过程中本就依赖于委托,比如对于房子.煮饭()这个方法调用请求,应该委托或转发给厨房.煮饭()

下面的伪代码实现了组合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Kitchen {
func cooking(){
print("cooking")
}
}
class Bathroom {
func wash(){
print("washing")
}
}
class House {
# 构造方法
# 在房子对象中,将一个厨房对象、一个卫生间对象组合进来
func constructor(){
@kitchen = Kitchen.new
@bathroom = Bathroom.new
}
}

上面的房子对象和厨房对象以及卫生间对象都是共存亡的,要创建房子对象,同时会创建出厨房对象和卫生间对象,销毁房子对象时,卫生间对象和厨房对象也会随之销毁。

上面的组合虽然符合组合的逻辑,但在功能上还不完整。比如h=House.new();h.wash()会报错,因为房子对象h并没有定义wash()方法。

所以,这里还需要加入方法委托:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class House {
# 构造方法:@kitchen和@bathroom是实例变量
# 在房子对象中,将一个厨房对象、一个卫生间对象组合进来
func constructor(){
@kitchen = Kitchen.new
@bathroom = Bathroom.new
}
func cooking(){
@kitchen.cooking()
}
func wash(){
@bathroom.wash()
}
}

这样功能就完善了,每次调用h.wash()都会转而调用@bathroom.wash()

如果语言有提供相关的库或模块,甚至可以自动完成方法转发。