基于继承的设计与接口、多态的应用
立即解锁
发布时间: 2025-08-21 00:56:33 阅读量: 2 订阅数: 12 


Java编程艺术:从初学者到大师的进阶指南
### 基于继承的设计与接口、多态的应用
#### 1. 继承的三大好处
继承在面向对象设计和实现中扮演着关键角色,但需适度应用。使用继承至少有以下三个好处:
- **辅助理解代码行为**:精心设计的继承层次结构有助于控制概念的复杂性。若能在层次结构的最上层正确定义抽象(基类/超类型),就能对具体实现(派生类/子类型)的行为做出合理假设。良好的继承层次结构能实现多态行为,这是面向对象编程的基石。
- **实现代码复用**:类中可能包含可在应用程序中复用的代码,如 Java 平台 API。通过继承实现代码复用的关键在于,首先要在类层次结构中正确建模应用领域,并将公共行为放在继承层次结构的根类中。
- **便于增量开发**:继承允许程序员在必要和适当的时候扩展现有类(即采用现有行为),从而促进增量开发。复杂的应用程序通常以迭代方式构建,在每个迭代开发周期中实现一个或多个满足特定需求的应用程序功能。
#### 2. 继承的形式:Meyer 的继承分类法
| 继承形式 | 描述 |
| --- | --- |
| 子类型(Subtype) | 最常见的继承形式,用于将应用领域对象建模为类别(基类)和子类别(派生类)。基类仅用于指定行为,因此是抽象的(在 Java 中可以是接口)。派生类代表不同的类型。 |
| 限制(Restriction) | 派生类对基类行为施加约束,通常应用于基类的不变式(不变式是在所有情况下都必须为真的属性)。 |
| 扩展(Extension) | 派生类引入基类中没有的新行为。 |
| 视图(View) | 派生类不适合划分为不相交的类型,子类不代表不同的类型,而是对基类实例进行分类的各种方式。视图继承在基类和派生类为抽象类(或接口)时最为适用。 |
| 功能变异(Functional Variation) | 派生类重新定义(覆盖)基类方法。 |
| 类型变异(Type Variation) | 派生类重新定义基类方法签名,但这种继承在 Java 中不被允许,派生类中的覆盖方法必须具有与被覆盖的基类方法完全相同的方法签名,包括返回类型。 |
| 无效继承(Uneffecting Inheritance) | 派生类将非抽象基类方法重新定义为抽象方法,从而有效去除不需要的基类行为。 |
| 软件具现化(Software Reification) | 基类代表一种通用的数据结构,如链表,派生类希望采用链表的功能并将其转变为另一种数据结构的行为,如队列。在具现化继承中,基类提供行为(非抽象)。 |
| 结构(Structure) | 结构继承与具现化继承的不同之处在于,基类是抽象的,仅为数据结构的行为提供一组规范(抽象方法)。派生类可以提供基类指定行为的全部或部分实现,这种继承形式在 Java 集合 API 中经常出现。 |
| 实现(Implementation) | 派生类继承基类指定的行为并直接使用。 |
| 常量设施(Constant Facility) | 基类由静态 final 字段(常量)和方法组成,这些方法的主体仅执行一次以返回对公共对象的引用,例如返回单例实例的方法。 |
| 机器设施(Machine Facility) | 基类包含派生类认为对执行其任务有用的方法。 |
#### 3. Coad 的继承标准
Peter Coad 提出了一组五个检查点,可用于确保有效使用继承,每个检查点试图避免的继承形式列在括号中:
1. 派生类应建模为与基类的“是一种特殊类型”关系,而非“由……扮演的角色”关系(视图继承)。
2. 派生类永远不需要转变为其他类的对象(视图继承)。
3. 派生类应扩展基类,而不是覆盖或取消基类的行为(功能变异、无效继承)。
4. 基类不应仅仅是一个代表你想要复用功能的实用类(常量设施、机器设施)。
5. 你试图构建的继承层次结构应代表应用领域中的特殊角色、事务或设备。
#### 4. 重新审视 Person - Employee 示例
在 Person - Employee 继承层次结构中,Person 类为通用人员提供完整功能,是完全实现的非抽象类。Employee 类通过扩展 Person 类使用实现继承,并通过实现 Payable 接口使用子类型继承。但由于 Employee 类未为 Payable 接口的 pay() 方法提供实现,因此被声明为抽象类,并将 pay() 方法的最终实现责任推给其派生类。HourlyEmployee 和 SalariedEmployee 类都采用了实现、子类型和功能变异继承。
然而,根据 Coad 的标准评估,此设计未能通过多个检查点:
1. Employee 类与 Person 基类之间并非严格的“是一种特殊类型”关系。
2. 位于继承层次结构根部的 Person 类未建模角色、事务或设备。
3. 尽管在这个有限的示例中不明显,但子类可能需要转变为其他子类类型。
这引发了以下问题:“这个设计能否改进以及如何改进?”和“当前设计是否完全无效且不可用?”接下来将通过探讨接口、多态和组合设计来解决这些问题。
下面是 Person - Employee 继承关系的 mermaid 流程图:
```mermaid
graph TD;
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
Person([Person]):::startend --> Employee([Employee]):::process;
Employee --> HourlyEmployee([HourlyEmployee]):::process;
Employee --> SalariedEmployee([SalariedEmployee]):::process;
Employee -->|implements| Payable([Payable]):::process;
```
#### 5. 接口的作用
Java 接口构造提供了指定类型行为的方式,接口可以从其他接口继承,还可以包含常量定义,这有助于应用多种继承形式,包括子类型继承、扩展继承和常量继承。
一个 Java 类只能从另一个类继承(使用 extends 关键字),但可以实现任意数量的接口。这是使用接口的关键优势之一,任何类都可以根据需要实现任何接口,而不受其继承层次结构的静态类型限制。例如,通过实现 ActionListener 接口并为其 actionPerformed() 方法提供行为,可以使任何类成为 ActionListener。
#### 6. 减少或限制模块间依赖
接口的另一个强大优势是能够减少对具体实现类的模块间依赖。如果针对接口编程,代码将不再依赖于该接口的任何特定实现。任何实现该接口的对象都可以在代码中需要该接口类型对象的地方使用(多态替换)。
但仅仅使用接口并不能自动减少功能依赖,虽然无法完全消除代码中的所有功能依赖,但可以通过架构组织应用程序,将其限制在少数几个类中。一种实现此任务的方法是使用工厂类模式,工厂是用于创建指定类型对象实例的对象,通常该类型是接口(可以是普通接口或抽象类)。在实践中,使用工厂类模式通常涉及两个接口:一是与工厂创建的对象类型对应的接口,二是工厂本身的接口,以便在应用程序启动时可以使用不同的工厂实例。工厂类模式通常与单例模式一起使用,因为所有需要工厂创建的对象类型的对象都只使用一个工厂对象实例。
#### 7. 建模主导、附属和动态角色
在建模应用领域实体或概念时,何时将其
0
0
复制全文
相关推荐










