北航面向对象设计与构造第四单元总结

        面向对象设计与构造课程的第四单元终于结束了。这一单元专注于培养学生的模型化设计能力。接下来我将基于我的个人代码进行一些介绍分析,并对个人第四单元的学习状况进行简要总结与分享。

架构分析

代码构造

        为了方便接下来对架构的分析,要先对本人的代码结构进行简要介绍,并对类的设计考虑进行简要介绍。下方依次是UML类图,从预定书籍到取书的顺序图,表征书籍状态的状态转移图。

        有一些类是基于题目情境中的实际场景而建立的,它们分别是AppointmentOffice(预订处),BorrowAndReturnOffice(借阅处),DriftCorner(漂流角),QueryOffice(查询处)。StudentList类用来管理所有访问的学生信息,学生用Student类的对象来表示。Book类用来管理Student手中的书籍,因此Book对象只能存在在Student中,进入Student时创建,离开Student时销毁。OrderRequest对象在学生下达预订请求后生成,作为“待完成订单”存在,在整理阶段如果将书送达借阅处,相对应的OrderRequest就被销毁,即订单被完成。Manager类的身份是管理者,它处理外部输入的指令,并指挥各个类交换信息,配合完成相关请求。

        Main类的主要作用是对一些类初始化,为了起到Manager分发器的作用,需要在Manager和场景类之间建立双向关联关系,Main类就对这方面进行配置,之后将新的输入传入Manager类进行处理。对于开店后的正常输入指令就由Manager类在各类之间进行信息交换。每次开店后的整理工作如下:扣除借书逾期学生的信用,将预约处过期的书放回书架,将能满足的订单从书架送到预约处,升级漂流角的书籍。每次关店后的整理工作如下:将顾客归还的书(正规/非正规)送回书架/漂流角。

与UML设计的对比

       UML图中的元素要与代码中形成一一对应的关系,包括类内方法,属性,类与类之间的关系,等等。课程组的官方包内提供的@SendMessage和@Trigger注释也体现了这种追踪关系,代码中被标记的方法必须要在状态转移图/顺序图中有对应的体现。

正向建模与开发

        正向建模与开发指的是先面向需求绘制出UML类图、顺序图以及状态图等等,先大体确定好程序的框架,然后具体编写代码逻辑。        

        代码合理地实现了UML图的设计。我感受到先画出UML类图再进行代码编写可以有效地帮我理清类之间的结构,采用Manager类进行分发的想法也是在画UML图的时候为了减少代码之间耦合的时候想出来的,可见良好的UML设计有助于体现高内聚低耦合的面向对象思想。不过最开始的类图是不够完善的,我在完成代码之后又对原先的类图进行修补,不过总体的结构是不变的。

        我的顺序图与状态转移图在代码完成后才画出来,我觉得无伤大雅,这两个图都是为了让别人更好的了解设计者意图才创建的,不过状态转移图很好地帮助我检查了书籍在不同位置之间的转移路径,进一步理清代码结构。

        本单元的三次作业中我并没有进行很大规模的重构,一是感觉我采用的代码架构具有比较高的鲁棒性,即使要添加补丁也比较容易;二是因为三次迭代几乎全部是增量式开发,不需要对原先代码做什么改动,比较大的改动大概也只在于添加了Book类来管理学生持有的书籍。

        第十三次作业的中测中,我在处理picked请求时忘了考虑student取书后是否还满足借书限制,而出现了BUG;第十四次作业和第十五次作业中测没有出现BUG,三次强测都没出现BUG。第十五次作业课下我测出来一个有关判断student是否满足信用要求和判断是否借A类书的优先级的问题。

        在特殊评测点,我出现过一次方法名与具体实现不对应的BUG,修复起来很容易;总的来说本单元的迭代开发比较顺利。

课程总结

        OO课程马上就要结束了,接下来对我这一学期的整体学习情况和学习收获做一个简要总结。

设计思维

U1:层次化设计

U2:线程安全设计

U3:规格化设计

U4:模型化设计

      

        我的代码架构几乎全部都是自己完成,并没有参考任何往届学长学姐的博客之类的,因为我觉得参考之后类似“作弊”,也有点没劲。结果就是丢了很多分。我这样也有点好处,就是训练了自己独立设计代码架构的能力,但是丢了很多分。我现在是有点后悔的,有时候应该放下自己心里的“傲气”。

        第一单元的主题是层次化设计,具体背景是完成一个具备代入函数表达式,含有指数,导数和基本数字的括号展开程序。大体方法是利用正则表达式和递归下降法来分析输入串的文法。在这个阶段我还不熟练Java的使用,而且并不明白面向对象如何体现在这种题目中,当时甚至不明白为什么要为表达式,项和因子各自建立一个类。现在看来当时的自己很可笑,但是这也说明我有很大的进步了吧。这一单元让我初步熟悉了面向对象编程,至少不再感到陌生。在hw3中我也特意引入了专门用作辅助工具的“工具类”,初步体会到了高内聚低耦合的好处。第一单元中由于没有前车之鉴,导致hw1代码可拓展性低,在hw2发生了一次大规模重构,对我来说是一次惨痛的教训。经过这次不愉快的体验后,我再也没有出现过大规模代码重构了,体会到了一个好的架构有多么重要。

        第二单元的主题是线程安全设计,具体背景是完成一个具备一定复杂度的电梯调度系统。本单元对我来说是印象最深刻的一个单元,主要是因为太折磨了。我主要的体会是架构的重要性,因为越迭代运行逻辑越复杂,如果一开始就没想明白具体实现细节,还盲目地向上迭代,只会变得令人恶心(从debug角度上和从视觉角度上)。因为这点,U2的开发异常艰难,回看我hw7的代码,令人忍俊不禁——

public void run() {
        while (true) { //completeResetRequest();
            if (waitingQueue.hasResetRequest()) {
                reset((ResetOp) waitingQueue.getResetRequestAndRemove());
                continue; }
            boolean noMainElevator = (mainElevator == ' '); //为DC且空时为真
            if (!noMainElevator) {
                dropFlag = checkDropOpenDoor();
                rideFlag = checkRideOpenDoor();
                if (dropFlag) {
                    openDoor();//first,open the door
                    tryDropOffGuests();//if exists passenger need to get off,remove them
                    closeDoor(); } } //seems useless!
            if (mainRequest != null && dcflag && mainElevator == ' ') {
                mainElevator = initMainElevator(mainRequest); }
            if (mainRequest == null) { //mainRequest has been finished ,update new mainRequest
                if (!ridingRequest.isEmpty()) { //search for mainRequest from riding Request
                    mainRequest = ridingRequest.get(0);
                    ridingRequest.remove(0);
                    mainRequestState = 1; //already on the elevator
                    if (dcflag) {
                        mainElevator = initMainElevator(mainRequest); }
                } else { //search from waiting queue
                    Req req = waitingQueue.getFirstPassengerRequest(); //如果空了或者有reset请求,返回Null
                    if (req != null) {
                        mainRequest = (Passenger) req;
                        mainRequestState = 0;
                        waitingQueue.removeRequest(req);
                        if (dcflag) {
                            mainElevator = initMainElevator(mainRequest); }
                    } else if (waitingQueue.hasResetRequest()) {
                        reset((ResetOp) waitingQueue.getResetRequestAndRemove());
                        continue;
                    } else {
                        return; } } }
            if (!noMainElevator) {
                if (rideFlag != null) {
                    direction = getNextDirection(); //get next moving direction
                    if (rideFlag) { //if the door has open
                        openDoor();
                        tryRideNewGuests();//if exists new passenger getting on,add them
                        closeDoor(); }
                    move(); } } } } //elevator moves

        说真的,即使加了注释,我自己都看不懂这一坨东西写的是什么意思。所以,一定要想好了,做好架构了再写!

        第三单元的主题是规格化设计,具体背景是依照JML完成一个社交网络管理系统。这一单元就比较轻松了。我主要学习的设计思想是规格与实现分离。引用第三次博客中我的感悟:

        事实上,数据规格描述只是为了方便后续方法的JML描述所必须“预设”的前提,与实际代码中的实现并没有很大关系,即规格与实现分离,persons可以用ArrayList,或HashMap管理。

        方法的规格也同理。按照JML来实现会导致效率低下,时间复杂度高。在代码中你只需要保证这样实现的结果与JML给出的结果一致。JML与实际代码能够一一对应,运行后的结果与按照JML实现的结果一致,这就足够了。说到底,JML是为了准确的描述一个方法都做了什么,实际代码如何实现跟它并无太大关系。

        第四单元的主题是模型化设计,具体背景是设计一个图书管理系统。我体会到正向建模的思想,在利用UML帮助进行模型设计后,再进行代码实现,更能做到高内聚低耦合。一个好的代码架构是很重要的。

        经过这四个单元的训练,OO课程让我从这方面的小白变得也能写一些代码,至少是比以前自如了,感觉不管在设计思维上还是在面向对象方法上我都收获很大。

测试思维

        在大一的课程和大二上的CO课程中,我最主要使用的是面向评测机测试(反正不限制次数嘛)。但是由于OO的评测机使用次数是有限的,我开始采用别的测试方式。

        大体上来看,我四个单元的测试思路都大差不差。先总结出来所有新增功能,每实现一个功能就手动编造样例做一个简单的黑箱测试,实现所有的功能后,将所有的新增功能综合起来做黑箱测试。之后利用前几次迭代留下来的代码做回归测试。当然这些数据点要尽量覆盖各种边界情况,与时间有关的要进行压力测试等等。最后利用评测机做自动化测试。

        在写代码的时候有意地在不期望出现的语句里留一些System.err的输出语句,这样方便debug,一下就知道自己在哪里出错,而且标准错误不影响多线程的运行,所以U2也可以应用。

        发现bug之后的调试就是应用IDEA的调试模式打断点测试,这一点就不再多说了。

        当然交上去之后也有可能出BUG,这时候就得不得已地回到面向评测机测试的方法了。

        第三单元我还是用了JUnit做单元测试,是一次新颖的体验。

课程收获

        首先自然是OO的核心教学目标:面向对象思维。我想这一点在上面设计思维和测试思维两部分已经说的足够多了。

        虽然这门课不是Java课,但是一定是学到了很多Java相关的编程知识的。我感受到Java是一门很优雅的语言,写起来很舒适,OO课让我学会写Java也算是一举两得。

        我觉得很有意思的是,每一单元的学习中,除了课程要求学习的主题内容外,我都或多或少地学到了别的知识,列出如下:

U1:正则表达式,BigInteger,圈复杂度,深拷贝与浅拷贝,TODO注释

U2:调度算法,Random类

U3:图算法,JUnit,自定义异常类,Java内置的Queue类,自定义比较器

U4:枚举类,自定义标记,时间相关类

        我以OO为媒介与许多同学,助教都成为了好朋友。

结尾

        想一想大二上的oopre课程似乎才刚刚结束,一转眼oo的正课都结束了,时间过得真的很快。在oopre的最后一节课,我记得吴际老师大概这样说:

“下学期的oo正课分四个单元,每个单元迭代三次,会比较有难度,你们几乎肯定要进行代码的大规模重构,但是只要把架构做好,其实难度没有那么大。”

        当时的我心里是表示不屑的,因为我当时想,oo正课才迭代了3次,而oopre却在一个主题上迭代了8次,必须轻松拿下oo。看来还是开心的太早了。

        oo正课的第一次课上袁源老师大概这么说:

“咱们这门课的难度比较大,有一些挂科的同学。我们现在上的课是昆仑课程,如果有不及格的同学,我们在暑假为同学们开设了补给站课程。”

        我的心里就比较害怕了,我想6系这么多大佬聚集的地方还有这么高的挂科率,我想及格真的很有难度。的确“破防”的情绪出现过几次,在hw1在新学期的第一周第一天就布置下去了,打了我一个措手不及,我以为会让我们缓冲一下的,而课业内容对我这种天赋略差又没有基础的同学来说真的是云里雾里,我几乎笃信自己会进入补给站了。在hw5~hw7的电梯单元内我因为听不懂也不会写,还在路上崩溃哭过,我实在无法接受自己做不出来,在这种地方因为做不出来作业而挂科。但是现在来看,即使结果可能不太如意,作业完成的不理想,但是我也完成oo课程了,也不至于进入补给站。我想连oo都熬过来了,之后也没什么好怕的吧。但是现在也有很多压力,实在卷不过别的同学了,够呛能保研了

        所以最后一次课时袁源老师这样说:

“这一次课是咱们oo的最后一节理论课。”

        听到这句话的时候我是有点感慨的。

        oo真是让我又爱又恨,但是毋庸置疑的是oo已经对我产生了很大影响。现在要跟oo说再见时,我居然会有点不舍,这算不算是一种斯德哥尔摩综合征呢。学oo的时候总是痛苦并快乐着,即使我自己也说不清有什么快乐的,或许自己的潜意识里是期待着挑战的吧,我不清楚。

        在这种情绪驱使下我报名了明年的oo助教,即使自己什么都不会,没有任何经验,能力也不足。我报名只是让我自己不后悔,被拒绝了也是很正常的。

        在最后我要感谢我的老师袁源老师,整个oo课程组(我知道为了给我们这届减负已经做了很多了),帮助过我的同学和助教们,比如srz,wwr,gyx,xy,hcty等等,谢谢你们。

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值