需要继承的原因
- 它支持更丰富、更强大的建模。类之间联系更紧密,结构更清晰。在画UML图时重复的属性、函数就可以省略
- 使得子类可以共享父类的一些定义
- 从面向对象的角度来说,继承是自然的
设计类的层次结构
首先确定层次结构底部有哪些类,接着找出一般的概念,以丰富模型、共享元素的定义
共享的方法,放置的层次越高越好
以四个集合类为例,设计层次结构
四类集合:
List:把所有的对象按照插入的顺序放置
Bag:该集合中的对象没有排序
LinkedList:链表
ArrayList:该集合中的对象使用数组排序
按照集合中的对象有序/无序进行分层
将下面三个消息放到层次结构中
contains(:Object):是否包含该对象
elementAt(:int):在参数指定的位置检索对象
numberOfElements():返回集合中的对象数
elementAt要把位置作为参数,所以它必须处理有顺序的对象。故消息放置情况如下:
抽象类
抽象方法:没有具体实现
抽象类至少要有一个抽象方法。它可以是该类本身的方法,也可以是从父类继承来的。
大多数面向对象的语言都禁止创建抽象类的实例。
继承层次结构是如何自底向上派生的
- 在问题域中查找具体的概念,推导它们的知识和行为
- 在具体的类中找出共同点,以便引入更一般的超类
- 把超类组合到更一般的超类中,直至找出最一般的根类为止
大多数情况下,超类都是抽象的
继承具体的类改变其中已有的元素,以此完成我们希望的任务。这样有一个缺陷:已有的代码无法创建改进类的实例
重定义(override)
重定义的原因:
- 继承的方法是抽象的
- 子类中的方法需要完成一些额外的工作
- 子类可以有更好的实现方法
在重定义时,应确保超类定义仍完成它以前完成的工作,这提高了代码共享性,简化了维护。每种面向对象的语言都允许重定义的方法调用超类上的方法
实现栈类
Stack类中需要有以下四个消息:
push():把对象添加到栈的顶部
peek():返回栈顶的对象
isEmpty():栈是否为空
pop():从栈顶删除一个对象
对比LinkedList类中,有相似的消息
addElement():在列表的尾部添加一个对象
lastElement():返回列表尾部的对象
numberOfElement():返回列表中的对象数
removeLastElement():删除列表尾部的对象
因此,我们把已有的LinkedList行为融合到新的Stack类中就可以了
使用继承实现
public class Stack extends LinkedList
{
public void push(Object o)
{
addElement(o);
}
public Object peek()
{
return lastElement();
}
public boolean isEmpty()
{
return numberOfElement() == 0;
}
public Object pop()
{
Object o = lastElement();
removeLastElement();
return o;
}
}
这样实现有一个严重的问题:LinkedList有一些消息是不适合栈的,例如firstElement()。用户可能通过这个方法把栈底部的元素给删除了,这是严重错误的。
使用复合实现
上图显示的Stack带有对LinkedList的引用。Stack将其行为委托给LinkedList,它对LinkedList的唯一引用在内部。
使用复合实现的Stack如下:
public class Stack
{
private LinkedList list;
public Stack()
{
list = new LinkedList();
}
public void push(Object o)
{
list.addElement(o);
}
public Object peek()
{
return list.lastElement();
}
public boolean isEmpty()
{
return list.numberOfElement() == 0;
}
public Object pop()
{
Object o = list.lastElement();
list.removeLastElement();
return o;
}
}
使用复合实现,Stack类中必须声明和创建一个字段,其方法必须引用该字段。
复合的优点
复合会得到与继承相同的结果(具体类、具体消息和重用已有的代码),但相比于继承有以下优点
- 较容易开发
- 发现设计不足时容易改变
- 客户容易理解
- 不会泄露给客户代码
私有继承
私有继承也称实现继承,允许一个类继承另一个类,而继承的元素无需成为新接口的一部分。通过私有继承的类可以直接访问需要的方法,不必引入委托。但同时因为继承是私有的,客户不能使用部分不合适的消息。
多重继承
多重继承中,每个类可以有任意多个父类,综合几种继承层次结构的优点
优点:
允许私有继承
更接近真实情况。现实中,物品往往有多种分类标准,类也是如此
允许混合继承。混合继承是一种设计模式,主要的类使用单重继承,同时允许各个类继承一个或多个辅助类,每个辅助类都会添加几个简单的元素
缺点:
更复杂
名称冲突
重复继承。重复继承表示从多条路径上继承了同一个元素
程序更难编写
使用继承的规则
- 不要过度使用
- 子类和父类之间应该有关系。比如Potato类继承Fruit类,这就是错误的
- 子类应是父类的扩展。在子类中,应确保只添加新的特性,不要删除、禁用或重新解释特性
- 尽量继承抽象类。