2022年春季学期
计算学部《软件构造》课程
lab3实验心得
目录
3.2.5 任务5:投票活动Poll的实现类GeneralPollImpl
3.3.2 任务8:采用Strategy设计模式实现灵活的计票规则
3.3.3 任务9:采用Strategy设计模式实现灵活的遴选规则
3.3.4 任务10:处理匿名和实名投票
3.3.5 任务11:采用Visitor设计模式实现功能扩展
3.5.3 聚餐点菜应用:取消权重设置、只计算“喜欢”的票数
1 实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性 的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、委派、CRP
语法驱动的编程、正则表达式
设计模式
本次实验给定了多个具体应用,学生不是直接针对每个应用分别编程实现, 而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充分考虑 这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)和更 容易面向各种变化(可维护性)。
2 实验环境配置
Eclipse IDE for java Developers-2022-03
Java SE 11.0.14
GitHub Desktop
EclEmma Java Code Coverage 3.1.3
由于使用了Eclipse,EclEmma下载安装非常方便,只要在Eclipse Market Place中下载就可以了。
3 实验过程
3.1 待开发的三个应用场景
应用一:商业表决(BusinessVoting):面向一个商业公司进行设计,对于一个商业公司的提案表决,公司的各位董事将进行提案表决,权重为各个董事的持股比例,若支持票超过2/3,则提案通过,否则不通过。
应用二:代表选举(Election):针对一次社会活动进行选举投票在该选举中,提前确定一部 分候选人,投票人从已确定的候选人中选取,不可提名新的候选人。计划选出的代表数量k是提前确定的。投票人针对每个候选人匿名选择“支持、反对、弃权” 之一,但选择“支持”的人数不能高于计划选出的代表数量,否则为非法票。所有投票人的权重均相等。全体投票人投票之后,首先判定各张选票的合法性,在去除非法选票之后,针对所有候选人,根据其所得到的支持票数量排序,前k个候选人当选;若有多个候选人的支持票数量相等而无法自然排出前,名,则仅有那些明确可进入前k名的人当选。
应用三:聚餐点菜(Dinnerorder): 一群人去餐馆就餐,需要从该餐馆提供的菜 单中选择若干道菜,点菜的数量要大于等于就餐总人数,且小于总人数+5。每个 人针对菜单上的每一道菜实名表达自己的喜好(喜欢、不喜欢、无所谓),选择 这三个选项的数目无限制, 所有人表达观点之后,根据影响力加权计票(喜欢、不喜欢、无所谓分别得分 2、0、1),取总得分最高的前,道菜。若因为有多道菜得分相等而无法自然排出前,名,则除 了那些明确可进入前k名的菜之外,在其他得分相等的菜中随机选取一部分,凑足k个菜。
3.2 ADT识别与设计
3.2.1 任务1:投票类型VoteType
在构造函数中,仅含有一个int类型的参数,函数原型如下
public VoteType(int type)
type=1对应election类型,type=0对应order类型
checkLegality和getscore如下
3.2.2 任务2:投票项VoteItem<C>
Voteitem包括两个部分,candidate和value,可以简单的在get函数中返回这两个属性。
3.2.3 任务3:选票Vote
创建选票对象需要一个VoteItem的集合,将它拷贝为参数并且获取投票时间即可
3.2.4 任务4:投票活动Poll<C>的测试
这里测试了get函数,在之前的提交中,这个get函数曾用于返回一些private类型的数据来debug。
3.2.5 任务5:投票活动Poll<C>的实现类GeneralPollImpl
这里,将大多数private的属性都修改成protected,便于三个子类使用
3.2.6 任务6:投票活动Poll<C>的子类型
Businessvoting:这个类中需要重写Addvote,checklegal,result,增加checkfinish
对于Addvote:传入的是一个vote类型的参数,实际是实名投票,需要转换成Realnamevote类型,检查合法性后添加
对于Checklegal:需要检查的是投票人是否已经有过票,投票的票数和candidate是否大小一致,以及每一票投的人是不是被选举人,通过这两点就可以判断投票是否覆盖到所有选举人(此处和Election一致,后续不赘述),即(|A|=|B|,a∈A→a∈B)→A=B。
对于Result:不同的子类实现不同的result,利用stringbuilder去构造结果
DinnerOrder:同样要重写上述的几个函数,实现方式和上边区别不大,这里简述Checkfinish函数:实际上checkfinish和判断投票合法性有相似之处,即检查已投票人集合和投票人集合是否一致。
Election: Election比起之前的子类,多出了一个限制,即每个人最多投K票支持票,这里注意不建议用Overlode的方式修改setinfo,而是仍然Override即可
3.3 ADT行为的设计与实现
3.3.1任务7:合法性检查
合法性检查在前文已经描述,在GeneralPollImpl中加入了一个新的集合:Legalvotes,用于保存那些合法的选票。
在addvote中,检查合法性后合法的选票加入其中
3.3.2 任务8:采用Strategy设计模式实现灵活的计票规则
Interface如下,这里只需要传入一个子类即可,GeneralPollImpl中的statistics如下
这里以Election的计票子类为例
只需要遍历每一张合法票,将其中的结果加入result中就可以了
3.3.3 任务9:采用Strategy设计模式实现灵活的遴选规则
和上边的大同小异,这里以dinner的筛选为例
排序部分定义了一个selfcomp类,借用他重写compare,实现排序和输出,对于Election,还包括一个去除相同票数的特殊规则,这个规则可以这样实现:
3.3.4 任务10:处理匿名和实名投票
此任务在任务三后即可完成,Realnamevote继承于vote,如下
3.3.5 任务11:采用Visitor设计模式实现功能扩展
由于修改了GeneralPollImpl的规约,Visitor所实现的功能非常容易解决,Legalvotes.Size即为有效票的数量,接口以及实现如下
3.3.6 任务12:基于语法的数据读入
读入后,使用split分割,并且使用正则表达式匹配,这里有两个表达式,需要注意的是两个表达式不能混合匹配,其中一个表达式匹配如图
3.4 任务13:应用设计与开发
3.4.1 商业表决系统
使用实验手册上的样例,结果为没有通过
3.4.2 代表选举系统
这里自定义了一个样例,这是因为手册上的样例不正确,3号投票不合规导致计票结果和手册上不同,最终AB当选
3.4.3 聚餐点菜系统
使用手册上的样例
3.5 任务14:应对面临的新变化
3.5.1 商业表决应用:可以一次表决多个商业提案
之前的设计完全可以应对变化,在判断选票时,之前的设计我将规约改成了只能投一票,这里简单的改成candidates.Size即可,测试结果如下
3.5.2 代表选举应用:遴选规则变化
这个规则的变化较为难以实现,但可以在计票过程做文章,一张反对票多扣除0.01分,就可以实现将反对票一致的人排在一起,类似于桶排序的思想。如果投票的人多,可以改为0.001这种更小的数,问题得以解决
3.5.3 聚餐点菜应用:取消权重设置、只计算“喜欢”的票数
之前的设计显然是更强的,只要类似于Election,去掉权重,并且将不喜欢和无所谓都设置为0就可以了