10. 面向对象思考
10.1 类的抽象和封装
类的抽象是指将类的实现和类的使用分离开,实现的细节被封装并且对用户隐藏,这被称为类的封装。
前面已经学习了方法的抽象以及如何在逐步求精中使用它。Java 提供了多层次的抽象。类抽象(class abstraction) 是将类的实现和使用分离。类的创建者描述类的功能,让使用者明白如何才能使用类。从类外可以访问的方法和数据域的集合以及预期这些成员如何行为的描述,合称为类的合约(class’s contract)。如下图所示,类的使用者不需要知道类是如何实现的。实现的细节经过封装,对用户隐藏起来,这称为类的封装(class encapsulation)。例如:可以创建一个 Circle 对象,并且可以在不知道面积是如何计算出来的情况下,求出这个圆的面积。由于这个原因,类也称为抽象数据类型(Abstract Data Type, ADT)。
类的抽象和封装是一个问题的两个方面。现实生活中的许多例子都可以说明类抽象的概念。例如:考虑建立一个计算机系统。个人计算机有很多组件~~CPU、内存、磁盘、 主板和风扇等。每个组件都可以看作是一个有属性和方法的对象。要使各个组件一起工作,只需要知道每个组件是怎么用的以及是如何与其他组件进行交互的,而无须了解这些组件内部是如何工作的。内部功能的实现被封装起来,对你是隐藏的。所以,你可以组装一台计算机,而不需要了解每个组件的功能是如何实现的。
对计算机系统的模拟准确地反映了面向对象方法。每个组件可以看成组件类的对象。例如,你可能已经建立了一个类,模拟用在计算机上的各种类型的风扇,它具有风扇尺寸和速度等属性,还有像开始和停止这样的方法。一个具体的风扇就是该类具有特定属性值的实例。
10.2 面向对象的思考
面向过程的范式重点在于设计方法。面向对象的范式将数据和方法耦合在一起构成对象。使用面向对象范式的软件设计重点在对象以及对对象的操作上。
一个程序的代码在 main 方法中,就不能在其他程序中重用。为使之具备可重用性,定义一个静态方法计算身体质量指数,如下所示:
public static double getBMI(double weight, double height)
这个方法对于计算给定体重和身高的身体质量指数是很有用的。但是,它是有局限性的。假设需要将体重和身髙同一个人的名字与出生日期相关联,虽然可以分别声明几个变量来存储这些值,但是这些值不是紧密耦合在一起的。将它们耦合在一起的理想方法就是创建一个包含它们的对象。因为这些值都被绑定到单独的对象上,所以它们应该存储在实例数据域中。可以定义一个名为 BMI的类,如下图所示:
假设 BMI 类是可用的。下列程序清单给出使用这个类的测试程序。显示
UseBMICIass.java
public class UseBMICIass {
public static void main(String[] args){
BMI bmil = new BMI("Kim Yang",18, 145, 70);
System.out.println("The BMI for " + bmil.getName() + " is ")+ bmi1.getBMI() + " " + bmil.getStatus());
BMI bmi2 = new BMI("Susan King", 215, 70);
System.out.println("The BMI for " + bmi2.getName()+ " is " + bmi2.getBMI() + " "bmi2.getStatus());
}
}
显示
The BMI for Kim Yang is 20.81 Normal
The BMI for Susan King is 30.85 0bese
第 3 行为 Kim Yang 创建一个对象 bmi1, 而第 7行为 Susan King 创建一个对象 bmi2。可以使用实例方法 getName( )、getBMI( )和 getStatus( )返回一个 BMI 对象中的 BMI 信息。
10.3 类的关系
为了设计类,需要探究类之间的关系。类中间的关系通常是关联、聚合、组合以及继承。
10.3.1 关联
关联是一种常见的二元关系,描述两个类之间的活例如,学生选取课程是 Student类和 Course 类之间的一种关联,而教师教授课程是 Faculty 类和 Course 类之间的关联。这些关联可以使用 UML 图形标识来表达,如下图所示:
关联由两个类之间的实线表示,可以有一个可选的标签描述关系。上图中,标签是Take 和 Teach。每个关系可以有一个可选的小的黑色三角形表明关系的方向。在该图中,方向表明学生选取课程(而不是相反方向的课程选取学生)。
关系中涉及的每个类可以有一个角色名称,描述在该关系中担当的角色。上图中,Teacher 是 Faculty 的角色名。关联中涉及的每个类可以给定一个多重性(multiplicity), 放置在类的边上用于给定 UML 图中关系所涉及的类的对象数。多重性可以是一个数字或者一个区间,决定在关系中涉及类的多少个对象。字符 * 意味着无数多个对象,而 m ..n 表示对象数处于 m 和 n 之间,并且包括 m 和 n。上图中,每个学生可以选取任意数量的课程数,每门课程可以有至少 5 个最多 6 个学生。每门课程只由一位教师教授,并且每位教师每学期可以教授 0 到 3门课程。
在 Java 代码中,可以通过使用数据域以及方法来实现关联。例如,上图中的关系可以使用下图中的类来实现。关系 “一个学生选取一门课程” 使用 Student类中的 addCourse 方法和 Course 类中的 addStudent 方法实现。关系 “一位教师教授一门课程” 使用 Faculty 类中的 addCourse 方法和 Course 类中的 setFaculty 方法实现。Student 类可以使用一个列表来存储学生选取的课程,Faculty 类可以使用一个列表来存储教师教授的课程,Course 类可以使用一个列表来存储课程中登记的学生以及一个数据域来存储教授该课程的教师。
注意:实现类之间的关系可以有很多种可能的方法。例如,Course 类中的学生和教师信息可以省略,因为它们已经在 Student 和 Faculty 类中了。同样的,如果不需要知道一个学生选取的课程或者教师教授的课程,Student 或者 Faculty 类中的数据域 courseList 和 addCourse 方法也可以省略。
10.3.2 聚集和组合
聚集是关联的一种特殊形式,代表了两个对象之间的归属关系。聚集建模 has -a 关系。所有者对象称为聚集对象,它的类称为聚集类。而从属对象称为被聚集对象,它的类称为被聚集类。
— 个对象可以被多个其他的聚集对象所拥有。如果一个对象只归属于一个聚集对象,那么它和聚集对象之间的关系就称为组合(composition)。例如:“一个学生有一个名字” 就是学生类 Student 与名字类 Name 之间的一个组合关系,而 “一个学生有一个地址” 是学生类 Student 与地址类 Address 之间的一个聚集关系,因为一个地址可以被几个学生所共享。在 UML 中,附加在聚集类(例如:Student ) 上的实心菱形表示它和被聚集类(例如:Name ) 之间具有组合关系;而附加在聚集类(例如:Student)上的空心菱形表示它与被聚集类(例如: