关于第四单元与整个OO课程的总结与反思 - Coekjan

Unit4架构设计

第一次作业

本次架构极差, 几乎将所有逻辑都塞在了 UmlInteraction 中, 谈不上设计.

若要找某一具有 id 元素的父元素, 则简单粗暴地采用 idMapElement.get(idMapElement.get(id).getParentId()) . 不具有层次关系.

第二, 三次作业(重构)

这两次作业中, 我将上述映射化为具体的类成员, 将官方包中的类封装为功能更强大的类.

抽象泛型类 UmlElementInfo 用于方便后续的封装:

/* package */ abstract class UmlElementInfo<T extends UmlElement> {
    /* package */ static final Map<String, UmlElementInfo<?>> ID_MEMORY = new HashMap<>();

    private final T element;

    protected UmlElementInfo(T element) {
        this.element = element;
        ID_MEMORY.put(element.getId(), this);
    }

    /* package */ T getElement() {
        return element;
    }

    public String getName() {
        return element.getName();
    }

    public UmlElementInfo<?> getParent() {
        return ID_MEMORY.get(element.getParentId());
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof UmlElementInfo &&
            Objects.equals(((UmlElementInfo<?>) obj).element, element);
    }

    @Override
    public int hashCode() {
        return element.hashCode();
    }
}

IDEA 生成的图还挺酷的.

可以看到大量中间圈中就是包装类(均继承该抽象泛型类)和相关的依赖.

通过这个封装, 基本上就解决了复杂的变换与数据存储问题, 在第二第三次作业中基本上不需要做任何改动.

四个单元中的架构设计与面向对象方法理解的演进

整个课程体验的过程中, 我并没有刻意去套用某种架构设计去完成某些具体任务, 而是通过解读需求来决定架构的设计.

第一单元 - 对抗任务复杂度与提取任务共性

第一单元的最重要的任务就是建立起函数存储模型, 要求其能够经受起增加函数类型的迭代考验. 那么就需要设计一个通用的函数类模型 - 抽象 Function - 用于承载具体的函数类, 并赋予其一些通用职责: 作为一个成熟的无穷次可微函数, 你应该会自己求导和字符串化了!

这样的职责设定, 相当于一种规范, 使得符合这样规范的函数类都能够融合到已有的函数类家族中, 而不引起任何的体系改动.

这样提取任务的共性, 就能够对抗任意增加函数类型的复杂任务.

第二单元 - 对抗线程安全问题与复杂的电梯模式

第二单元的最重要的任务就是建立起线程安全控制, 要求其能够经受起增加电梯种类与数量, 模拟情景变化的迭代考验. 那么就需要设计线程安全类, 来进行协调多个线程对共享资源的互斥访问.

可以说, 只要设计好了线程安全类, 就已经完成了大部分难点与避免了大部分bug了.

本单元的另一重要任务是电梯状态变化的描述(但其实这部分不采用具有扩展性的写法也能够平稳通过第二单元), 笔者采用了状态模式来建模电梯的状态变化, 只要设定好当前状态的直接后继状态, 即可确保状态变化的正确性.

此外, 还有一个重要的任务是电梯运行策略的描述, 笔者采用策略模式来建模电梯的运行策略, 只要给出电梯的当前运行状态与外来的需求, 即可进行电梯的运行决策. 只要保证策略类的逻辑正确, 即可保证外来需求完全解决.

第三单元与第四单元 - 对抗多类大量数据的通用性存储

这两个单元有一个共性的重点问题是建立起存储大量数据的通用性存储容器, 如一对多封装 Map<T, Collection<U>> 这部分的难度并不大, 只要对手造轮子进行针对性的单元测试, 就不会出什么问题.

四个单元中测试理解与实践的演进

四个单元中, 仅有第二单元没有搭建数据生成与评测(或对拍)机.

  1. 第一单元: 基于形式化文法的数据生成机与基于sympy黄金模型的正确性评定机.
  2. 第二单元: 白盒测试(俗称肉眼盯代码), 找出线程安全问题, 并通过一些极端手造样例来辅助验证.
  3. 第三单元: 基于指令格式的数据生成机与基于本地标程的正确性评定机.
  4. 第四单元: 基于指令格式的数据生成机与对拍机.(中途咕咕)

总的而言, 由于前三单元的数据与数据之间的关联可以很弱, 所以测试难度在前三个单元都不高; 到了第四单元, 我发现UML类图元素的生成与组织还是相当困难的, 很难才能做出合格的数据生成机, 因此第四单元的后两次作业也就没法向后迭代数据生成机了.

关于第四单元的数据生成, 笔者反思, 或许需要依赖面向对象的构造思路, 才能更好地管理数据生成逻辑, 控制工程复杂度.

课程收获

所学

总体而言, 我在OO课程中, 尤其是课程作业中学到了很多东西.

  1. Java语言及其高级特性
    • 泛型编程
    • 流式编程(函数式接口)
    • ...
  2. 面向对象理念与设计方法论
    • SOLID
    • 各类模式: 状态, 策略, 单例, 工厂, 抽象工厂, 命令, 生产者消费者, 读者写者...
    • ...

所想

北航第一届学生入学时, 有横幅"欢迎未来的红色工程师"; 计算机学院的英文全称是 School of Computer Science and Engineering (计算机科学与工程学院); 高院长上学期授计算机组成原理课时反复强调工程化方法; OS课要求同学们阅读大量工程化代码; OO课要求同学们学习运用面向对象的思维方法来对抗工程复杂性, 独立完成鲁棒, 可扩展, 可维护的软件程序. 可见信息化语境下对工程化的强烈需求.

在整个OO课程的紧凑节奏下, 笔者也不断锤炼自己的工程化能力, 在写下每一句代码时都仔细斟酌其鲁棒性, 可扩展性, 可维护性. 总体看下来, 笔者顺利完成了每一次作业, 最终保持住了强测互测不被hack的记录, 这可以看作是对笔者工程化能力的肯定.

愿笔者与诸君都能在今后的工程生涯中都能坚持工程化思维, 编写出健壮的工程产品, 不负信息时代给我们信息技术学生的使命.

关于课程的改进建议

欢迎读者一起探讨下述有关本课程的改进建议.

预习部分

有一点大刀阔斧的感觉.

将每个同学的代码公开, 不进行查重或只进行弱查重:

  1. 高层次同学往往在预习部分就展露锋芒, 将他们的代码公开, 有利于中低层次同学的学习与研究.
  2. 同学们可以互相找bug, 互相提出修改意见, 促进"教中学".

研讨课

从题目阅读到代码实现, 这是一个很复杂的过程, 希望今后的研讨课可以多多涉及这方面的内容, 如将这部分内容设定为必做或推荐选项. 让优秀的设计实现思路充分展现, 让大家都能领会一个项目的诞生全过程.

交叉验题

面试中学来的专业词汇 hhh

加强交叉验题水平, 验证题目表述的精确性, 保证测试数据合乎题目要求.

互测部分的改进

并发互测

希望能够加入并发互测选项, 如同时并发100个进程, 可能更能测试出java的并发bug.

迭代展示

在一个单元内, 假设参与第 \(n\) 次互测的同学有 \(\{P_1, P_2, \dotsm, P_k\}\) , 则 \(\forall i\in[1,k]\) , \(P_i\) 能够在第 \(n+1\) 次公测结束后拉取其他同学在第 \(n+1\) 次作业中最后一次提交的迭代进度与具体内容, 以及同一单元内前几次作业中最后一次提交内容.

这样做的好处是, 如果某同学认为同屋者的代码水平优秀, 那么就可以跟踪其迭代轨迹, 学习其迭代的艺术, 增长自己的能力.


6系的金课让我永生难忘, 衷心祝愿OO课发展得越来越好!

posted @ 2021-06-26 19:50  Coekjan  阅读(151)  评论(2编辑  收藏  举报