(1)在控制台输出“请选择要租车的类型:(1代表轿车,2代表卡车)”,等待用户输入。
(2)如果用户选择的是轿车,则在控制台输出“请选择轿车品牌:(1代表红旗,2代表长城)”,等待用户输入。
(3)如果用户选择的是卡车,则在控制台输出“请选择卡车吨位:(1代表5吨,2代表10吨)”,等待用户输入。
(4)在控制台输出“请给所租的车起名:”,等待用户输入车名。
(5)所租的车油量默认值为20升,车辆损耗度为0(表示刚保养完的车,无损耗)。
(6)具有显示所租车辆信息功能,显示的信息包括:车名、品牌/吨位、油量和车损度。
程序员开发出来的软件是需要满足用户需求的,所以程序员做分析和设计的依据是用户需求,通常是软件开发前期形成的“需求规格说明书”。面向对象设计时,首先要阅读用户需求,找出需求中名词部分用来确定类和属性,找出动词部分确定方法。
首先要进行类抽象,就是发现类并定义类的属性和方法。具体的步骤如下。
(1)发现名词。
通过阅读需求,发现需求中有类型、轿车、卡车、品牌、红旗、长城、吨位、车名、油量、车损度等名词。
(2)确定类和属性。
通过分析,车名、油量、车损度、品牌这些名词依附于轿车这个名词,车名、油量、车损度、吨位依附于卡车这个名词,所以可以将轿车、卡车抽象成类,依附于这些类的名词抽象成属性。
需要补充一点,不是所有依附于类的名词都需要抽象成属性,因为在分析需求的过程中会会发现其中某些名词不需要关注,则在抽象出类的过程中放弃这些名词,不将其抽象成属性。例如红旗、长城,这是两个轿车的品牌,属于属性值,不需要抽象成类或属性。
(3)确定方法。
根据对轿车和卡车的类抽象,可以得到如图8.1和图8.2所示的结果。

类抽象的目的在于抽象出类,并确定属性和方法,而接下来的类封装,则要在封装的角度隐藏类的属性,提供公有的方法来访问这些属性。
最简单的操作方法就是,把所有的属性都设置为私有属性(表示私有属性和方法时,需在类图中的属性和方法前加上“-”号),每个私有属性都提供getter和setter公有的方法(表示公有属性和方法时,需在类图中的属性和方法前加上“+”号),封装后的类图如图8.3和图8.4所示,在类图中设定了类的成员变量的初始值。

这样的封装过于简单,没有考虑需求,接下来进一步阅读需求,可以发现以下几点。
(1)租车时可以指定车的类型和品牌(或吨位),之后不允许修改。
(2)油量和车损度租车时取默认值,只有通过车的加油和行驶的行为改变其油量和车损度值,不允许直接修改。
根据需求,应对轿车类和卡车类做如下修改。
(1)去掉所有的setter方法,保留所有的getter方法。
(2)提供addOil()、drive()这两个公有的方法,实现车的加油和行驶的行为。
(3)至少需要提供一个构造方法,实现对类型和品牌(或吨位)的初始化。
调整后的类图如图8.5和图8.6所示。

封装后的Car类代码如下所示,具体内容看注释。
封装后的Truck类代码如下所示。
将之前《租车系统》的需求总结如下。
(2)如果用户选择的是轿车,则在控制台输出“请选择轿车品牌:(1代表红旗,2代表长城)”,等待用户输入。
(3)如果用户选择的是卡车,则在控制台输出“请选择卡车吨位:(1代表5吨,2代表10吨)”,等待用户输入。
(4)在控制台输出“请给所租的车起名:”,等待用户输入车名。
(5)所租的车油量默认值为20升,车辆损耗度为0(表示刚保养完的车,无损耗)。
(6)具有显示所租车辆信息的功能,显示的信息包括车名、品牌/吨位、油量和车损度。
(7)租车时指定车的类型和品牌(或吨位),之后不允许修改。
(8)油量和车损度租车时取默认值,只有通过车的加油和行驶的行为改变其油量和车损度值,不允许直接修改。
按需求完成代码如下,程序运行结果如图8.7所示。
在Car类和Truck类的代码中,addOil()方法和drive()方法的功能还没有实现,接下来结合需求,分别完成Car类和Truck类中的这两个方法。
《租车系统》增加了如下需求。
(1)不论是轿车还是卡车,油箱最多可以装60升汽油,每次给车加油,增加油量20升。如果加油20升超过油箱容量时,则加到60升即可,并在控制台输出“油箱已加满!”。
(2)汽车行驶1次,耗油5升,车损度增加10,如果油量低于10升,则不允许行驶,直接在控制台输出“油量不足10升,需要加油!”。
具体实现代码如下所示。
执行下面的代码,注意观察油量和车损度的变化,程序运行结果如图8.8所示。
