先看下题目:第四单元实现一个基于JDK 8
带有效性检查的UML(Unified Modeling Language)
类图,顺序图,状态图分析器 MyUmlInteraction
,实际上咱们要创建一个有向图模型,UML
中的对象(元素)可能与同级元素链接,也可与低级元素相连造成层次关系java
输入:上述三UML
图的边集(非实体元素)与点集,上下层级元素经过_parent
隐式邻接。值得注意的是,不一样于前两个单元,咱们终于回归了离线算法OFFLINE
,I/O
经过一行END_OF_MODEL
分隔,处理难度减少了c++
输出:针对给出的_name
索引到相应的UML
元素查询相关对应图中信息,复杂的problem
是hw15
中对循环继承的判断,其余的都能不使用高级算法解决掉算法
下图是我对本单元做业设计的类图,由于三次做业都是这个架构,下文只按解析器构造流程顺序分析。btw感谢肖同窗的排雷帖,可见我对UML
的理解不够深刻,UMLAttribute
能够出如今顺序图中,不该该简单纳入类图元素的子类,不过我这样仍是能活过本单元的编程
我构建了抽象类MyEle
,以及三个子抽象类对应三种UML
图中的元素,由于接口给出的结点(对应实体的元素)没有邻接结点的信息,我有必要自定义类来保存这些信息。能将这种“一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能知足的”问题解决的是适配器模式设计模式
public class MyAdapt { //适配器用于转换的类 public static MyEle as(UmlElement e) { switch (e.getElementType()) { case UML_CLASS: { return new MyClass((UmlClass) e); } case UML_ASSOCIATION: { return new MyAssc((UmlAssociation) e); }... // public abstract class MyEle { //My*类对UmlElement对象有一个依赖,MyEle实现基本信息接口,其余由子类分别考量 private UmlElement ele; public MyEle(UmlElement e) { this.ele = e; }...
在UmlInteraction
构造函数中,我首先将传入的UmlElement
经过MyAdapt
“转换”成My*
对象,后者能够从前者中只存储咱们关心的信息,一些能够忽略的元素UmlEvent
/ UmlEndPoint
等直接在这里过滤掉安全
有了这些准备工做开始建图,以类图为例(另两类图如法炮制),经过.mdj
解析,边集经过_parent
/ _reference
/ UML
元素表示,提取后完成建图。我对做业中涉及的全部的UmlElement
,包括边集UMLAssociation
/ UMLGeneralization
等都适配本身的类,固然这无关紧要,我这样作是为了遍历一遍MyEle
对象就完成建图,看起来清晰一点。类图中的顶级元素是类与接口,都管理着方法与属性,StarUML
中能够对两者进行不少相同操做,有必要为两者建立父类ClassLike
(类实体)来复用代码数据结构
根据输入模式,咱们在解析器中须要管理按_name
索引的Class
/ StateMachine
/ Interaction
等集合,而且索引查询中要进行有效与重复检查。我为了复用代码采用了泛型容器,分别用Hashmap
/ Hashset
实现有效_name
查找表与重复_name
池多线程
public class MySet<T> { private HashMap<String, T> na2ele = new HashMap<>(); private HashSet<String> dupName = new HashSet<>(); public void add(T t) { if (t instanceof MyEle) if (na2ele.containsKey(t.getName())) dupName.add(t.getName()); else na2ele.put(t.getName(), t); } public boolean contains(String s) { return na2ele.containsKey(s); } public boolean isDup(String s) { return dupName.contains(s); } ...
根据输入,查询请求关心ClassLike
自身的层次或直接关联关系或ClassLike
间接继承/实现的关系,后者如“类的顶级父类 / 实现的所有接口”须要ClassLike
继承链上的信息。没有循环继承时继承链为树形结构,由此,我在MyClassLike
定义addFather
/ getFather
/ update
继承相关抽象方法。update
将extends
或implements
的ClassLike
进行update
后再合并信息,置为更新过的状态,是递归式搜索架构
输出方面,经过与同窗交流能够记忆化搜索(查询时更新),可是这是个OFFLINE
问题,同时考虑到题目的数据规模1e2.6
,小,在完成建图以后直接对全部类update
,所有元素更新后再进行查询也没问题,这样不用每一个查询方法都先update
一下了ide
hw15
要求进行有效性检查,上文中查询前update
显然要放到R002
/ R003
之间,有效性以_id
/ _name
重复,_visibility
等检查为主,很简单;R002
检测循环继承,输出继承环路上全部元素,固然不用求出环路,只要求出全部环路上的元素。MyEle
处在继承环路上 iff 其处于某阶数大于1
的强连通份量上或自己存在自环或一次以自身为源点的搜索通过自己1
次以上,故可以使用Tarjan
或BFS
来检查
谈到架构设计及OO方法理解的演进,我认为本身本学期确实有长进,起码前两个Unit
次次重构,但后两个Unit
都是先好好分析设计结构再写的,并且都不很复杂,因此都没有重构
Unit1
为层次化设计,hw1
/ hw2
是纯纯的面向过程,hw3
中利用工厂模式根据实际中的实体为表达式中的对象创建了具备继承层次关系的类,经过将求导以及化简操做做为抽象方法,使得因子,表达式啥的都能按照本身的求导法则各自计算导数、化简,输出经过覆写toString()
,这是我首次在做业中体会到面向对象中多态的优点。
Unit2
为多线程设计,代码结构很简单,采用简单的生产-消费者模式都行,主要仍是经过Java
库及保留字实现Java
线程的同步与互斥,这块一直理不太清又很难调试,因此又重构了好屡次,能够说我也是初次接触在线算法问题,电梯调度的优劣是跟数据相关的,其实有点玄学,在这里我初步了解了Java
的异常机制,这是保证鲁棒性的方法之一,Java
中还有封装好的ReentrantLock
可重入锁等锁,我也作了了解
Unit3
为规格设计,须要根据JML
封装官方包中的接口,首先得说JML
表意很准确,不用像指导书般反复研究,这个Unit
你们实现的接口功能都是同样,但方法与效果不尽相同,面向对象中就是经过这样的封装实现代码的模块化,调用者很难为封装好的方法优化,做为实现者有义务实现高效的封装。面向过程和面向对象确定不是对立的,咱们实现的代码对外是屏蔽掉的,因此在做业中我在底层函数用了不少面向过程风格的代码
Unit4
为模型化设计,咱们接触的UML
脱离了Java
,描述的是对象、属性、状态等,更加接近面向对象的本质,做业方面空间很大,因为hw13
就理好了层次,后续轻松拓展,还第一次使用了泛型容器,节约了不少代码量,充分封装各个层次的操做以模块化
固然我印象最深入的是多线程程序测试,程序运行结果与环境相关,在测试出现问题后很难经过程序自己找到病因,我使用JProfiler
+ println()
才能逐步肯定程序卡在了哪里
测试分两方面,一是验证程序的基本功能,二是对极限数据或异常输入测试,前者经过随机生成样例数据或JUnit
单元测试法或对拍实现,后者如电梯换乘调度测试,特殊表达式求导,给定数据规模下复杂度最高的数据。我认为注重后者或许对咱们更有价值,反正个人Bug
都出在极限状况...还有一种测试就是对优化过的代码进行测试,必定得用修改前版本对拍一下,属实对这一点有ptsd
了
JUnit
对我而言很好用,@Before
/ @Test
/ @After
完整构建了数据生成、断言测试、错误分析的测试流程,还在同一项目下就能用,舒服啊
首先我在本学期经过Java
认识到面向对象的印象深入的特征:封装与多态
封装定义为隐藏对象的属性和实现细节,仅对外公开接口,我最开始对每一个属性几乎都写了get
/ set
,但这样属性的隐藏原则就没意义了,我理解的封装是没有需求就不开放属性的查询或方法的调用,因此属于对象“分内事”的方法也尽可能用private
修饰。封装的优点显然,首先封装隐藏了属性,保证了程序的数据安全,封装方法,可使程序设计更加模块化,好比Unit4
中可将三种图的查询分派给三种管理类,这样也能下降类的平均复杂度,还有一点是封装的代码拓展性强,须要改变程序功能时,调用者每每不用改写,只需对封装的对象中的数据结构与方法进行修改,这也是课程迭代开发模式下的要求。固然,既然有这个信息隐藏原则,调用者就很难根据实际问题对封装对象做出调整,致使封装对象已经很难有优化的空间,因此使用封装时,调用者得对对象有一个大致的了解,好比在Unit3
中我由于对Hashmap
的底层实现不了解,初始容量过大引起了隐患;实现者则需合理利用时空资源保证程序的总体良性
多态,我理解的是同一类型对象引用在运行时可使用不一样的方法以实现“多种形态”,一种形式是子类Override
方法后使用父类类型引用,还能够是由不一样类实现的接口。经过多态在Unit1
实现表达式中不一样因子的求导,在Unit4
中可让数据结构不一样的Class
/ Interface
(单/多继承)共享一份Tarjan
代码,多态巧妙在咱们无需去if-else
所引用的对象实际是什么类型,调用的方法采用对应继承链上最近的版本,因此多态中想要扩展新子类很简单,只须要符合引用者的行为逻辑,引用新子类的对象则无需修改代码
固然一些别的特征也很酷,咱们能够经过继承复用父类的代码,也能够经过泛型类来复用代码,而且实现相似多态的效果,不过泛型类在构造传入类型时,其所管理的类型就肯定了,因此我以为泛型倒有一点c/c++
中#define
的意思
不难看出,上面这些特征,不管是类也好,接口也好,其共性是对对象行为范式的一种抽象,OO
语言不一样于PO
语言的一点就是它给了一套这种抽象的机制,因此面向对象编程更贴近实际,咱们代码的结构每每对应实际中的逻辑关系,可能这也是Java
的优点,虽然不精炼,但很直观易懂
在理论网课 / 实验课中,我也学到了OO
设计模式与原则,OO
的可维护性强,很适合于迭代开发与复杂系统的总体设计,学习OO
编程思想,更深刻地理解Python
/ C++
等的OO
机制了
看到这么多同窗吐槽实验课我就放心了,我认为实验课有必要给同窗们必定的反馈,具体的形式能够是编程性实验以后能够在平台上像做业同样了解测试状况而且进行Bug
修复
感受后两Unit
对可维护性要求下降了,Unit1
中嵌套的出现打破了本来简单的表达式到因子的层次关系,Unit2
中多部电梯的出现改变了控制类的数据结构,也改变了线程同步的条件,而Unit3 / 4
在迭代开发中基本是跟随指导书在上一次做业中多写些方法增长功能就okay了,反正就是对原先的做业架构的冲击力不够大,应该提升难度,btw程序高效性也很重要,但愿后两个Unit
能开启性能分
Unit3
能够放到前面,体现出类规格,层次间的规格
互测本意应该是驱动你们学习其余同窗代码中的设计策略吧,建议发动Hack
的时候得提交本身发现的Bug
的位置,防止互测变成纯黑箱互测
条件艰苦的线上网课,颇怀念线下的课堂氛围和peer pressure呢
感谢仍离同窗们最近的老师与助教们~
马马虎虎算是入门了OO
,铁铁们,共勉