第二章 面向对象分析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
5
解决思路 #2 考虑抽象 图中有两个基类 Sensor,Heater
sensor.java public interface Sensor{ public int sense(); } Heater.java public interface Heater{ public void turnOn(); public void turnOff(); } 程序的这两个类有什么用? 一个类只包含抽象方法, 但没有使用者,返回值不确定, sense()方法返回什么? 在烧水壶的传感器中返回 值有两种可能. 在保温托盘的传感器中返 回值有三种可能. 无法在接口中表达传感器 的约定. Sensor,Heater没什么用.
6
一个可行的解决方案
重新根据问题的本质特性分解细节
忘掉:烧水壶、阀门、加热托盘、传感器等 专注本质问题: 怎样冲咖啡? 向咖啡粉上倒热水,然后将产生的液体收集到 某种容器内。 可设定为两个抽象类: HotWaterSource(烧水壶、阀门、传感器)加热水, 把热水送到咖啡粉,最后滴到ContainmentVessel (保温托盘、传感器演了这个角色)容器中,且保温。
Source 保温器 通过调用 coffeeMakerAPI 函数来实现
18
M4Containment Vessel +isReady() 图 4-10 实现1sReady()函数
M4HotWaterSource.java public class M4HotWaterSource extends HotWaterSource{ public boolean isReady() { int boiler Status = CoffeeMakerAPI.api.getBoilerStatus(); return boilerStatus == CoffeeMakerAPI.BOILER_NOT_EMPTY; } }
是否可实现这三个类中的方法,去调用相应的
x CoffeeMakerAPI? 是否将咖啡机的本质同Mark4机绑定在一起就 可以? 是个糟糕的设计 利用依赖倒置(DIP)进行设计
User Interface #startBrewing HotWater Source
startBrewing()? M4UserInterface 为什么要创建保护方法 当checkButton函数被调用 ,它就调用 对start(),isReady() 检测方法的调用都是 为什么不直接从 M4UserInterface调用 Containment +checkButton CoffeeMakerAPI.getBrewButtonStatus() UserInterface 应该处理的高级行为 ,不应 start() 函数机中。 ?Vessel 耦合到 Mark4 按钮己经被按下,它就调用UserInterface 图 9 检查加热按钮 中的保护方法 startBrewing() 14
7
其描述:
HotWater Source
CoffeeFlow 图 2 交叉线 Containment Vessel
这个关系是根据物理特性设定的,而不是根 据软件操作的控制设定的。
HotWater Source
start
Containment Vessel
图 3 开启热水水流
设计中的关联是在对象间发送消息的通道, 和物理对象的流程无关。
2
需要监控的硬件
用于烧水壶的加热部件,可以开启和关闭 保温托盘的加热部件,可以开启和关闭 保温托盘的传感器,有三个状态: warmerEmpty(温热装置空闲) potEmpty(壶空), potNotEmpty (壶不空) 烧水壶中的传感器,有二个状态: boilerEmpty, boilerNotEmpty 加热键指示加热过程,加热过程结束,咖啡 煮好,指示灯亮 压力阀门,有开启和关闭状态。当开启时,烧水 壶中的压力降低,使经过过滤网的水流立刻停止。
3
硬件由硬件工程师提供API实现。 软件如何设计?
Button 灯亮 Light CoffeeMaker
这个设计如何?
烧水壶
Boiler
咖啡壶
WarmePlate 保温托盘
BoilerSensor BoilerHeater Plate Sensor Plate Heater
传感器
Sensor
Heater 加热器
10
2a:IsReady
Containment Vessel
图 5 加热按钮被按下,完成
煮咖啡时,咖啡机的用户可以把咖啡源自文库从加 热托盘上拿走。哪个对象检查壶被拿走?停止咖 啡的流动。 是ContainmentVessel对象:
咖啡壶被拿走,停止加热; 咖啡壶被放回原处,开始加热
1a:IsReady User HotWater Interface Source 3a:Start
Containment Vessel 1b:Pause 2b:Resume
2a:IsReady
图 6 暂停和恢复热水的流动
11
加热完毕 咖啡煮好,就停止热水的流动。哪个对象知道 煮咖啡的过程结束了?
User Interface 要知道,咖啡煮好, 亮灯。 HotWaterSource要知道加热过程结束,停止热水流动。 对于咖啡机,关闭烧水壶,打开阀门。 ContainmentVessel也要知道加热过程结束,检查空咖 啡壶确实放回到保湿托盘上,并发出用户倒完最后一杯咖 啡的信号。 2c:Done User Interface 2d:Done 1a:IsReady HotWater Source
1b:Pause 2b:Resume 1d:Done 2a:IsReady 图 7 检查加热的过程是否完成(done)
3a:Start 1c:Done Containment Vessel
12
咖啡倒空 加热过程结束后,咖啡机熄灭指示灯,空咖啡 壶放回到托盘上。谁来检查这种情况?
ContainmentVesselu检查,它还向UserInterface 发送一个Complete(完成)消息 2c:Done 1a:IsReady
startBrewing();
} } }
16
UserInterface.java
public class UserInterface { private HotWaterSource hws; private ContainmentVessel cv; public void done() { } public void complete() { } protected void startBrewing() { if (hws.isReady() && cv. isReady()) { hws.start(); cv.start(); } } }
17
实现准备就绪IsReady()函数
User Interface #startBrewing
Hot Water Source +isReady()
加热水 检查
M4UserInterface +checkButton()
Containment 停止/ Vessel 开始 M4HotWater +isReady() 加热
CoffeeMakerAPI.JAVA public interface CoffeeMakerAPI { public static CoffeeMakerAPI api = null;//由 main 设置 实现以下的函数: 返回加热托盘传感器的状态,该传感器检查咖啡壶否 在托盘上,其中是否有咖啡. 返回烧水壶开关的状态,检查壶中的存水是水多于1/2杯 检查开关烧水壶中的加热托盘 检查开关保温托盘中的加热托盘 检查开关指示灯 开关压力阀门,阀门关,烧水壶蒸气压力迫使开水流过 过滤器
9
User Interface
IsReady
HotWater Source
IsReady
Containment Vessel
图 4 加热按钮被按下,检查是否就绪 1a:IsReady User HotWater Interface Source 3a:Start
只要有一个 查询为false, 拒绝加热咖啡, 通知用户,请求 无效。 具体到咖啡机 上,可用指示灯闪 烁来表示。 两个查询的 结果都为true, 开始热水的流动。 具体到咖啡机上, 关闭阀门,打开烧 水壶。
3a:Start 2d:Done 1e:Complete 1c:Done Containment Vessel 2a:IsReady
User Interface
HotWater Source 1b:Pause 2b:Resume 1d:Done
13
4a:Start
图 8 咖啡倒空
如何在这种结构下实现Mark4咖啡机?
一个设计的启发: Mark4 专用咖啡机
1
描述: Mark4专用咖啡机最多一次可煮好12杯咖啡。
使用者先将咖啡粉放入滤网(filter)内,
滤网放入滤网支架(filter holder)中, 将滤网支架滑入托盘中, 咖啡壶(pot)放在保温托盘(warmer plate上。 使用者向烧水壶(boiler)内加入最多12杯冷水, 按下加热(Brew)键,水被加热至沸腾,蒸气压力迫使水漫 过咖啡粉末,咖啡通过滤网的过滤,流入咖啡壶中,并保 持一定温度。壶中有咖啡时,保温托盘处于工作状态,若 将壶从保温托盘上拿开,水流将立刻停止,这样煮沸的咖 啡不会溢出到保温托盘上。
15
检查加热按钮的状态
public int getBrewButtonStatus(); public static final int BREW_BUTTON_PUSHED = 0; public static final int BREW_BUTTON_NOT_PUSHED = 1
M4UserInterface.java public class M4UserInterface extends UserInterface{ private void checkButton() { int buttonStatus = CoffeeMakerAPI.api.getBrewButtonStatus(); if (buttonStatus == CoffeeMakerAPI.BREW_BUTTON_PUSHED) {
19
M4ContainmentVessel.java public class M4ContainmentVessel extends ContainmentVessel { public boolean isReady() { int plateStatus = CoffeeMakerAPI.api.getWarmerplateStatus(); return plateStatus == CoffeeMakerAPI. POT_EMPTY; } }
4
图 1 过于具体的咖啡机
解决思路
#1 向Light(灯亮)类添加方法,看看怎么样?
Light只有on(),off()两个方法
这段代码有什么问题? Light.java public class Light { 没有变量,奇怪,因为对象通常拥 public void on() { 有状态,且还操纵这些状态 CoffeeMakerAPI.api. 两个方法只是简单调用的一个转 setIndicatorState(CoffeeMakerAPI.INDICATOR_ON); 发者,没作任何事情,多余称为泡 泡类(Vapor Class) } //设置指示器状态 public void off() { Button,Boiler,WarmerPlate也 是如此 CoffeeMakerAPI.api. setIndicatorState(CoffeeMakerAPI.INDICATOR_OFF); } }
8
用户与系统是怎样交互的?
用类UserInterface(按钮、指示灯)来表示。
这三个类实例之间是怎样交互的? # 由哪个对象检测用户按下加热“Brew”按钮这一事件? UserInterface对象
# 加热按钮被按下时,这个对象应该做什么? 确认烧水壶满了,咖啡壶是空的,且放在保温托盘上。 UserInterface对象发送消息给: HotWaterSource ContainmentVessel,是否就绪。
解决思路 #2 考虑抽象 图中有两个基类 Sensor,Heater
sensor.java public interface Sensor{ public int sense(); } Heater.java public interface Heater{ public void turnOn(); public void turnOff(); } 程序的这两个类有什么用? 一个类只包含抽象方法, 但没有使用者,返回值不确定, sense()方法返回什么? 在烧水壶的传感器中返回 值有两种可能. 在保温托盘的传感器中返 回值有三种可能. 无法在接口中表达传感器 的约定. Sensor,Heater没什么用.
6
一个可行的解决方案
重新根据问题的本质特性分解细节
忘掉:烧水壶、阀门、加热托盘、传感器等 专注本质问题: 怎样冲咖啡? 向咖啡粉上倒热水,然后将产生的液体收集到 某种容器内。 可设定为两个抽象类: HotWaterSource(烧水壶、阀门、传感器)加热水, 把热水送到咖啡粉,最后滴到ContainmentVessel (保温托盘、传感器演了这个角色)容器中,且保温。
Source 保温器 通过调用 coffeeMakerAPI 函数来实现
18
M4Containment Vessel +isReady() 图 4-10 实现1sReady()函数
M4HotWaterSource.java public class M4HotWaterSource extends HotWaterSource{ public boolean isReady() { int boiler Status = CoffeeMakerAPI.api.getBoilerStatus(); return boilerStatus == CoffeeMakerAPI.BOILER_NOT_EMPTY; } }
是否可实现这三个类中的方法,去调用相应的
x CoffeeMakerAPI? 是否将咖啡机的本质同Mark4机绑定在一起就 可以? 是个糟糕的设计 利用依赖倒置(DIP)进行设计
User Interface #startBrewing HotWater Source
startBrewing()? M4UserInterface 为什么要创建保护方法 当checkButton函数被调用 ,它就调用 对start(),isReady() 检测方法的调用都是 为什么不直接从 M4UserInterface调用 Containment +checkButton CoffeeMakerAPI.getBrewButtonStatus() UserInterface 应该处理的高级行为 ,不应 start() 函数机中。 ?Vessel 耦合到 Mark4 按钮己经被按下,它就调用UserInterface 图 9 检查加热按钮 中的保护方法 startBrewing() 14
7
其描述:
HotWater Source
CoffeeFlow 图 2 交叉线 Containment Vessel
这个关系是根据物理特性设定的,而不是根 据软件操作的控制设定的。
HotWater Source
start
Containment Vessel
图 3 开启热水水流
设计中的关联是在对象间发送消息的通道, 和物理对象的流程无关。
2
需要监控的硬件
用于烧水壶的加热部件,可以开启和关闭 保温托盘的加热部件,可以开启和关闭 保温托盘的传感器,有三个状态: warmerEmpty(温热装置空闲) potEmpty(壶空), potNotEmpty (壶不空) 烧水壶中的传感器,有二个状态: boilerEmpty, boilerNotEmpty 加热键指示加热过程,加热过程结束,咖啡 煮好,指示灯亮 压力阀门,有开启和关闭状态。当开启时,烧水 壶中的压力降低,使经过过滤网的水流立刻停止。
3
硬件由硬件工程师提供API实现。 软件如何设计?
Button 灯亮 Light CoffeeMaker
这个设计如何?
烧水壶
Boiler
咖啡壶
WarmePlate 保温托盘
BoilerSensor BoilerHeater Plate Sensor Plate Heater
传感器
Sensor
Heater 加热器
10
2a:IsReady
Containment Vessel
图 5 加热按钮被按下,完成
煮咖啡时,咖啡机的用户可以把咖啡源自文库从加 热托盘上拿走。哪个对象检查壶被拿走?停止咖 啡的流动。 是ContainmentVessel对象:
咖啡壶被拿走,停止加热; 咖啡壶被放回原处,开始加热
1a:IsReady User HotWater Interface Source 3a:Start
Containment Vessel 1b:Pause 2b:Resume
2a:IsReady
图 6 暂停和恢复热水的流动
11
加热完毕 咖啡煮好,就停止热水的流动。哪个对象知道 煮咖啡的过程结束了?
User Interface 要知道,咖啡煮好, 亮灯。 HotWaterSource要知道加热过程结束,停止热水流动。 对于咖啡机,关闭烧水壶,打开阀门。 ContainmentVessel也要知道加热过程结束,检查空咖 啡壶确实放回到保湿托盘上,并发出用户倒完最后一杯咖 啡的信号。 2c:Done User Interface 2d:Done 1a:IsReady HotWater Source
1b:Pause 2b:Resume 1d:Done 2a:IsReady 图 7 检查加热的过程是否完成(done)
3a:Start 1c:Done Containment Vessel
12
咖啡倒空 加热过程结束后,咖啡机熄灭指示灯,空咖啡 壶放回到托盘上。谁来检查这种情况?
ContainmentVesselu检查,它还向UserInterface 发送一个Complete(完成)消息 2c:Done 1a:IsReady
startBrewing();
} } }
16
UserInterface.java
public class UserInterface { private HotWaterSource hws; private ContainmentVessel cv; public void done() { } public void complete() { } protected void startBrewing() { if (hws.isReady() && cv. isReady()) { hws.start(); cv.start(); } } }
17
实现准备就绪IsReady()函数
User Interface #startBrewing
Hot Water Source +isReady()
加热水 检查
M4UserInterface +checkButton()
Containment 停止/ Vessel 开始 M4HotWater +isReady() 加热
CoffeeMakerAPI.JAVA public interface CoffeeMakerAPI { public static CoffeeMakerAPI api = null;//由 main 设置 实现以下的函数: 返回加热托盘传感器的状态,该传感器检查咖啡壶否 在托盘上,其中是否有咖啡. 返回烧水壶开关的状态,检查壶中的存水是水多于1/2杯 检查开关烧水壶中的加热托盘 检查开关保温托盘中的加热托盘 检查开关指示灯 开关压力阀门,阀门关,烧水壶蒸气压力迫使开水流过 过滤器
9
User Interface
IsReady
HotWater Source
IsReady
Containment Vessel
图 4 加热按钮被按下,检查是否就绪 1a:IsReady User HotWater Interface Source 3a:Start
只要有一个 查询为false, 拒绝加热咖啡, 通知用户,请求 无效。 具体到咖啡机 上,可用指示灯闪 烁来表示。 两个查询的 结果都为true, 开始热水的流动。 具体到咖啡机上, 关闭阀门,打开烧 水壶。
3a:Start 2d:Done 1e:Complete 1c:Done Containment Vessel 2a:IsReady
User Interface
HotWater Source 1b:Pause 2b:Resume 1d:Done
13
4a:Start
图 8 咖啡倒空
如何在这种结构下实现Mark4咖啡机?
一个设计的启发: Mark4 专用咖啡机
1
描述: Mark4专用咖啡机最多一次可煮好12杯咖啡。
使用者先将咖啡粉放入滤网(filter)内,
滤网放入滤网支架(filter holder)中, 将滤网支架滑入托盘中, 咖啡壶(pot)放在保温托盘(warmer plate上。 使用者向烧水壶(boiler)内加入最多12杯冷水, 按下加热(Brew)键,水被加热至沸腾,蒸气压力迫使水漫 过咖啡粉末,咖啡通过滤网的过滤,流入咖啡壶中,并保 持一定温度。壶中有咖啡时,保温托盘处于工作状态,若 将壶从保温托盘上拿开,水流将立刻停止,这样煮沸的咖 啡不会溢出到保温托盘上。
15
检查加热按钮的状态
public int getBrewButtonStatus(); public static final int BREW_BUTTON_PUSHED = 0; public static final int BREW_BUTTON_NOT_PUSHED = 1
M4UserInterface.java public class M4UserInterface extends UserInterface{ private void checkButton() { int buttonStatus = CoffeeMakerAPI.api.getBrewButtonStatus(); if (buttonStatus == CoffeeMakerAPI.BREW_BUTTON_PUSHED) {
19
M4ContainmentVessel.java public class M4ContainmentVessel extends ContainmentVessel { public boolean isReady() { int plateStatus = CoffeeMakerAPI.api.getWarmerplateStatus(); return plateStatus == CoffeeMakerAPI. POT_EMPTY; } }
4
图 1 过于具体的咖啡机
解决思路
#1 向Light(灯亮)类添加方法,看看怎么样?
Light只有on(),off()两个方法
这段代码有什么问题? Light.java public class Light { 没有变量,奇怪,因为对象通常拥 public void on() { 有状态,且还操纵这些状态 CoffeeMakerAPI.api. 两个方法只是简单调用的一个转 setIndicatorState(CoffeeMakerAPI.INDICATOR_ON); 发者,没作任何事情,多余称为泡 泡类(Vapor Class) } //设置指示器状态 public void off() { Button,Boiler,WarmerPlate也 是如此 CoffeeMakerAPI.api. setIndicatorState(CoffeeMakerAPI.INDICATOR_OFF); } }
8
用户与系统是怎样交互的?
用类UserInterface(按钮、指示灯)来表示。
这三个类实例之间是怎样交互的? # 由哪个对象检测用户按下加热“Brew”按钮这一事件? UserInterface对象
# 加热按钮被按下时,这个对象应该做什么? 确认烧水壶满了,咖啡壶是空的,且放在保温托盘上。 UserInterface对象发送消息给: HotWaterSource ContainmentVessel,是否就绪。