hit软件构造lab3

2022年春季学期
计算学部《软件构造》课程

Lab 3实验报告
 

姓名

赵誉泽

学号

120L020310

班号

2003003

电子邮件

2839366160@qq.com

手机号码

18317069040

 

目录

1 实验目标概述······································································ 1

2 实验环境配置······································································ 1

3 实验过程················································································ 1

3.1 待开发的三个应用场景·········································· 1

3.2 ADT识别与设计························································ 1

3.2.1 任务1:投票类型VoteType······················ 1

3.2.2 任务2:投票项VoteItem<C>·················· 1

3.2.3 任务3:选票Vote·········································· 2

3.2.4 任务4:投票活动Poll<C>的测试·········· 2

3.2.5 任务5:投票活动Poll<C>的实现类GeneralPollImpl······················································ 2

3.2.6 任务6:投票活动Poll<C>的子类型····· 2

3.3 ADT行为的设计与实现·········································· 2

3.3.1 任务7:合法性检查······································· 2

3.3.2 任务8:采用Strategy设计模式实现灵活的计票规则······································································ 2

3.3.3 任务9:采用Strategy设计模式实现灵活的遴选规则······································································ 2

3.3.4 任务10:处理匿名和实名投票················· 2

3.3.5 任务11:采用Visitor设计模式实现功能扩展····················································································· 2

3.3.6 任务12:基于语法的数据读入················· 2

3.4 任务13:应用设计与开发···································· 2

3.4.1 商业表决系统····················································· 2

3.4.2 代表选举系统····················································· 2

3.4.3 聚餐点菜系统····················································· 2

3.5 任务14:应对面临的新变化······························· 2

3.5.1 商业表决应用:可以一次表决多个商业提案····················································································· 2

3.5.2 代表选举应用:遴选规则变化··················· 3

3.5.3 聚餐点菜应用:取消权重设置、只计算“喜欢”的票数·································································· 3

3.6 Git仓库结构································································· 3

4 实验进度记录······································································ 3

5 实验过程中遇到的困难与解决途径·························· 3

6 实验过程中收获的经验、教训、感想····················· 4

6.1 实验过程中收获的经验和教训(必答)········ 4

6.2 针对以下方面的感受(必答)··························· 4

1. 实验目标概述

 本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性

的软件,主要使用以下软件构造技术:

  1. 子类型、泛型、多态、重写、重载
  2. 继承、委派、CRP
  3. 语法驱动的编程、正则表达式
  4. 设计模式

本次实验给定了多个具体应用,学生不是直接针对每个应用分别编程实现,

而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充分考虑

这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)和更

容易面向各种变化(可维护性)。

2. 实验环境配置

使用idea,右上角自带覆盖率测试。

GitHub Lab2仓库的URL地址:https://github.com/ComputerScienceHIT/HIT-Lab3-120L020310

3. 实验过程

请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。

3.1 待开发的三个应用场景

  1. Election

  针对某次活动(例如哈工大学生代表大会),需要从一群候选人中选出若干人,作为代表参加活动。在该选举中,提前确定一部分候选人,投票人从已确定的候选人中选取,不可提名新的候选人。计划选出的代表数量,是提前确定的。投票人针对每个候选人匿名选择“支持、反对、弃权”之一,但选择“支持”的人数不能高于计划选出的代表数量,,否则为非法票。

所有投票人的权重均相等。全体投票人投票之后,首先判定各张选票的合法性,在去除非法选票之后,针对所有候选人,根据其所得到的支持票数量排序,前k个候选人当选;若有多个候选人的支持票数量相等而无法自然排出前k名,则仅

有那些明确可进入前k名的人当选——这意味着最终选出的人数可能小于k。

例如,计划从5个提前确定的候选人(A,B,C,D,E)中选出3个代表,投票人数为6,各自的选票如下:

         图3.1.1选举投票示例

可以看出,投票人 1、2、4、5、6 的选票都是有效选票,但投票人 3 投了 4 票“支持”,超过了法定当选人数 3,故其选票无效。对有效选票进行统计,候选人A-E所得支持票的数目分别为:1、2、5、3、3,候选人C,D,E的票数排名前三,故成功选出了 3 位代表。如果本次选举希望选出 2 个代表,那么因为D和E的得票相同,则只能选出C,而D和E无法当选。

2. DinnerOrder

一群人去餐馆就餐,需要从该餐馆提供的菜单中选择若干道菜,点菜的数量要大于等于就餐总人数,且小于总人数+5。每个人针对菜单上的每一道菜实名表达自己的喜好(喜欢、不喜欢、无所谓),选择这三个选项的数目无限制。不同人的身份不同,其偏好的影响力会有所不同(例如家庭聚餐时,老人的权重更大、子女的权重更小,见下表黄色部分)。所有人表达观点之后,根据影响力加权计票(喜欢、不喜欢、无所谓分别得分 201),取总得分最高的前,道菜。若因为有多道菜得分相等而无法自然排出前k名,则除了那些明确可进入前k名的菜之外,在其他得分相等的菜中随机选取一部分,凑足k个菜。

例如,4 个人去吃饭,从菜单的 6 道菜中选择 4 道菜,各自偏好如下表所示:

      图3.1.2 点菜投票示例

针对菜品C,有 2 个“喜欢”票、1 个“不喜欢”票,1 个“无所谓”票,其总得分为4 × 1 + 1 × 2 + 2 × 0 + 2 × 2 = 10。用相同的计算方式得到其他菜的得分,最终计算排名结果为:A >B >C =D > E > F,故选择ABCD四道菜。

如果最终六道菜的总得分分别为17、12、8、8、8、6,那么前两道菜一定可以排入前 4 名,但第 3~5 道菜得分都是8,无法区分,此时从中随机选出 2道菜,形成了最终入选的 4 道菜。

3. BusinessVoting

面向某个商业公司,其内部成员提出某个商业提案(例如“关于投资 xx 项目的提案”),公司董事会的各位董事对其进行实名表决(支持、反对、弃权),各董事在表决中的权重取决于其所持有的公司股票的比例,根据该持股比例对投票结果进行计算,若“支持”票的比例超过 2/3,则该提案通过,否则该提案不通过。以下给出了针对某提案的表决结果示。

例:

                 图3.1.3 商业表决投票

  那么,“支持”所占的比例为51% + 10% = 61% < 2/3,故该提案表决未通

过。

   分析这三个应用的要求,我们发现三个应用都包含了各种各样的“一组投票人针对一组候选对象的投票、评价、打分”的现象,虽然评价对象的类型有差异、评价机制有差异、评价规则有差异,但仍存在很多共性。

共性:

  1. 三者投票活动分别是:股东表决某个商业提案,学生选举大会代表,一家人聚餐点菜。
  2. 三者候选对象分别是:商业提案,被选举的学生,菜品。
  3. 三者投票人分别是:股东,学生,就餐者。
  4. 三者投票项分别是:一个股东对一个商业提案的表决票,一个学生对一个被选举人的选票,一个人对某个菜品的偏好。
  5. 三者的选票分别是:一个股东对所有商业提案的表决票,一个学生对所有被选举人的投票,一个人对所有菜品的偏好。

以上都是共性的概念,可以根据用同样的数据类型表示并复用。

然而在实际应用中,还有多个维度上的差异。

差异:

  1. 三者候选数量差异:股东表决唯一一个商业提案,学生选举大会和聚餐点菜选出多个结果,聚餐点菜选出的结果数量大于等于就餐人数,小于就餐人数+5。
  2. 三者投票权重差异:股东表决和聚餐点菜的投票者权重不同,选举大会的投票者权重均相同。
  3. 三者投票者匿名与否差异:股东表决和聚餐点菜的投票者采取实名投票,选举大会的投票者采取匿名投票。
  4. 投票数量差异:股东表决只能对唯一的商业提案投票,聚餐点菜的投票者和选举大会的投票者须对所有候选项投票。
  5. 投票要求不同:股东表决和选举大会的投票者选择“支持”,“反对”,“弃权”,聚餐点菜的投票者选择“喜欢”,“不喜欢”,“无所谓”。
  6. 投票结果不同:选举大会投票限制在k人及以内,即相同票数时选择排除相同票数,选择前面名次票数的人;聚餐点菜限制k道菜,若票数相同,则在相同票数中随机选择满足k道菜的要求;股东表决要求满足支持率大于2/3,否则不通过。                                                                                                                                          3.2 ADT识别与设计

 设计结构如下图所示:

3. 2. 1 任务1:投票类型VoteType

  包含两种投票类型,分别是喜欢(2),不喜欢(0),无所谓(1)支持(1),弃权(0),反对(-1)

  由于初始化投票类型时,只能初始化为一种类型,则不允许同时出现两种类型的票在这个初始化的Map<String,Integer> options种,在checkrep中判断,若如此则不通过checkrep。

  设置checkLegality检查每个选项是否和预设分值相对应,否则也不通过checklegality。

 图3.2.1.1votetype的checkrep              图3.2.1.2checklegality方法

3.2.2 任务2:投票项VoteItem<C>

  指代对一个候选人Candidate的投票string。Checkrep检查两个rep均不为空即可。

设置了observor方法,得到投票的对象和选项。其hashcode值为candidate的hashcode和选项的hashcode之和。

    图3.2.2.1 voteitem的hashcode

3.2.3 任务3:选票Vote

 这里是匿名投票,不需要管理投票人的信息,代表一个投票人对所有候选对象的投票项集合,即set<VoteItem<C>>,以及投票时间。

  Checkrep检查集合中每个voteitem均不为空。

  Getvoteitem函数返回set<VoteItem<C>>的防御式拷贝,防止client对此进行修改。

  Hashcode为每个voteitem的hashcode的相加和。

   图3.2.3.1vote的hashcode

3.2.4 任务4:投票活动Poll<C>的测试

  根据如下数据,设计test方法,同时在generalpollimpl中设置observer方法,来观察每一步得到的结果是否符合预期。

  图3.2.4.1poll的测试方法

数据如下:

  1. 投票活动的名称
  2. 发起时间
  3. 候选对象的集合(例如 List<C> candidates
  4. 投票人集合及各自的权重:本次活动由哪些投票人(类型为 Voter)、每个投票人的权重(类型为 Double)拟遴选出的候选人数量%
  5. 本次投票可使用的投票类型(VoteType
  6. 投票(即所有投票人的选票 Vote 的集合)
  7. 投票结果统计(根据投票结果,统计出所有候选对象的得分)
  8. 计票结果(根据规则和投票结果统计,从中选出%个对象作为结果)

3.2.5 任务5:投票活动Poll<C>的实现类GeneralPollImpl

Checkrep方法检查初始化以及进行数据统计(即statistics方法)之前是否使得adt符合规范性。

图3.2.5.1 GeneralPollImpl的checkrep

Statistics函数使用strategy模板,传入一个statisticsstrategy类的变量,通过委托给其他adt来实现。在该方法内检查选票的合法性,即保证选票首先符合规范,且不出现多选少选的情况。

图3.2.5.2 statistics方法

Selection函数同样使用strategy模板,传入一个SelectionStrategy类的变量,同时传入要选出的结果的数量,通过委托给其他adt来实现选选票结果排序。

图3.2.5.3 selection方法

3.2.6 任务6:投票活动Poll<C>的子类型

即Election,DinnerOrder,BusinessVoting,每个类型均继承自generalPollImpl,但对具体的类进行了细化。

Election中的泛型即用Person表示,DinnerOrder中是dish,BusinessVoting中是proposal。

同时区分实名投票和不实名投票,在election使用vote类型选票,在DinnerOrder和BusinessVoting中使用realnamevote。

同时根据要求分别限定addvote的检查方法。如在Election中,限定一个人的投票数了不能超过选出结果数量。

图3.2.6.1满足每个选票数不超过选出结果数量。

同时要求另外两adt均满足每个选票覆盖所以候选项,且不能多选或漏选。

      图3.2.6.2 每个选票覆盖所有候选项且不能多选或漏选

3.3 ADT行为的设计与实现

3.3.1 任务7:合法性检查

在进行计票之前,还需要检查以下内容,具体在 Poll 的 statistics()方法中实现:

  1. 若尚有投票人还未投票,则不能开始计票;
  2. 若一个投票人提交了多次选票,则它们均为非法,计票时不计算在内。

解决方法如下:其中提交多次选票的解决方法在addvote方法中实现。

图3.3.1.1 检查投票是否合法

图3.3.1.2检查投票在addvote中是否合法。

3.3.2 任务8:采用Strategy设计模式实现灵活的计票规则

   首先在接口中设置抽象方法calculate,参数为投票人集合和所有选票集合。

  图3.3.2.1接口StatisticsStrategy实现

这里以DinnerOrder的策略模式实现为例介绍策略模式的步骤:

  1. 首先将set<Realnamevote<C>>votes转换为list形式,便于取值。
  2. 设置一个map记录票选出来的分数。
  3. 设置double类型数组sum记录每个候选人的得分,最后输入到map中。数组大小取值为list.get(0).getVoteItems().size(),即任意选票的voteitem大小,前提是选票是合法的。数组初始化为0.0。
  4. 设置votetype
  5. 遍历votes中每个投票人对每个候选人的投票,sum数组分别为其权重*投票分数之和。
  6. 再次遍历每个候选人,此时将候选人和对应分数put进数组score中,并返回。

  图3.3.2.2 strategy方法

ElectionStatisticsStrategy大致上与DinnerOrder一致,sum加分方法改为不计算权重的加分。

BusinessVotingStatisticsStrategy由于只需要选出一个proposal,所以做出了相应修改。

3.3.3 任务9:采用Strategy设计模式实现灵活的遴选规则

首先完成接口设计,参数为统计得到的得分数据和待选出的人数。

这里以Electionselection为示例介绍方法:

  1. 首先,设置score数组,类型为double,大小为statistics的大小,将score赋值为statistics的得分。
  2. 其次,对score数组中的数据采取冒泡排序,得到排序后的结果。
  3. 根据排序后的结果,根据value的值找key,将key按照value的大小依次put进map中。

 图3.3.3.1ElectionSelectionStrategy

DinnerOrderSelection策略与electionselection类似。

BusinessVotingSelectionStrategy方法中,只要求得分高于2/3即可通过提案,则可以简化设计。

  图3.3.3.2 BusinessVotingSelectionStrategy

3.3.4 任务10:处理匿名和实名投票

  1. 设置realnamevote子类型继承vote,其在vote的基础上添加了一个voter类型的rep,用以实名化vote。它的hashcode设置为原来的set<VoteItem<C>>中所以voteitem项的hashcode之和加上voter的hashcode得到新的hashcode。

图3.3.4.1 realnamevote的hashcode

  1. 设置一个新的接口VoteforDecorator,包含基础方法,然后设置一个decorator的抽象类来实现这个接口,在抽象类中构造函数初始化。

图3.3.4.2 VoteforDecorator接口                    图3.3.4.3 decorator类

接下来设计一个RealnameDecorator类继承decorator类,在这个类中定义一个Voter类型参数,在构造函数中加入,实现构造器方法。

3.3.5 任务11:采用Visitor设计模式实现功能扩展

首先设计接口visitor,根据三个app设置三个visit方法,分别以三个不同的子类型作参数。为了使用Visitor模式,我们需要利用GeneralPollImpl中的getter方法,使访问者能够访问到这些信息。此外,还需要在GeneralPollImpl中留一个accept方法。

  图3.3.5.1 visitor接口

Visitor的一般继承结构如下:

图3.3.5.2继承结构

在GeneralPollImpl类中增加对应的三个方法,分别在子类型中重写。

图3.3.5.3 accept方法

由于设计了三个visit方法,所以设置子类型的visit类为抽象类,仅重写对应方法。

图3.3.5.4 visitor实现

在election中的accept方法:

图3.3.5.5 election中visit方法实现。

3.3.6 任务12:基于语法的数据读入

设置另一种构造函数,通过输入正则表达式来指定votetype的种类。

  图3.2.2.3正则表达式构造函数。

  1. GetScoreByOption方法,通过输入的字符串判断其得分。
  2. 重写Hashcode方法,hashcode的值为votetype中选项字符串的hashcode值之和。                                                                                                                                          3. 4 任务13:应用设计与开发
  3. 4.1 商业表决系统

这里自定义了一场投票,投票人,权重,投票选择如下:

  图3.4.1.1投票人和权重

  图3.4.1.2 投票选择

结果输出如下,符合预期

  图3.4.1.3 投票结果显示

修改权重后proposal通过:

3.4.2 代表选举系统

这里electionapp给出了示例,给定投票人,,权重,投票选择如下:

  图3.4.2.1投票人和权重

  图3.4.2.2 候选人及投票选择

结果输出如下,符合预期

  图3.4.2.3 投票结果显示

3.4.3 聚餐点菜系统

这里自定义了一场投票,投票人,权重,投票选择如下:

  图3.4.3.1投票人和权重

  图3.4.3.2 候选菜品及投票选择

结果输出如下,符合预期

  图3.4.3.3 投票结果显示

3.5 任务14:应对面临的新变化

3.5.1 商业表决应用:可以一次表决多个商业提案

在原来代码基础上,首先BusinessVotingApp中添加另外两个提案,再添加对应投票,这里设置为最后输出两个提案通过,一个提案不通过。

修改poll的子类型BusinessVoting,其中删除checkrep中的quantity=1和candidates=1的要求。

然后在statisticsstrategy中将double sum改为double sum[]数组涵盖每个候选人对应的得分。Selectionstrategy中修改为统计所有得分大于2/3的候选提案,并一起输出。

  图 3.5.1.1投票结果

3.5.2 代表选举应用:遴选规则变化

仅修改selectionstrategy,设置一个Double sum[]数组记录对应候选人的反对票数。

   图3.5.2.1Double sum[]记录反对票数

在第k位和第k+1位票数相等时,判断相等支持票数候选人的反对票数,选出较小的反对票数的候选人进入最终结果。

   图3.5.2.2 选出反对票较少的候选人

3.5.3 聚餐点菜应用:取消权重设置、只计算“喜欢”的票数

首先修改app中输入voters的信息:

  图3.5.3.1修改权重信息

然后修改statisticsstrategy中的计分规则,不考虑权重且只计算喜欢的票数:

   图3.5.3.2 修改计分规则

3.6 Git仓库结构

得到的object graph如下

4 实验进度记录

请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。

每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。

不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。

日期

时间段

计划任务

实际完成情况

2022-06-20

8:30-11:30

总览指导手册,有初步概念,并完成VoteType设计

按计划完成

2022-06-20

13:00-13:30

完成VoteItem的方法和规约的设计

按计划完成

2022-06-20

13:30-14:40

完成Vote的方法与规约的设计

按计划完成

2022-06-20

14:50-16:00

完成VoteTypeTest和VoteItemTest设计

按计划完成

2022-06-20

18:50-20:00

完成VoteTest设计

按计划完成

2022-06-21

8:30-10:00

完成GeneralPollImpl设计

未按计划完成

2022-06-21

13:30-16:00

完成GeneralPollImpl设计以及ElectionApps完善

按计划完成

2022-06-21

18:30-23:00

完成StatisticsStrategy和其子类设计

按计划完成

2022-06-22

8:30-10:30

完成PollTest

按计划完成

2022-06-22

13:30-17:00

完成SelectionStrategy及其子类设计

按计划完成

2022-06-22

18:30-22:00

完成DinnerOrderApp设计

未按计划完成

2022-06-23

8:30-11:00

从DinnerOrderApp出发寻找bug并解决

按计划完成

2022-06-23

13:00-15:00

完成BusinessVotingApp设计

未按计划完成

2022-06-23

15:00-18:00

从BusinessVotingApp出发寻找bug并解决

按计划完成

2022-06-24

8:30-9:00

完成votetype的正则表达式构造方法设计

按计划完成

2022-06-24

9:30-11:00

完成ElectionTest设计

按计划完成

2022-06-24

13:30-15:00

完成DinnerOrderTest设计

按计划完成

2022-06-24

18:30-20:00

完成BusinessVotingTest设计

按计划完成

2022-06-25

8:00-10:30

完成三个visitor设计

按计划完成

2022-06-25

13:30-16:00

完成realnamevote的decorator设计

按计划完成

5. 实验过程中遇到的困难与解决途径

遇到的难点

解决途径

不理解PollTest要测试的内容

放在最后对整体框架有清晰认识之后写,测试了GeneralPollImpl类中的共性方法,区别性的方法放在具体app的test中进行测试。

Statisticsstrategy实现时对算法结构无法实现按顺序遍历

将set转换为list类型,再画出snapshot图便于理解以及写代码。

SelectionStrategy最后将有序的数据返回时无法保证有序性

设置一个LinkedHashMap保证输出的有序性

测试app时发现选项相同时会不被set集合加入

重写voteitem的hashcode,设置新的hashcode算法

设计decorator时无法用decorator继承接口类型

设计一个vote接口,再设计一个抽象类来实现该接口,再设计包含voter的rep的实现类继承该抽象类,

 6. 实验过程中收获的经验、教训、感想

6.1 实验过程中收获的经验和教训(必答)

实践了可复用性和可维护性的构造,巩固了理论知识,同时使得自身写代码更具有框架感。同时,更好地运用了set,list,map等数据结构,以及多种构造模板方法,如strategy,decorator,visitor等。同时也学到了自定义异常以及异常的使用方法。

6.2 针对以下方面的感受(必答)

1. 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在三个不同的应用场景下使用,你是否体会到复用的好处?

答:能,代码复用可以通过组合和继承,简化具有共性的方法的应用。如votetype,voteitem,vote,realnamevote,voter类等在各个app中的使用,以及策略模式的不同用法,极大地简化了代码。

2. 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?

答:是为了提高代码的可读性,可修正性,便于健壮性检查,也便于写代码图中的规范。我会在以后的编程中坚持这么做。

3. 之前你将别人提供的ADT/API用于自己的程序开发中,本次实验你尝试着开发给别人使用的ADT/API,是否能够体会到其中的难处和乐趣?

答:能,可以体会到一个完整的程序构造需要的步骤的繁杂,以及极强的结构性要求;但同时每完成一个部分都能带来成就感,这是不可多得的乐趣。

4. 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个简单的解析器,使用语法和正则表达式去解析一个遵循特定规则的字符串并据此构造对象。你对语法驱动编程有何感受?

答:语法驱动程序对于客户端来说更为直观,同时也具有很强的包容性,可以接受多种多样的字符串输入,且一定程度减少了规范性检查的要求。

5. Lab1和Lab2的工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验中也提供了一部分基础代码。假如本实验要求你完全从0开始进行ADT的设计并用OOP实现,你觉得自己是否能够完全搞定?你认为“设计ADT”的难度主要体现在哪些地方?

答:能,不过需要额外的时间。主要体现在理解整个adt的框架思路及作用上,同时要使得设计出来的adt具有可复用性和可维护性,这是十分需要时间去仔细琢磨的。

6. “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的三个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、委派、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?

答:接口中包含实现类需要实现的具体方法,抽象类可以继承接口,同样包含方法但不需要实现,在实现类中,具体实现继承于抽象类的方法或实现于接口的方法,可以设置多个类继承或实现同一个接口或抽象类,达到复用的目的。

但这样的设计需要清晰的整体思路,以及对于接口,抽象类和实现类的清晰的认识,同时也需要大量时间去梳理框架,这是设计的难点所在。

7. 关于本实验的工作量、难度、deadline。

答:工作量较大,难度较高,不过deadline较为合适,没有其他课程考试的压力,可以有整整一周时间沉下心来写代码,十分有利于我们深入学习。

8. 课程结束了,你对《软件构造》课程内容和任课教师的评价如何?

答:较好,内容如果能有更好地翻译版本就更好了,任课教师答疑很负责,会很耐心地解答同学们的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值