**2021年春季学期
** 计算学部《软件构造》课程
**Lab 3实验报告
**
姓名 | |
---|---|
学号 | |
班号 | |
电子邮件 | |
手机号码 |
目录
3.2 面向可复用性和可维护性的设计:IntervalSet· 1
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)··· 2
3.3 面向可复用性和可维护性的设计:MultiIntervalSet· 2
3.3.1 MultiIntervalSet的共性操作··· 2
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)··· 2
1 实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性 的软件,主要使用以下软件构造技术: l 子类型、泛型、多态、重写、重载 l 继承、代理、组合 l 语法驱动的编程、正则表达式 l API 设计、API 复用 本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学 课表管理),学生不是直接针对每个应用分别编程实现,而是通过 ADT 和泛型等 抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性 和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)
2 实验环境配置
\1. 首先下载IDEA
\2. 在IDEA setting中的下载最新版本的Git,并于IDEA连接上
\3. 在IDEA中绑定你的Github帐号
\4. 通过VCS建立本地仓库,并Commit and Push
\5. 通过Git将本地代码上传到实验仓库中(根据Url)
\6. 进入Travis中,绑定你的Github帐号,并将实验所用的Private库授权
\7. 在项目中添加一个.yml文件,并且在.pom写下配置文件,用于配置编译
\8. 如果Git不上去的话,在命令行中git init之后,强行push上去,git push origin master -f 输入你的Github帐号密码即可Push上去
在这里给出你的GitHub Lab3仓库的URL地址。
Lab3: https://github.com/ComputerScienceHIT/lab3-1190200610.git
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
简要介绍三个应用:
第一个应用场景为值班表管理::一个单位有 n 个员工,在某个时间段内(例 如寒假 1 月 10 日到 3 月 6 日期间),每天只能安排唯一一个员工在单位 值班,且不能出现某天无人值班的情况;每个员工若被安排值班 m 天 (m>1),那么需要安排在连续的 m 天内。值班表内需要记录员工的名 字、职位、手机号码,以便于外界联系值班员。
我实现的界面大致如下:
第二个应用为操作系统进程调度::考虑计算机上有一个单核 CPU,多个进程被操作系统创建出来,它们被调度在 CPU 上执行,由操 作系统决定在各个时段内执行哪个线程。操作系统可挂起某个正在执行 的进程,在后续时刻可以恢复执行被挂起的进程。可知:每个时间只能 有一个进程在执行,其他进程处于休眠状态;一个进程的执行被分为多 个时间段;在特定时刻,CPU 可以“闲置”,意即操作系统没有调度执行 任何进程;操作系统对进程的调度无规律,可看作是随机调度。
我实现的情况大致如下:
第三个应用场景为课表管理:看一下你自己的课表,每一上午 10:00-12:00 和每周三上午 8:00-10:00 在正心楼 42 教室上“软件构造”课 时间轴 同一颜色的矩形表示同一个进程 Lab Manuals Lab-3 Reusability and Maintainability oriented Software Construction 程。课程需要特定的教室和特定的教师。在本应用中,我们对实际的课 表进行简化:针对某个班级,假设其各周的课表都是完全一样的(意即 同样的课程安排将以“周”为单位进行周期性的重复,直到学期结束); 一门课程每周可以出现 1 次,也可以安排多次(例如每周一和周三的“软 件构造课”)且由同一位教师承担并在同样的教室进行;允许课表中有空 白时间段(未安排任何课程);考虑到不同学生的选课情况不同,同一个 时间段内可以安排不同的课程(例如周一上午 3-4 节的计算方法和软件 构造);一位教师也可以承担课表中的多门课程。
我实现的大致如下:
共性:每个场景的底层数据结构都是我们实现的MultiIntervalSet,其中的插入操作,删除操作都是相同的。而对于构造的思想,都是基于MultiIntervalSet
差异:在值班表管理中,对于同一时间段不能有多个人在同时值班,因此我们需要用委托的思想来运用NonOverlapIntervalSetImpl的接口实现,来完成在同一个时间段不能有多个人值班。同时,在值班表的应用当中,在值班范围内不能有空白的时间段。因此,我们也用委托的思想,运用NoBlankIntervalSetImpl的接口实现,来实现不能存在空闲时间段的性质。
在操作系统进程调度管理中,再次应用中可以出现空白,但是CPU在一个时间段只可以运行一个进程。因此,同课程表应用,因此我们需要用委托的思想来运用NonOverlapIntervalSetImpl的接口实现,来完成在同一个时间段不能有多个进程同时运行。
在课表管理中,我们需要有周期性的性质。因此,我们需要运用PeriodicIntervalSetImpl委托实现这个周期性的性质。
3.2 面向可复用性和可维护性的设计:IntervalSet
3.2.1 IntervalSet的共性操作
\1. IntervalSet 接口实现
我们首先定义IntervalSet的接口,来从比较高层次的方面来定义IntervalSet的功能。
1) empty方法
返回一个空的intervalSet对象
2) insert方法
向IntervalSet中插入一个以类型为L的label变量,起始时间为start,终止时间为end的时间段。
3)labels方法
返回HashMap中时间段的标签集合
4)remove方法
在IntervalSet集合中删除标签为label的集合
5) getInterval方法
返回Interval中的HashMap集合,也就是时间段的集合
6) NumLabels方法
返回IntervalSet中的标签数量
7) getStart方法
获得label标签的开始时间
8) getEnd方法
获得label标签的结束时间
hashcode and equals方法
用来表达对interval类型变量的相等判断
接下来是Interval接口中的静态方法,对于每个ADT调用时可能用到的
1) overlap方法
对于两个IntervalSet,来判断两个intervalSet中时间是否有重合的区域,如果没有返回false,如果有返回true。
2) minTime方法
计算IntervalSet s中的最短的时间,并返回
3) maxTime方法
计算IntervalSet s中的最长的时间,并返回
4) timeLength方法
计算一个IntervalSet集合中时间段的总长度
5) similarityLength方法
计算两个IntervalSet中重合时间段的长度
3.2.2 局部共性特征的设计方案
我们介绍完了IntervalSet接口中的定义,我们具体来看实现类CommonIntevalSet中的具体实现
首先是定义
1) 其中HaspMap<L, ArrayList> interval 用来表示一个标签对应的时间段的集合。其中两个常量startIndex,endIndex表示ArrayList中的数组索引,因此ArrayList[0]来保存开始时间,ArrayList[1]来保存结束时间。后面取得时候就用我们的两个常量来取。
2) 下两个long类型的数据是我们的参数,用来保存传入的时间段的开始时间与结束时间。
3) 接下来是checkRep,来保证ADT的RI
其中,保证每个时间段的结束时间都大于等于开始时间。
接下来是就是对于接口功能的依次实现
4) insert方法
5) labels方法
6) remove方法
7) NumLabels方法
8) getStart方法
9) getEnd方法
10) equals与hashcode方法
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
我的应用都是继承于MultiIntervalSet而设计的,而MultiIntervalSet又是基于IntervalSet设计的。对于我的基于委托的设计方法,我在MultiIntervalSet中会继续重点分析,在这里不过多赘述。
3.3 面向可复用性和可维护性的设计:MultiIntervalSet
3.3.1 MultiIntervalSet的共性操作
同Interval我们在这节也将分析MultiInterval接口中定义的操作
1) empty方法
返回一个空的CommonMultiIntervalSet对象
2) insert方法
向MultiIntervalSet中插入一个以类型为L的label变量,起始时间为start,终止时间为end的时间段。
3) labels方法
返回MultiIntervalSet中的所有标签
4) remove方法
在MultiIntervalSet中除去label标签
5) intervals方法
返回一个ArrayList数组,其中包含了标签label的所有时间段,其中每个时间段有intervalSet来表示
6) multiIntervalSet方法
这个是MultiIntervalSet接口中的静态方法,功能为:根据一个IntervalSet中的数据来初始化一个MultiIntervalSet。
具体实现如下
7) minTime方法
在MultiIntervalSet的静态方法,其中功能为计算一个MultiIntervalSet中的时间的最小值,这个方法调用了IntervalSet中的静态方法。
具体实现如下:
8) maxTime方法
在MultiIntervalSet的静态方法,其中功能为计算一个MultiIntervalSet中的时间的最大值,这个方法调用了IntervalSet中的静态方法。
具体实现如下:
9) timeLength方法
这也是MultiIntervalSet中的静态方法,用于计算这个Set中所有时间段集合的长度之和。
具体实现如下:
3.3.2 局部共性特征的设计方案
同IntervalSet,我们在这一小节具体介绍CommonMultiIntervalSet中接口功能的具体实现。
1) 首先是数据定义
multiInterval用来表示标签对应的所有的时间段,用HashMap表示
之后是常量init = 0
之后两个long类型的数据来表示我们multiInterval的起始时间与结束时间
2) insert方法
- labels方法
3) remove方法
- intervals方法
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
对于三个应用场景的个性化设计,我使用的是CPR,通过接口组合实现局部共性特征的复用。
通过delegation机制,每个维度分别定义自己的接口,针对每个维度的不同特质取值,分别实现针对该维度接口的不同实现类,实现其特殊操作逻辑。
针对我们的问题,我们需要有三个接口组合来实现各应用的功能
我对于委托的实现如下:
通过定义四个接口,组合实现来完成功能
\1. NonOverlapIntervalSet 以及实现类NonOverlapIntervalSetImpl
这是在MultiIntervalSet中见检查在同一个时间段内是否有多个label占用的情况,这是接口的定义。
接下来是接这个接口类的实现
其中,需要传入一个MultiIntervalSet作为参数。
这是checkOverlap的具体实现,这就在这个接口中实现了一个检查是否有重复时间的功能
\2. NoBlankIntervalSet以及实现类NoBlankIntervalSetImpl
接口定义如下:
其功能是用来检测一个MultiIntervalSet中,在起始时间与结束时间范围内,是否有空闲的时间。
具体实现如下:
这是实现类的数据定义,构造函数,以及checkRep。其中对于检查是否有空白,我们需要传入一个MultiIntervalSet参数,以及开始时间start与结束时间end。
对于checkRep,我们需要保持RI,因此传入的开始时间必须小于结束时间,并且传入的set不为空。
这是checkNoBlank的具体实现。如果没有空闲,返回true;如果有空闲,返回false。
\3. PeriodicIntervalSet以及实现类PeriodicIntervalSetImpl
上图是接口的定义,其功能是对于一个时间周期来说,label的时间段具有周期性。
下图是接口的具体实现:
首先传入了一个MultiIntervalSet set,之后进行实现。
\4. RemainingTime以及实现类RemainingTimeImpl
下图实现接口的定义:
其中我们需要传入一个被计算的MultiIntervalSet set参数,由于需要计算剩余时间,因此在构造函数中需要传入开始时间与结束时间。
功能为:计算一个MultiIntervalSet的剩余时间比例
下面为具体实现:
返回剩余时间的ArrayList数组。
3.4 面向复用的设计:L
对于可复用的设计泛型L来说,在这三个应用场景中,代表着三个参数:Employee, Coures, Process。下面是这三个参数的具体定义:
\1. Employee:
Employee的参数有姓名,职位以及电话号码。下面是这个属性的get方法与equals,hashcode方法
\2. Process:
在Process中,基本属性有进程ID,进程名字,以及此进程的最短执行时间与最长执行时间。同时我们也在每个进程中维护了一个他在CPU上已经运行时间的长度:runningTime。用于判断这个进程是否能够结束。同时,这个进程管理用基于Swing的图形界面表示,因此在构造函数中需要随机生成一个颜色块,用来唯一的标识进程。这个随机生成颜色块的函数在Tools工具类中,在下面小节中我们会详细说明。
接下来就是这个Process的get方法,注意:runningTime需要设置set方法,这样我们才能随时更新这个进程的执行时间。
\3. Course:
在Course中,基本属性有课程ID,课程名字,授课老师姓名,授课地点,这节课的周学时数,这节课是否已经被安排。
注意:其中isArrange需要设置set方法,用来观察那些课程还没安排,这是课表应该具备的功能。
下面是一些属性的get,set,equals,hashcode方法
3.5 可复用API设计
因为我所有的APP都建立在MultiIntervalSet之上,因此我在可复用的API都写在了MultiIntervalSet接口的静态方法中,这样我们就直接可以通过类名调用这个复用的方法。
3.5.1 计算相似度
similarity方法
首先参数为两个MultiIntervalSet类型的参数为s1, s2。我们将计算的相似长度保存在similarityLength中,最后将相似长度除以总长度,返回的时候保留两位小数。
我们在计算MultiInterval的相似度时,调用了在IntervalSet接口中计算similarity的静态方法,下图是IntervalSet中的similarity方法:
其中参数为IntervalSet的s1, s2
3.5.2 计算时间冲突比例
calcConflictRatio方法:
其中冲突时间放在conflictTime中,返回的是保留两位小数的比例
3.5.3 计算空闲时间比例
calcFreeTimeRatio方法:
同冲突事件比例,我们将空闲时间放在freeTime中,在返回地时候返回保留两位小数的空闲比例。
3.6 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
首先我先要介绍一下我在这个项目所运用的工具类
我在工具类中定义了一些时间的转化,以及随机生成的一些函数。具体如下:
\1. StringToLong方法
将年-月-日类型的输入转换为long类型的变量
\2. LongToString方法
将long类型的变量转换为年-月-日类型的时间格式
\3. StringToLongHour方法
将年-月-日-小时-分钟的时间格式转换为long类型变量
\4. LongToStringHour方法
将时间类型为年-月-日-小时-分钟的时间格式转化为long类型
\5. StringToLongHourMin方法
将时间格式为小时-分钟的时间转化为long类型
\6. LongToStringToHourMin方法
将long 类型的变量转化为小时-分钟的时间格式
\7. randomColor方法
为每一个进程随机生成一个颜色块
\8. randomInt方法
随机生成min~max之间的int值
接下来就来看我们的APP都是如何实现的
3.6.1 排班管理系统
由于我们用委托的实现方法,而对于课表系统来说,不能出现时间重叠,不能出现空白时间段,同时需要算任意时刻的剩余时间。因此对于DutyRoster的接口定义来说,我们需要将IDutyRoster继承这三个接口:
NonOverlapIntervalSet, NoBlankIntervalSet, RemainingTime来实现接口的组合,以便于下面实现委托的功能。
接下来是我们排班表管理的具体的实现类DutyIntervalSet, 首先他要实现我们的排班表管理系统的接口,同时继承CommomMultiIntervalSet也就是我们上面实现的ADT,具体实现如下:
我们需要将功能委托给我们上文中单独实现的功能,因此我们需要定义委托类的对象。同时也需要传入排班表的起始时间与结束时间。构造函数如下:
下面是委托的过程:
对于insert方法来说,我们需要对于每次插入之后都要检查是否存在时间重叠的现象。因此,我们对于insert方法,功能是不变的,只需调用父类的insert方法:super.insert。而在插入完成之后,我们利用我们委托的功能:即nOverlap.checkOverlap(封装在了checkOverlap中)。对于空白时间段和剩余时间段的检查,我们根据用户的选择来决定是否检查,因此我们将它封装为checkNoBlank与checkRemainingTime来依据用户的需要来调用。实现如下:
到这里,我们已经实现了排班管理的底层实现。之后我们实现对用户的交互以及自动排版的功能。
对于排班管理的APP,我的包结构如下:
其中AutoScheduling为自动排班功能;DutyRosterApp为APP的主体框架,包括在命令行与用户的交互;FileParse为对于文件的正则表达式解析,我们在下面会详细的讲到,在这里略过;ShowDutyRoster为对排班表的格式化表示,下面我们详细的说明APP的具体实现:
\1. DutyRosterApp
首先是对是否从文件中读取排班信息的判断。如果自动读入,则用正则解析文件,自动插入并且输出排班表,排版过程直接结束。
如果不选择从文件中读取,则手动输入排班的开始日期与截止日期,并且新建一个DutyIntervalSet对象(我们上面刚刚讲过的ADT)。
接下来是对员工信息的输入
在员工信息输入完成之后,用户可以选择自动排班或者自己手动排班
如果进行手动排班的话,需要选择特定的员工并且选择特定的时间。在插入过程中随时可以检查当前排班是否已满并且会输入未安排的时间比例。如果排班未满,会让用户继续排班直到排班表满。
这些就是APP的主体内容,接下来我们看具体功能的实现
\2. AutoScheduling
对于自动排班功能,我实现的情况大致为:我们可知可排班的总天数为allDay,其中员工个数为num个。我们可得 allDay / num = averageDay
allDay % num = remainDay。我们的算法为,对于每个员工先排班averageDay天,其中连续非连续是随机的。然后判断remainDay是否为0,如果为0结束排班,如果不为0则随机给一名员工进行剩余天数的排班。
具体实现如下:
\3. ShowDutyRoster
这个类就是用来格式化输入排班表的,不用多说,具体实现如下:
3.6.2 操作系统的进程调度管理系统
首先我的进程调度系统包结构如下:
\1. ProcessScheduleApp
首先是对我们要操作进程的初始化,实现如下:
接下来是对模拟调度方案的选择,其中1为随机模拟调度,2为最短进程优先调度
调度结束之后,用Swing进行图形的输出
\2. ProcessFrameRandom
这个类是对于Swing来说随机进程调度的框架。我的算法如下,首先我先生成一个长度为0~100的随机时间片,之后让一个进程随机执行这个时间片。如果在时间片内到达了它的最大执行时间,则只执行到最大执行时间,剩余时间CPU处于空闲状态。具体实现如下:
\3. ProcessFrameShortestFirst
这个类是对于Swing来说优先进行最短进程的框架实现,具体要求在PDF中已经说明的很清楚,这里具体实现如下:
3.6.3 课表管理系统
我的课表管理系统包结构如下图:
只有一个用户交互界面的App,具体实现如下:
首先定义课程开始的时间,每分钟、每小时、每天、每周的毫秒数,以及每节课的开始时间,方便功能实现
首先是对学期开始日期,总周数的输入
之后是对我们想要进行安排课程的初始化
接下来是手动选择课程,对其进行课表排列
之后进行对空闲时间和重复时间比例的检测
最后实现学期内任意一天课表的查看
3.7 基于语法的数据读入
对于正则表达式的语法输入,我用FileParse来进行文件的正则表达式解析,具体分析如下:
首先,我们先寻找Period字段,先将DutyRoster进行初始化,之后将Employee中的员工信息解析出来并且初始化员工列表。之后对Roster进行解析,将每个员工的排班信息插入DutyRoster中。
解析Period:
解析Employee:
解析Roster:
最后将值班表打印出来:
3.8 测试用例汇总
以下是整个应用的测试用例,下图为测试文件包结构:
\1. CommonIntervalSetTest
这是对IntervalSet实现类中方法的测试,具体实现如下图:
getStartTime方法:
getEndTime方法:
getStart方法:
getEnd方法:
getInterval方法:
insert方法:
labels方法:
remove方法:
NumLabels方法:
toString方法:
\2. CommonMultiIntervalSetTest
这是MultiIntervalSet的测试方法,具体如下:
getStartTime方法
getEndTime方法:
getInterval方法:
insert方法:
labels方法:
remove方法:
\3. IntervalSetInstanceTest
其中就为Common中方法变形,在这里不过多赘述
\4. MultiIntervalSetInstanceTest
其中就为Common中方法变形,在这里不过多赘述
\5. IntervalSetStaticTest
其中为IntervalSet接口中的静态方法测试,具体实现如下:
empty方法:
overlap方法:
minTime方法:
maxTime方法:
timeLength方法:
similarityLength方法:
\6. MultiIntervalSetStaticTest
其中为MultiIntervalSet接口中的静态方法,下面为具体实现:
empty方法:
multiIntervalSet方法:
similarity方法:
calcConflictRatio方法:
calcFreeTimeRatio方法:
3.9 应对面临的新变化
3.9.1 变化1
由于我的DutyRoster的底层结构就是MultiIntervalSet,因此本来可以出现一个员工被安排多段值班的情况,因此无需改变。
3.9.2 变化2
第二个情况下,我们再选课表中再接口中继承NoOverlapIntervalSet,之后委托这个接口的功能来实现两个课不能排在同一时间的情况,代价很小。
3.10 Git仓库结构
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
---|---|---|---|
6.15 | 19:00~22:00 | 理解实验意思,确定设计方案 | 失败 |
6.16 | 10:00~11:00 | 继续确定设计方案 | 成功 |
6.16 | 20:00~23:00 | 搭好底部ADT的框架,并写好delegation | 成功 |
6.17 | 20:00~22:00 | 开发DutyRoster的基本功能 | 成功 |
6.25 | 19: 00~21:00 | 学习java正则表达式解析,并且实现功能 | 成功 |
6.26 | 19:00~21:00 | 开始开发进程管理系统 | 失败 |
6.27 | 10:00~12:00 | 学习Java Swing 为了开发进程图形界面 | 成功 |
6.27 | 15:00~18:00 | 继续开发进程管理系统 | 成功 |
6.28 | 19:00~21:00 | 开发课表管理系统 | 成功 |
6.29 | 21:00~23:00 | 对于新的改变的编写 | 成功 |
6.30 | 19:00~21:00 | 对于测试程序的编写 | 成功 |
5 实验过程中遇到的困难与解决途径
遇到的难点 | 解决途径 |
---|---|
Java Swing的学习 | 学的时间长就好了 |
集合框架不熟悉 | 继续学 |
正则表达式不会写 | 继续学 |
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
真的学到了许多,不仅是Java语言的代码能力,我感觉更多的是自学能力的提高。
6.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在五个不同的应用场景下使用,你是否体会到复用的好处?
答:感觉面向场景化的变成需要先考虑我们需要用什么样的ADT,之后再对ADT进行编程。确实体会到了,如果不复用的话会导致有很多的重复代码会让程序变得很臃肿,更要命的是如果要改里面的东西,会特别的麻烦。
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
答:我认为这样既会让程序员写的程序变得安全合理,也会让用户的使用体验变得更好。我会坚持这样做的。
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
答:乐趣就是可以按照自己的想法进行创造力的开发,可以做到真正欸但掌握这个程序,有点改造世界的意思了。但是同样的,难处就是你的技术可能会限制你想象性的开发,如果你想达到你预期的目的,你就要去学更多的东西。
(4) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
答:我认为还是挺复杂的,尤其是这学期学了形式语言与自动机这门课程之后,我们写的编译驱动还是过于简陋了。
(5) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过五周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
答:主要是开始的时候没有想法,无从下手。之后慢慢捋清,认真阅读实验要求,最后慢慢写出来的。
(6) “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的五个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?
答:需要考虑多种情况
(7) 关于本实验的工作量、难度、deadline。
答: 工作量:很大
难度:中等
Deadline: 正常
(8) 到目前为止你对《软件构造》课程的评价。
一门真正有用的课
**2021年春季学期
** 计算学部《软件构造》课程
**Lab 3实验报告
**
姓名 | 张景阳 |
---|---|
学号 | 1190200610 |
班号 | 1903004 |
电子邮件 | 1224581500@qq.com |
手机号码 | 15945984760 |
目录
3.2 面向可复用性和可维护性的设计:IntervalSet· 1
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)··· 2
3.3 面向可复用性和可维护性的设计:MultiIntervalSet· 2
3.3.1 MultiIntervalSet的共性操作··· 2
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)··· 2
1 实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性 的软件,主要使用以下软件构造技术: l 子类型、泛型、多态、重写、重载 l 继承、代理、组合 l 语法驱动的编程、正则表达式 l API 设计、API 复用 本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学 课表管理),学生不是直接针对每个应用分别编程实现,而是通过 ADT 和泛型等 抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性 和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)
2 实验环境配置
\1. 首先下载IDEA
\2. 在IDEA setting中的下载最新版本的Git,并于IDEA连接上
\3. 在IDEA中绑定你的Github帐号
\4. 通过VCS建立本地仓库,并Commit and Push
\5. 通过Git将本地代码上传到实验仓库中(根据Url)
\6. 进入Travis中,绑定你的Github帐号,并将实验所用的Private库授权
\7. 在项目中添加一个.yml文件,并且在.pom写下配置文件,用于配置编译
\8. 如果Git不上去的话,在命令行中git init之后,强行push上去,git push origin master -f 输入你的Github帐号密码即可Push上去
在这里给出你的GitHub Lab3仓库的URL地址。
Lab3: https://github.com/ComputerScienceHIT/lab3-1190200610.git
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
简要介绍三个应用:
第一个应用场景为值班表管理::一个单位有 n 个员工,在某个时间段内(例 如寒假 1 月 10 日到 3 月 6 日期间),每天只能安排唯一一个员工在单位 值班,且不能出现某天无人值班的情况;每个员工若被安排值班 m 天 (m>1),那么需要安排在连续的 m 天内。值班表内需要记录员工的名 字、职位、手机号码,以便于外界联系值班员。
我实现的界面大致如下:
第二个应用为操作系统进程调度::考虑计算机上有一个单核 CPU,多个进程被操作系统创建出来,它们被调度在 CPU 上执行,由操 作系统决定在各个时段内执行哪个线程。操作系统可挂起某个正在执行 的进程,在后续时刻可以恢复执行被挂起的进程。可知:每个时间只能 有一个进程在执行,其他进程处于休眠状态;一个进程的执行被分为多 个时间段;在特定时刻,CPU 可以“闲置”,意即操作系统没有调度执行 任何进程;操作系统对进程的调度无规律,可看作是随机调度。
我实现的情况大致如下:
第三个应用场景为课表管理:看一下你自己的课表,每一上午 10:00-12:00 和每周三上午 8:00-10:00 在正心楼 42 教室上“软件构造”课 时间轴 同一颜色的矩形表示同一个进程 Lab Manuals Lab-3 Reusability and Maintainability oriented Software Construction 程。课程需要特定的教室和特定的教师。在本应用中,我们对实际的课 表进行简化:针对某个班级,假设其各周的课表都是完全一样的(意即 同样的课程安排将以“周”为单位进行周期性的重复,直到学期结束); 一门课程每周可以出现 1 次,也可以安排多次(例如每周一和周三的“软 件构造课”)且由同一位教师承担并在同样的教室进行;允许课表中有空 白时间段(未安排任何课程);考虑到不同学生的选课情况不同,同一个 时间段内可以安排不同的课程(例如周一上午 3-4 节的计算方法和软件 构造);一位教师也可以承担课表中的多门课程。
我实现的大致如下:
共性:每个场景的底层数据结构都是我们实现的MultiIntervalSet,其中的插入操作,删除操作都是相同的。而对于构造的思想,都是基于MultiIntervalSet
差异:在值班表管理中,对于同一时间段不能有多个人在同时值班,因此我们需要用委托的思想来运用NonOverlapIntervalSetImpl的接口实现,来完成在同一个时间段不能有多个人值班。同时,在值班表的应用当中,在值班范围内不能有空白的时间段。因此,我们也用委托的思想,运用NoBlankIntervalSetImpl的接口实现,来实现不能存在空闲时间段的性质。
在操作系统进程调度管理中,再次应用中可以出现空白,但是CPU在一个时间段只可以运行一个进程。因此,同课程表应用,因此我们需要用委托的思想来运用NonOverlapIntervalSetImpl的接口实现,来完成在同一个时间段不能有多个进程同时运行。
在课表管理中,我们需要有周期性的性质。因此,我们需要运用PeriodicIntervalSetImpl委托实现这个周期性的性质。
3.2 面向可复用性和可维护性的设计:IntervalSet
3.2.1 IntervalSet的共性操作
\1. IntervalSet 接口实现
我们首先定义IntervalSet的接口,来从比较高层次的方面来定义IntervalSet的功能。
1) empty方法
返回一个空的intervalSet对象
2) insert方法
向IntervalSet中插入一个以类型为L的label变量,起始时间为start,终止时间为end的时间段。
3)labels方法
返回HashMap中时间段的标签集合
4)remove方法
在IntervalSet集合中删除标签为label的集合
5) getInterval方法
返回Interval中的HashMap集合,也就是时间段的集合
6) NumLabels方法
返回IntervalSet中的标签数量
7) getStart方法
获得label标签的开始时间
8) getEnd方法
获得label标签的结束时间
hashcode and equals方法
用来表达对interval类型变量的相等判断
接下来是Interval接口中的静态方法,对于每个ADT调用时可能用到的
1) overlap方法
对于两个IntervalSet,来判断两个intervalSet中时间是否有重合的区域,如果没有返回false,如果有返回true。
2) minTime方法
计算IntervalSet s中的最短的时间,并返回
3) maxTime方法
计算IntervalSet s中的最长的时间,并返回
4) timeLength方法
计算一个IntervalSet集合中时间段的总长度
5) similarityLength方法
计算两个IntervalSet中重合时间段的长度
3.2.2 局部共性特征的设计方案
我们介绍完了IntervalSet接口中的定义,我们具体来看实现类CommonIntevalSet中的具体实现
首先是定义
1) 其中HaspMap<L, ArrayList> interval 用来表示一个标签对应的时间段的集合。其中两个常量startIndex,endIndex表示ArrayList中的数组索引,因此ArrayList[0]来保存开始时间,ArrayList[1]来保存结束时间。后面取得时候就用我们的两个常量来取。
2) 下两个long类型的数据是我们的参数,用来保存传入的时间段的开始时间与结束时间。
3) 接下来是checkRep,来保证ADT的RI
其中,保证每个时间段的结束时间都大于等于开始时间。
接下来是就是对于接口功能的依次实现
4) insert方法
5) labels方法
6) remove方法
7) NumLabels方法
8) getStart方法
9) getEnd方法
10) equals与hashcode方法
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
我的应用都是继承于MultiIntervalSet而设计的,而MultiIntervalSet又是基于IntervalSet设计的。对于我的基于委托的设计方法,我在MultiIntervalSet中会继续重点分析,在这里不过多赘述。
3.3 面向可复用性和可维护性的设计:MultiIntervalSet
3.3.1 MultiIntervalSet的共性操作
同Interval我们在这节也将分析MultiInterval接口中定义的操作
1) empty方法
返回一个空的CommonMultiIntervalSet对象
2) insert方法
向MultiIntervalSet中插入一个以类型为L的label变量,起始时间为start,终止时间为end的时间段。
3) labels方法
返回MultiIntervalSet中的所有标签
4) remove方法
在MultiIntervalSet中除去label标签
5) intervals方法
返回一个ArrayList数组,其中包含了标签label的所有时间段,其中每个时间段有intervalSet来表示
6) multiIntervalSet方法
这个是MultiIntervalSet接口中的静态方法,功能为:根据一个IntervalSet中的数据来初始化一个MultiIntervalSet。
具体实现如下
7) minTime方法
在MultiIntervalSet的静态方法,其中功能为计算一个MultiIntervalSet中的时间的最小值,这个方法调用了IntervalSet中的静态方法。
具体实现如下:
8) maxTime方法
在MultiIntervalSet的静态方法,其中功能为计算一个MultiIntervalSet中的时间的最大值,这个方法调用了IntervalSet中的静态方法。
具体实现如下:
9) timeLength方法
这也是MultiIntervalSet中的静态方法,用于计算这个Set中所有时间段集合的长度之和。
具体实现如下:
3.3.2 局部共性特征的设计方案
同IntervalSet,我们在这一小节具体介绍CommonMultiIntervalSet中接口功能的具体实现。
1) 首先是数据定义
multiInterval用来表示标签对应的所有的时间段,用HashMap表示
之后是常量init = 0
之后两个long类型的数据来表示我们multiInterval的起始时间与结束时间
2) insert方法
- labels方法
3) remove方法
- intervals方法
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
对于三个应用场景的个性化设计,我使用的是CPR,通过接口组合实现局部共性特征的复用。
通过delegation机制,每个维度分别定义自己的接口,针对每个维度的不同特质取值,分别实现针对该维度接口的不同实现类,实现其特殊操作逻辑。
针对我们的问题,我们需要有三个接口组合来实现各应用的功能
我对于委托的实现如下:
通过定义四个接口,组合实现来完成功能
\1. NonOverlapIntervalSet 以及实现类NonOverlapIntervalSetImpl
这是在MultiIntervalSet中见检查在同一个时间段内是否有多个label占用的情况,这是接口的定义。
接下来是接这个接口类的实现
其中,需要传入一个MultiIntervalSet作为参数。
这是checkOverlap的具体实现,这就在这个接口中实现了一个检查是否有重复时间的功能
\2. NoBlankIntervalSet以及实现类NoBlankIntervalSetImpl
接口定义如下:
其功能是用来检测一个MultiIntervalSet中,在起始时间与结束时间范围内,是否有空闲的时间。
具体实现如下:
这是实现类的数据定义,构造函数,以及checkRep。其中对于检查是否有空白,我们需要传入一个MultiIntervalSet参数,以及开始时间start与结束时间end。
对于checkRep,我们需要保持RI,因此传入的开始时间必须小于结束时间,并且传入的set不为空。
这是checkNoBlank的具体实现。如果没有空闲,返回true;如果有空闲,返回false。
\3. PeriodicIntervalSet以及实现类PeriodicIntervalSetImpl
上图是接口的定义,其功能是对于一个时间周期来说,label的时间段具有周期性。
下图是接口的具体实现:
首先传入了一个MultiIntervalSet set,之后进行实现。
\4. RemainingTime以及实现类RemainingTimeImpl
下图实现接口的定义:
其中我们需要传入一个被计算的MultiIntervalSet set参数,由于需要计算剩余时间,因此在构造函数中需要传入开始时间与结束时间。
功能为:计算一个MultiIntervalSet的剩余时间比例
下面为具体实现:
返回剩余时间的ArrayList数组。
3.4 面向复用的设计:L
对于可复用的设计泛型L来说,在这三个应用场景中,代表着三个参数:Employee, Coures, Process。下面是这三个参数的具体定义:
\1. Employee:
Employee的参数有姓名,职位以及电话号码。下面是这个属性的get方法与equals,hashcode方法
\2. Process:
在Process中,基本属性有进程ID,进程名字,以及此进程的最短执行时间与最长执行时间。同时我们也在每个进程中维护了一个他在CPU上已经运行时间的长度:runningTime。用于判断这个进程是否能够结束。同时,这个进程管理用基于Swing的图形界面表示,因此在构造函数中需要随机生成一个颜色块,用来唯一的标识进程。这个随机生成颜色块的函数在Tools工具类中,在下面小节中我们会详细说明。
接下来就是这个Process的get方法,注意:runningTime需要设置set方法,这样我们才能随时更新这个进程的执行时间。
\3. Course:
在Course中,基本属性有课程ID,课程名字,授课老师姓名,授课地点,这节课的周学时数,这节课是否已经被安排。
注意:其中isArrange需要设置set方法,用来观察那些课程还没安排,这是课表应该具备的功能。
下面是一些属性的get,set,equals,hashcode方法
3.5 可复用API设计
因为我所有的APP都建立在MultiIntervalSet之上,因此我在可复用的API都写在了MultiIntervalSet接口的静态方法中,这样我们就直接可以通过类名调用这个复用的方法。
3.5.1 计算相似度
similarity方法
首先参数为两个MultiIntervalSet类型的参数为s1, s2。我们将计算的相似长度保存在similarityLength中,最后将相似长度除以总长度,返回的时候保留两位小数。
我们在计算MultiInterval的相似度时,调用了在IntervalSet接口中计算similarity的静态方法,下图是IntervalSet中的similarity方法:
其中参数为IntervalSet的s1, s2
3.5.2 计算时间冲突比例
calcConflictRatio方法:
其中冲突时间放在conflictTime中,返回的是保留两位小数的比例
3.5.3 计算空闲时间比例
calcFreeTimeRatio方法:
同冲突事件比例,我们将空闲时间放在freeTime中,在返回地时候返回保留两位小数的空闲比例。
3.6 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
首先我先要介绍一下我在这个项目所运用的工具类
我在工具类中定义了一些时间的转化,以及随机生成的一些函数。具体如下:
\1. StringToLong方法
将年-月-日类型的输入转换为long类型的变量
\2. LongToString方法
将long类型的变量转换为年-月-日类型的时间格式
\3. StringToLongHour方法
将年-月-日-小时-分钟的时间格式转换为long类型变量
\4. LongToStringHour方法
将时间类型为年-月-日-小时-分钟的时间格式转化为long类型
\5. StringToLongHourMin方法
将时间格式为小时-分钟的时间转化为long类型
\6. LongToStringToHourMin方法
将long 类型的变量转化为小时-分钟的时间格式
\7. randomColor方法
为每一个进程随机生成一个颜色块
\8. randomInt方法
随机生成min~max之间的int值
接下来就来看我们的APP都是如何实现的
3.6.1 排班管理系统
由于我们用委托的实现方法,而对于课表系统来说,不能出现时间重叠,不能出现空白时间段,同时需要算任意时刻的剩余时间。因此对于DutyRoster的接口定义来说,我们需要将IDutyRoster继承这三个接口:
NonOverlapIntervalSet, NoBlankIntervalSet, RemainingTime来实现接口的组合,以便于下面实现委托的功能。
接下来是我们排班表管理的具体的实现类DutyIntervalSet, 首先他要实现我们的排班表管理系统的接口,同时继承CommomMultiIntervalSet也就是我们上面实现的ADT,具体实现如下:
我们需要将功能委托给我们上文中单独实现的功能,因此我们需要定义委托类的对象。同时也需要传入排班表的起始时间与结束时间。构造函数如下:
下面是委托的过程:
对于insert方法来说,我们需要对于每次插入之后都要检查是否存在时间重叠的现象。因此,我们对于insert方法,功能是不变的,只需调用父类的insert方法:super.insert。而在插入完成之后,我们利用我们委托的功能:即nOverlap.checkOverlap(封装在了checkOverlap中)。对于空白时间段和剩余时间段的检查,我们根据用户的选择来决定是否检查,因此我们将它封装为checkNoBlank与checkRemainingTime来依据用户的需要来调用。实现如下:
到这里,我们已经实现了排班管理的底层实现。之后我们实现对用户的交互以及自动排版的功能。
对于排班管理的APP,我的包结构如下:
其中AutoScheduling为自动排班功能;DutyRosterApp为APP的主体框架,包括在命令行与用户的交互;FileParse为对于文件的正则表达式解析,我们在下面会详细的讲到,在这里略过;ShowDutyRoster为对排班表的格式化表示,下面我们详细的说明APP的具体实现:
\1. DutyRosterApp
首先是对是否从文件中读取排班信息的判断。如果自动读入,则用正则解析文件,自动插入并且输出排班表,排版过程直接结束。
如果不选择从文件中读取,则手动输入排班的开始日期与截止日期,并且新建一个DutyIntervalSet对象(我们上面刚刚讲过的ADT)。
接下来是对员工信息的输入
在员工信息输入完成之后,用户可以选择自动排班或者自己手动排班
如果进行手动排班的话,需要选择特定的员工并且选择特定的时间。在插入过程中随时可以检查当前排班是否已满并且会输入未安排的时间比例。如果排班未满,会让用户继续排班直到排班表满。
这些就是APP的主体内容,接下来我们看具体功能的实现
\2. AutoScheduling
对于自动排班功能,我实现的情况大致为:我们可知可排班的总天数为allDay,其中员工个数为num个。我们可得 allDay / num = averageDay
allDay % num = remainDay。我们的算法为,对于每个员工先排班averageDay天,其中连续非连续是随机的。然后判断remainDay是否为0,如果为0结束排班,如果不为0则随机给一名员工进行剩余天数的排班。
具体实现如下:
\3. ShowDutyRoster
这个类就是用来格式化输入排班表的,不用多说,具体实现如下:
3.6.2 操作系统的进程调度管理系统
首先我的进程调度系统包结构如下:
\1. ProcessScheduleApp
首先是对我们要操作进程的初始化,实现如下:
接下来是对模拟调度方案的选择,其中1为随机模拟调度,2为最短进程优先调度
调度结束之后,用Swing进行图形的输出
\2. ProcessFrameRandom
这个类是对于Swing来说随机进程调度的框架。我的算法如下,首先我先生成一个长度为0~100的随机时间片,之后让一个进程随机执行这个时间片。如果在时间片内到达了它的最大执行时间,则只执行到最大执行时间,剩余时间CPU处于空闲状态。具体实现如下:
\3. ProcessFrameShortestFirst
这个类是对于Swing来说优先进行最短进程的框架实现,具体要求在PDF中已经说明的很清楚,这里具体实现如下:
3.6.3 课表管理系统
我的课表管理系统包结构如下图:
只有一个用户交互界面的App,具体实现如下:
首先定义课程开始的时间,每分钟、每小时、每天、每周的毫秒数,以及每节课的开始时间,方便功能实现
首先是对学期开始日期,总周数的输入
之后是对我们想要进行安排课程的初始化
接下来是手动选择课程,对其进行课表排列
之后进行对空闲时间和重复时间比例的检测
最后实现学期内任意一天课表的查看
3.7 基于语法的数据读入
对于正则表达式的语法输入,我用FileParse来进行文件的正则表达式解析,具体分析如下:
首先,我们先寻找Period字段,先将DutyRoster进行初始化,之后将Employee中的员工信息解析出来并且初始化员工列表。之后对Roster进行解析,将每个员工的排班信息插入DutyRoster中。
解析Period:
解析Employee:
解析Roster:
最后将值班表打印出来:
3.8 测试用例汇总
以下是整个应用的测试用例,下图为测试文件包结构:
\1. CommonIntervalSetTest
这是对IntervalSet实现类中方法的测试,具体实现如下图:
getStartTime方法:
getEndTime方法:
getStart方法:
getEnd方法:
getInterval方法:
insert方法:
labels方法:
remove方法:
NumLabels方法:
toString方法:
\2. CommonMultiIntervalSetTest
这是MultiIntervalSet的测试方法,具体如下:
getStartTime方法
getEndTime方法:
getInterval方法:
insert方法:
labels方法:
remove方法:
\3. IntervalSetInstanceTest
其中就为Common中方法变形,在这里不过多赘述
\4. MultiIntervalSetInstanceTest
其中就为Common中方法变形,在这里不过多赘述
\5. IntervalSetStaticTest
其中为IntervalSet接口中的静态方法测试,具体实现如下:
empty方法:
overlap方法:
minTime方法:
maxTime方法:
timeLength方法:
similarityLength方法:
\6. MultiIntervalSetStaticTest
其中为MultiIntervalSet接口中的静态方法,下面为具体实现:
empty方法:
multiIntervalSet方法:
similarity方法:
calcConflictRatio方法:
calcFreeTimeRatio方法:
3.9 应对面临的新变化
3.9.1 变化1
由于我的DutyRoster的底层结构就是MultiIntervalSet,因此本来可以出现一个员工被安排多段值班的情况,因此无需改变。
3.9.2 变化2
第二个情况下,我们再选课表中再接口中继承NoOverlapIntervalSet,之后委托这个接口的功能来实现两个课不能排在同一时间的情况,代价很小。
3.10 Git仓库结构
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
---|---|---|---|
6.15 | 19:00~22:00 | 理解实验意思,确定设计方案 | 失败 |
6.16 | 10:00~11:00 | 继续确定设计方案 | 成功 |
6.16 | 20:00~23:00 | 搭好底部ADT的框架,并写好delegation | 成功 |
6.17 | 20:00~22:00 | 开发DutyRoster的基本功能 | 成功 |
6.25 | 19: 00~21:00 | 学习java正则表达式解析,并且实现功能 | 成功 |
6.26 | 19:00~21:00 | 开始开发进程管理系统 | 失败 |
6.27 | 10:00~12:00 | 学习Java Swing 为了开发进程图形界面 | 成功 |
6.27 | 15:00~18:00 | 继续开发进程管理系统 | 成功 |
6.28 | 19:00~21:00 | 开发课表管理系统 | 成功 |
6.29 | 21:00~23:00 | 对于新的改变的编写 | 成功 |
6.30 | 19:00~21:00 | 对于测试程序的编写 | 成功 |
5 实验过程中遇到的困难与解决途径
遇到的难点 | 解决途径 |
---|---|
Java Swing的学习 | 学的时间长就好了 |
集合框架不熟悉 | 继续学 |
正则表达式不会写 | 继续学 |
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
真的学到了许多,不仅是Java语言的代码能力,我感觉更多的是自学能力的提高。
6.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在五个不同的应用场景下使用,你是否体会到复用的好处?
答:感觉面向场景化的变成需要先考虑我们需要用什么样的ADT,之后再对ADT进行编程。确实体会到了,如果不复用的话会导致有很多的重复代码会让程序变得很臃肿,更要命的是如果要改里面的东西,会特别的麻烦。
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
答:我认为这样既会让程序员写的程序变得安全合理,也会让用户的使用体验变得更好。我会坚持这样做的。
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
答:乐趣就是可以按照自己的想法进行创造力的开发,可以做到真正欸但掌握这个程序,有点改造世界的意思了。但是同样的,难处就是你的技术可能会限制你想象性的开发,如果你想达到你预期的目的,你就要去学更多的东西。
(4) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
答:我认为还是挺复杂的,尤其是这学期学了形式语言与自动机这门课程之后,我们写的编译驱动还是过于简陋了。
(5) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过五周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
答:主要是开始的时候没有想法,无从下手。之后慢慢捋清,认真阅读实验要求,最后慢慢写出来的。
(6) “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的五个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?
答:需要考虑多种情况
(7) 关于本实验的工作量、难度、deadline。
答: 工作量:很大
难度:中等
Deadline: 正常
(8) 到目前为止你对《软件构造》课程的评价。
一门真正有用的课