上大学以来,一直保持了写学期总结的习惯。本来软工这部分是要放在总结里的,但想了想,作为本学期花时间最多的课,大作业贯穿了差不多半个学期,而以我啰啰嗦嗦的行文方式,直接塞进总结里未免显得有点主次颠倒。因此直接将这一部分提了出来,单成一篇文章。

软工这门课,全称软件工程,与造计算机基本齐名,是贵系大三上学期的几座大山之一。其大作业要求学生组队一起完成一个工程项目,助教会从成品、文档,以及过程管理等几个方面考察。

而为了缓解大三上的课程压力,在这个不怎么忙(当时觉得的,唉)的大二上,我们几个人一起选了这门课。

以上为课程背景。

组队与选题

选题

我和黄大大、韦师和天龙一起组了一队。队伍其实是大一下预选的时候就组好了的,毕竟这种课,第一节课上来就要大家组队选题,基本上就是“一人退课,全组同退”的节奏。

那么第一个问题就是选哪个项目。本学期的项目一共有8个,每组可以选择一个一志愿,两个二志愿和三个三志愿。项目有:

  • 学生组织微信公众号辅助管理平台。这个项目要求的是做一个网站,而小学期也大概学了怎么用Django框架搭一个简单的网站,多少算有一点基础。而且它的基础要求并不高,如果实在来不及做附加要求,也不会有太大损失。因此这个项目就成了我们的一志愿。
  • 空气污染天气预报手机应用。这个要求做一个手机App,虽然看上去也挺简单,但毕竟大家都没有基础。因此这个项目成了我们的二志愿。
  • 实验室物资管理系统(二期)。这个是二期项目(一期就是传说中的百页文档项目),也就是说,我们需要看一期工程的代码和文档,然后在此基础上修bug加feature。同组的人里除了天龙都是上过姚boss的OOP的,都深刻体会过看别人代码是怎样的体验,因此说什么也不选这个项目。
  • 机器学习实验平台。其实这个项目还好,看上去难度也不是很大。但是坑爹的一点是:我们所需实现的前端,与实际调用的由他人负责的后端,是一同开发的。也就是说,我们得调用一个谁知道有没有bug的黑箱。不过由于其他项目还是偏难,因此这个项目成了我们的二志愿。
  • 计蒜课中小学趣味编程平台。大概是要做一个普及编程的网站,让用户编写代码完成一些游戏,寓教于乐。这个项目看上去就非常好玩,但难度也是可想而知得大。出于一种保守的想法,我们还是希望优先选择简单的项目。因此这个项目成了我们的三志愿。
  • 人人贷业务数据报表平台。要求其实不高,主要就是做一个包装SQL的网站(也没细看,感觉是这样的)。但这个项目是给人人贷公司做的,这个感觉就不一样了。其实是对这个项目没什么想法的,但三志愿要有三个,由实在不想选二期,因此这个项目也成了我们的三志愿。
  • 基于安卓系统的车载蓝牙应用。事后证明是最坑爹的项目。要做的是在Android 4.4上实现Android 5.0开始内置了的新版蓝牙协议。出于和上面的项目同样的想法,我们把它放成了三志愿。
  • 计算机组成原理联动项目“玩脱一次挂两科”系列。因为没选计原(造计算机)所以本来就选不了。

最后我们被分到的也是第一个项目。事实证明这一选择是非常明智的,因为这一项目的难度的确不大,这也就给了我们充分的时间来完成附加功能和美化前端,使得网站看上去逼格比较高。而且这一项目的负责助教是罗成罗老板,人非常好,还经常表扬我们组。(这里给还没选软工的各位一点忠告:一定要挑简单、熟悉的项目,如果有罗老板的项目,一定要砸一志愿选!)

队名与队长

起名是非常麻烦的一件事情。在平时写代码时都起不出好变量名的我们,采用了韦师ACM队起名时用的弃疗策略:把四人的姓的首字母拼到一起,看能打出什么字。

于是我们拿h、h、w和y来做全排列,当试到whhy的时候,打出了“我好慌呀”。这个名字得到了一致好评,于是按照小驼峰式命名法,我们的英文队名就成了imSoNervousAh。而这个队名也成为了第一个项目中字典序最小的,所以我们也成为了展示时的第一组。不过这是后话了。

而至于队长,之前是采用石头剪子布的方法决定了是我的。提交分组方案时要求的是把队长名字写在文件名中,文件中应当附上队员信息列表。所以我把我的名字加在了文件名中,然后按照姓名字典序写上了队员信息列表。然后队长就成了黄大大。这个真不怪我啊。

前期规划

用Django框架+Python 2是一早就确定好的,剩下要确定的是网站的框架和队员分工。分工是比较好确定的:

  • 我们把工作大致分为前端(网页部分)和后端(Python和数据库部分)。因为小学期做了一些炫酷界面,我就自告奋勇成了前端;黄大大表示自己没有审美,因此自觉成了后端。而天龙后来也成了前端,韦师则成了后端。
  • 后端的分工大概是韦师负责数据库部分,黄大大负责剩下的杂项部分。事实证明这是个不明智的分发,因为杂项看起来多,但实际比数据库的部分少很多= =所以后面又把单元测试的锅丢给了黄大大。
  • 前端则没有太详细的分工,基本上是看着感觉来,谁想写就谁写。硬要说的话,比较像是我打基础,搭框架,然后天龙写具体的页面和Django Views,最后再由我来优化细节。

框架这事说来简单其实麻烦。一开始对需求理解不透彻,很难建出一个详细的框架,只能给一个粗略的、大概能work的壳子。之后干的也只是往壳子里塞干货而已。到了一定的时候回发现,原来的壳子无法容下新的feature了,这时有需要重新思考框架,如果可能就在原框架上修改,必要的话还得推倒重做。

比较幸运的是我们没有遇到需要推倒重来的情况,不过补丁倒是打了一大堆。另外一方面,也算是切身体验到了OOP中讲的那么多设计原则是干什么的:各部分的解耦、前后端的分离真的非常重要,即便像我们在设计时已经比较注意解耦合了,在需要修改一部分的时候还是会遇到需要改相关的许多其他部分的情况。

好像有点扯远了。

开发过程

这个要是详细讲起来就没完没了了。因此这一部分只打算按照时间线粗略说说。

课程要求的是采用敏捷开发的方法,将开发过程分为6个Sprint(大概就是6个阶段)。由于大家都是喜欢赶DDL的人,而从第4到10周每周四下午都有软工组会,因此我们的代码基本上都是周三(很多时候是周三晚上)到周四早上写的。从我们的项目进度上可以明显观察出哪些日子是周四。另一方面,由于软工课在周四上午,而学期中需要熄灯,又因为黄大大的电脑续航能力大约是1个半小时,所以我们四个中,基本上只有黄大大偶尔会去上课。

打基础的Sprint 1

我们大概是从国庆节开始写的,因为国庆节后有第一次组会。第一次讨论(其实就是关于分工和框架的讨论)在国庆节放假前。其实本来打框架的是天龙,但我这个人其实是比较固执偏执的,因此最后还是由我来挑符合我自己胃口的模板。在第一个Sprint中我们的界面是长这样的:

第一版管理员界面

第一版学生界面

基本上也就是直接把网上的模板粘贴了进来,然后改了几个板块而已,很多不需要的东西都还没有删掉。这个时候链接都是点不了的,真真能work的只有登录界面和学生、管理员的首页内容显示而已。

但是这个界面在组会上也被表扬成了“进度较快”。的确也算是,看上去还是挺唬人的,但其实真正属于我们自己的东西还没多少。不过这个时候后端已经把框架差不多打完了,已经基本上处于一个在等前端显示的状态了。

没干啥事的Sprint 2

在第一次组会上,我们大概算是做的第二好的。看到做的最好的一组的成果时,还是深感存在差距。他们不光是打好了框架,甚至已经实现了基础的审批功能,并且从一开始就使用了Ajax,而这个时候作为前端框架的我还不知道什么是Ajax。而且他们使用Django Template的继承基本上实现了代码复用,而我还在到处复制粘贴。

因此这一段时间中我的核心任务就是重构框架,利用Template继承,然后加上Ajax。第一次重构这么快就来了。但这个事情也不是说弄就能弄的,还有很多前置技能需要学习。所以我的目标是在Sprint 3的时候加上Ajax之类的功能,在此之前先把别的界面铺上去。

但可能也是因为Sprint 1的进度“看上去比较快”,Sprint 2我们基本上没干啥事,主要干的就是实现了公众号详情页面,以及审批的流程。另外,由于之前觉得我们的界面还不够好看,因此天龙又去找了一套CSS贴到了我们的模板上。

本来这里应该有图的,但当我checkout到那个时候的commit后网站莫名其妙不work了(思考了一下,应该是因为不在内网,而当时的登录顺序还有问题),想了想就懒得贴了。

前端基本成熟的Sprint 3

Sprint 2完了之后,基本上前端后端进度就相同了。后面的开发其实更多的是前端先鼓捣出一个页面,告诉后端要显示什么,然后后端再布置相应的接口。之前其实说接口这块是由黄大大负责的,但其实写的时候发现,接口部分和数据结构高度耦合,而且都是无脑代码。所以这部分也变成韦师的锅了。

Sprint 3主要做的有站内信功能和前端的优化。由于我又觉得天龙找的CSS不够好看,所以我又找了一套自己觉得比较漂亮的CSS,贴到了我们的模板上。可能罗老板觉得我们进度快的一个原因就是我们每次都换皮肤吧。

前端的优化自然包括之前说好的Ajax和Template继承。奋斗过程就不说了,写上来也没什么意思。总之在这两周中还是接触到了很多前端的黑科技,最后借助jQuery造了一些轮子,实现了自己的Ajax载入函数loadContent,还加入了看上去比较有逼格的动画效果。

站内信功能则是我们实现的第一个附加功能。大概就是管理员和学生之间随便聊天的一个功能。这一块主要是天龙写的,因为这阵子我正在折腾Ajax。总的来说,做的还是不错的。

完善细节的Sprint 4

这个Sprint的事情我记得比较清楚,因为其实我没干太多实事。这个Sprint里我干的很多事情其实是可有可无的,相对细枝末节的事情。大概也算是所谓的“人性化设计”吧。比如加入了一个表格模板,用Template继承的方法配合前端的JavaScript实现排序、搜索和分页;还有又花了左侧边栏的各种乱七八糟的行为。

说到这个左侧边栏,可能是整个工程里我干的最费力不讨好的事情了。光在这货上砸的时间大概就有10个小时:

  • 首先是我希望能实现一种左侧边栏固定在页面上,不随页面滚动而滚动的效果,但这样的话滚动到页面底部就会鬼畜。因此我找了一个库来处理这种行为,还修了库里面的一个bug。
  • 然后又因为左侧边栏比较长,我又在侧边栏内嵌入了一个div,这样就能在侧边栏内部滚动了。但这样又会有滚动条,非常难看,于是又去找了各种CSS黑科技,在各个浏览器中就隐去了滚动条。
  • 现在没了滚动条,用户怎么能知道还可以滚动呢?因此在侧边栏底部和顶部加了一条滚动指示栏,如果没有滚动到头则会显示一个箭头。
  • 最后由于整个网页都做了响应式设计,因此直接支持移动设备显示,但这时左侧边栏又非常碍事。因此针对移动端单独设计了一个左侧边栏,采用了类似Material Design中Drawer的形式。

但说实话,其实不会有人太在意这些东西吧= =实际使用的时候不好说,但就展示而言,其实很多都是不会重点提到的功能,毕竟不是主打功能。

响应式布局

不过话说回来,通过实现这么一个东西,大概也能体会到各位模板作者是有多么厉害了……也学到了一些黑科技。而且可能真正使用的时候,有这么个东西还是方便一些吧。

不断加功能的Sprint 5和忘记开了的Sprint 6

过完Sprint 4就只剩两周了。这时我们开始一条一条对照需求列表,逐步加上我们还没有的东西。因此这一段时间也是我们在JIRA上加issue最多的一段时间。

除了加功能之外,还有修复bug以及考虑一些原来没考虑的数据合法性的问题,以及潜在的安全漏洞。总之都是一些琐碎繁杂的事情。

至于这个忘了开的Sprint 6……是因为虽然课程规定了Sprint的时间段,但Sprint的开启和结束是由每个队自己控制的……而我们直到大作业期限截止才意识到没开Sprint 6……也就没管了= =

最后上两张成品图吧,逼格还是挺高的吧:

登录界面

管理员界面

有关git的轶事

开发中需要用git做版本控制。一开始只有韦师是熟练掌握git用法,天龙、我和黄大大都只是略懂,因此很多时候会把我们的git仓库玩炸,又需要韦师来善后。这个过程中就发生了很多好玩的事情:

  • 一开始的乱玩:

xjb commit

  • 经常忘记commit,一次憋一个大招:

巨大的commit

  • 初期的版本树(顺带一提,后期因为每次都rebase而变成了一条链……感觉也不是太优秀):

混乱的Git版本树

  • 说好了commit不准带数据库:

甚至单独commit了一个数据库

  • ——“天龙,你把那个修改的按钮改成删除的按钮。”

反正用户不知道发生了什么

小作业、考试和展示

虽然这课大作业存在感出奇地高,但小作业和考试也还是有的。说到底,这两者之间好像也没什么联系。

小作业都挺简单的,就不说了。这20%的分还是很好拿的。

考试占了最后得分的20%。说实话,这个比例还是挺高的,毕竟大作业只占60%,而这60%中只有52%(是这60%的52%也就是31.2%)和成品有关,其它都是开发过程、测试、文档的分,或者是送的分。考试内容和作业内容重合度还是比较高的,也有一些论述题,大概就是要你说该怎么分工怎么测试之类的……最后写满了半份答题纸。感觉像是文科题。

展示这个事情还是有点伤心的。讲稿是我基于管哥的炫酷Keynote模板改的,上去讲的是黄大大(队长责任之一)。我们作为第一个上场的队,光荣地拿到了倒数第一的投票。总结一下的话,原因大概有两点:想展示所有的功能,但时间不够,而且也没能突出自己独有的特色功能;没有笑点或亮点,加上第一个出场,让人记不住。不过也算是学习了一个人生的经验。

文档

由于之前赶DDL吃过亏,因此这次我们机智地在DDL前一天开始写文档。但悲伤的是,我花了一整天思考该怎么写,以及用什么来写。先后试过各种Markdown主题、各种LaTeX模板之后,还是决定:用Word写。事实说明Word也是能写出不属于LaTeX的效果的。

而DDL当天则由起床失败开始。坐到电脑前开始写则是接近中午。从中午到晚上12点,除了吃饭就是在写文档。最后把所有部分汇总到一起,得到了两份30多页的文档,赶在DDL前1分钟交上了网络学堂。

于是这门课就正式结课了。

一点感想

这门课是我这个学期投入最多的一门课,基本上第4~10周每周得通宵一次;也是最后成绩最好的一门课,总评拿到了97分,排名第四,相比之下其他课程没有上92分的。

要说收获的话,当然有很多可以说的:学到了前端的这套理论,算是入了门;学会了怎么用git做版本控制;认识到了架构设计的重要性等等。但我觉得最重要的其实是,和要好的朋友一起合作完成了一个大家都很满意的项目,这件事本身。之前的项目基本上都是独立完成的,即便是合作的项目,也基本上是各干各的最后拼起来,而且我还会不放心地再过一遍,其实就相当于我自己再写了一遍。

但是这次的合作则不同。我们几个人一起写代码,同步推进各部分的进度,配合紧密。而且,由于我们都是竞赛党,可以说都互相放心:毕竟代码能力是摆在那里的。我可以相信队友写出来的代码,可以放心地使用他们交给我的API(当然这不是说code review就不重要了= =这块的亏也吃过)。这样的一种合作氛围就非常地愉快,以至于我们在男生节那天一起在308熬夜码代码到5点左右时,308被我们包场,于是我们一帮放歌,一边笑着聊天,一边码代码。上面图中的“单独commit一个数据库”以及“修改改成删除”的两次commit都是发生在这个时候,当时真是笑得肚子疼,现在想起来也觉得欢乐。

而通过这门课,我也大概体验到了工程开发是怎样的。之前一直说毕业之后要去工作,现在想一想,是不是也可以不那么快做决定?之所以现在会这么想,是因为感觉这一类工程开发……怎么说呢,其实谁都能做。尤其是前端这一块,本身的更新换代也快,隔几个月就蹦出来一个新的框架,要学的太多。而实际产品设计应该是由专门的美工来完成的,那么前端说白了就是要把美工纸上的设计搬到网页上,再加上逻辑代码。这种工作,说真的,提不起我的兴趣。而如果是很有技术含量的后端呢,一方面我现在也不太懂,另一方面有些实验室里干的活可能也和这个差不多……所以说,可能还是需要等我实际到实验室和实习岗位都去走过一遭后才好确定。现阶段能干的事情,大概就是多做几手准备了吧。扯远了= =

要是问我后不后悔这个学期选了这门课的话,我的回答肯定是不后悔,因为:这样我大三上就可以笑看其他人赶DDL了啊。另外也希望我们的这个项目能成为明年的二期工程,那样我就能更开心地看别人赶DDL了。玩笑开到这,不过这个项目倒真的挺适合成为二期的,原因有三:文档详细、界面美观、改进空间大。

总之,这门课给我的这个学期添了不少堵,也带来了不少快乐。想不到更好的话结尾了,就这样吧。