一、组合与继承
组合:在新的类中产生现有类的对象。该方法只是复用了现有程序代码的功能,而非它的形式。
1)优点
- 被包含对象通过包含他们的类来访问
- 黑盒重用,因为被包含对象的内部细节是不可见的
- 很好的封装
- 每个类专注于一个任务
- 通过获得和被包含对象的类型相同的对象引用,可以在运行时动态定义组合的方式
2)缺点
- 结果系统可能会包含更多的对象
- 为了使组合时可以使用不同的对象,必须小心的定义接口
继承:按照现有类的类型创建新类。无需改变现有类的形式,采用现有类的形式并在其中添加新代码,用extends关键字实现。
1)优点
- 新的实现很容易,因为大部分是继承而来的
- 很容易修改和扩展已有的实现
2)缺点
- 打破了封装,因为基类向子类暴露了实现细节
- 白盒重用,因为基类的内部细节通常对子类是可见的
- 当父类的实现改变时可能要相应的对子类做出改变
- 不能在运行时改变由父类继承来的实现
二、组合与继承的选择
组合与继承都允许在新的类中放置子对象,组合是显式地这样做,继承是隐式地做。
组合技术通常用于想在新类中使用现有类地功能而非它的接口这种情形。即,在新类中嵌入某个对象让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。
继承则是使用某个现有类,并开发一个它的特殊版本。意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。另外考虑是否需要向上转型,若必须则继承是必要的
简单来说:
1.考虑是否需要从新类向上转型回基础类。若必须上溯,就需要继承;否则用组合。
2. “属于”关系(is-a)用继承来表达,而“包含”(has-a)关系用组合来表达。
三、代理
Java语言不直接支持代理,但是很多开发工具支持代理。
我们将一个成员对象置于所要构造的类中,但与此同时我们在新类中暴露了该成员对象的所有方法,通过下例说明
//太空船需要一个控制模块
public class SpaceShipCntrols {
void up(int velocity) {
}
void down(int velocity) {
}
void left(int velocity) {
}
void right(int velocity) {
}
void turboBoost(int velocity) {
}
}
//一种方式是继承
public class SpaceShip extends SpaceShipCntrols {
private String name;
public SpaceShip(String name){
this.name=name;
}
@Override
public String toString() {
return "SpaceShip{" +
"name='" + name + '\'' +
'}';
}
public static void main(String[] args){
SpaceShip protector=new SpaceShip("NSEA PROTECTOR");
protector.right(100);
}
}
显然SpaceShip不是SpaceShipControls类型,而且SpaceShipControls的所有方法在SpaceShip中都暴露了出来。代理解决了此问题:
public class SpaceShipDelegation {
private String name;
private SpaceShipCntrols controls=new SpaceShipCntrols();
public SpaceShipDelegation(String name){
this.name=name;
}
//Delegation methods:
public void up(int velocity) {
controls.up(velocity);
}
public void down(int velocity) {
controls.down(velocity);
}
public void left(int velocity) {
controls.left(velocity);
}
public void right(int velocity) {
controls.right(velocity);
}
public void turboBoost(int velocity) {
}
public static void main(String[] args){
SpaceShipDelegation protector=new SpaceShipDelegation("NSEA PROTECTOR");
protector.right(100);
}
}
我们可以看到上面的方法传递给底层的controls对象,接口也与使用继承得到的接口相同了,代理拥有更多的控制力,因为我们可以选择只提供成员对象的方法的某个子集。