OO终章

Posted berserkerruaaaaaaa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OO终章相关的知识,希望对你有一定的参考价值。

OO终章

OO完结,这个课程比我想象得要肝,看来暑假得养老了。

感谢各位老师,各位助教带来的全新窝窝,游戏体验良好。

不过,也有遗憾,所以谁能告诉我BUG修复界面长啥样??

OO无伤过

感谢wsb,lsj,shh,xcb,hdl,zyy,lyt,xsy,wjyi,zt几位巨佬的帮助。

10198 lines,0 bugs。

技术图片

第四单元总结

Project13

类图

技术图片

大体上是把每个类挂在到类图下,把类的方法、属性挂在到类下,接口的属性和方法因为不涉及查询,故直接忽略。

复杂度

Method ev(G) iv(G) v(G)
Main.main(String[]) 1 1 1
silly.SillyAssociation.SillyAssociation(UmlElement) 1 1 1
silly.SillyAssociation.getTargetElementType() 1 1 1
silly.SillyAssociation.getTargetId() 1 1 1
silly.SillyAssociation.getTargetName() 1 1 1
silly.SillyClass.SillyClass(UmlClass) 1 1 1
silly.SillyClass.addAssociation(SillyAssociation) 1 2 2
silly.SillyClass.addAttribute(UmlAttribute) 1 2 2
silly.SillyClass.addInterface(SillyInterface) 1 1 1
silly.SillyClass.addOperation(SillyOperation) 1 1 3
silly.SillyClass.containsAttri(String) 1 1 1
silly.SillyClass.getAllAssoClassesId(HashSet) 2 3 3
silly.SillyClass.getAllAttri() 1 1 1
silly.SillyClass.getAttribute(String) 1 1 1
silly.SillyClass.getAttributeVis(String) 5 2 5
silly.SillyClass.getId() 1 1 1
silly.SillyClass.getMethodCnt(OperationQueryType) 7 3 7
silly.SillyClass.getMethodPermission(String) 4 3 8
silly.SillyClass.getName() 1 1 1
silly.SillyClass.getNotHiddenAttriList 2 3 3
silly.SillyClass.getParent() 1 1 1
silly.SillyClass.getSelfAttriCnt() 1 1 1
silly.SillyClass.getTopParentClassName() 2 2 3
silly.SillyClass.getTotalAssoCnt() 2 2 3
silly.SillyClass.getTotalAttriCnt() 2 2 3
silly.SillyClass.getTotalImplement() 2 3 4
silly.SillyClass.setParent(SillyClass) 1 1 1
silly.SillyInterface.SillyInterface(UmlInterface) 1 1 1
silly.SillyInterface.addParentInterface(SillyInterface) 1 1 1
silly.SillyInterface.getId() 1 1 1
silly.SillyInterface.getName() 1 1 1
silly.SillyInterface.getTotalParents() 2 2 3
silly.SillyOperation.SillyOperation(UmlOperation) 1 1 1
silly.SillyOperation.addParameter(UmlParameter) 1 1 2
silly.SillyOperation.getId() 1 1 1
silly.SillyOperation.getName() 1 1 1
silly.SillyOperation.getVisibility() 1 1 1
silly.SillyOperation.isHasReturnValue() 1 1 1
silly.SillyOperation.isNeedParameter() 1 1 1
silly.SillyUmlInteraction.SillyUmlInteraction(UmlElement...) 1 20 20
silly.SillyUmlInteraction.addAssociation(UmlAssociation) 1 3 3
silly.SillyUmlInteraction.addAttribute(UmlAttribute) 2 1 2
silly.SillyUmlInteraction.addClass(UmlClass) 1 1 1
silly.SillyUmlInteraction.addExtend(UmlGeneralization) 1 3 3
silly.SillyUmlInteraction.addImpelement(UmlInterfaceRealization) 1 1 1
silly.SillyUmlInteraction.addInterface(UmlInterface) 1 1 1
silly.SillyUmlInteraction.addOperation(UmlOperation) 2 1 2
silly.SillyUmlInteraction.addParameter(UmlParameter) 2 1 2
silly.SillyUmlInteraction.checkClass(String) 3 1 3
silly.SillyUmlInteraction.getClassAssociatedClassList(String) 1 2 2
silly.SillyUmlInteraction.getClassAssociationCount(String) 1 1 1
silly.SillyUmlInteraction.getClassAttributeCount 2 2 2
silly.SillyUmlInteraction.getClassAttributeVisibility(String,String) 1 1 1
silly.SillyUmlInteraction.getClassCount() 1 1 1
silly.SillyUmlInteraction.getClassOperationCount 1 1 1
silly.SillyUmlInteraction.getClassOperationVisibility(String,String) 1 1 1
silly.SillyUmlInteraction.getImplementInterfaceList(String) 1 2 2
silly.SillyUmlInteraction.getInformationNotHidden(String) 1 1 1
silly.SillyUmlInteraction.getTopParentClass(String) 1 1 1
Class OCavg WMC
Main 1 1
silly.SillyAssociation 1 4
silly.SillyClass 2.55 56
silly.SillyInterface 1.4 7
silly.SillyOperation 1.14 8
silly.SillyUmlInteraction 2.55 51
Package v(G)avg v(G)tot
1 1
silly 2.17 126
Module v(G)avg v(G)tot
Project13_True 2.15 127
Project v(G)avg v(G)tot
project 2.15 127

可见,在类图的初始化的时候,我把所有元素的挂载调用全部塞在了这一个方法,导致复杂度过高。

BUG

本次自己测试中遇到了忘记考虑接口的方法与属性的问题,导致了空指针bug,不过及时发现。

在查找所有实现的接口时我的算法三番两次地出现细节bug,不具有普遍意义,难以描述。

感谢wsb同学愿意一直协助我debug,使得我无伤过强测。

Project14

类图

技术图片

与上一个作业大致一样,都是基于挂载的结构。

不过,这次我最顶层的不是类图了,而是类图、状态机图、时序图三者的管理者。

类图设计同上个单元,故不再赘述。

时序图和状态机图可能各自存在多个,所以需要各自设置一个管理者,来管理多个状态机图和多个时序图。

之后则是对各个状态机图与时序图挂载上其下属。

依旧是一个树形关系结构。

复杂度分析

Method ev(G) iv(G) v(G)
Main.main(String[]) 1 1 1
SillyGeneralInteraction.SillyGeneralInteraction(UmlElement...) 1 1 1
SillyGeneralInteraction.checkForUml002() 1 1 1
SillyGeneralInteraction.checkForUml008() 1 1 1
SillyGeneralInteraction.checkForUml009() 1 1 1
SillyGeneralInteraction.getClassAssociatedClassList(String) 1 1 1
SillyGeneralInteraction.getClassAssociationCount(String) 1 1 1
SillyGeneralInteraction.getClassAttributeCount(String,AttributeQueryType) 1 1 1
SillyGeneralInteraction.getClassAttributeVisibility(String,String) 1 1 1
SillyGeneralInteraction.getClassCount() 1 1 1
SillyGeneralInteraction.getClassOperationCount(String,OperationQueryType) 1 1 1
SillyGeneralInteraction.getClassOperationVisibility(String,String) 1 1 1
SillyGeneralInteraction.getImplementInterfaceList(String) 1 1 1
SillyGeneralInteraction.getIncomingMessageCount(String,String) 1 1 1
SillyGeneralInteraction.getInformationNotHidden(String) 1 1 1
SillyGeneralInteraction.getMessageCount(String) 1 1 1
SillyGeneralInteraction.getParticipantCount(String) 1 1 1
SillyGeneralInteraction.getStateCount(String) 1 1 1
SillyGeneralInteraction.getSubsequentStateCount(String,String) 1 1 1
SillyGeneralInteraction.getTopParentClass(String) 1 1 1
SillyGeneralInteraction.getTransitionCount(String) 1 1 1
silly.SillyAssociation.SillyAssociation(UmlElement,String) 1 1 1
silly.SillyAssociation.getEndName() 1 1 1
silly.SillyAssociation.getTargetElementType() 1 1 1
silly.SillyAssociation.getTargetId() 1 1 1
silly.SillyAssociation.getTargetName() 1 1 1
silly.SillyClass.SillyClass(UmlClass) 1 1 1
silly.SillyClass.addAssociation(SillyAssociation) 1 3 3
silly.SillyClass.addAttribute(UmlAttribute) 1 2 2
silly.SillyClass.addInterface(SillyInterface) 1 1 1
silly.SillyClass.addOperation(SillyOperation) 1 1 3
silly.SillyClass.checkCircleExtend() 4 2 4
silly.SillyClass.checkForUml002() 5 5 7
silly.SillyClass.containsAttri(String) 1 1 1
silly.SillyClass.getAllAssoClassesId(HashSet) 2 3 3
silly.SillyClass.getAllAttri() 1 1 1
silly.SillyClass.getAttribute(String) 1 1 1
silly.SillyClass.getAttributeVis(String) 5 2 5
silly.SillyClass.getId() 1 1 1
silly.SillyClass.getImplementCnt() 2 3 4
silly.SillyClass.getMethodCnt(OperationQueryType) 7 3 7
silly.SillyClass.getMethodPermission(String) 4 3 8
silly.SillyClass.getName() 1 1 1
silly.SillyClass.getNotHiddenAttriList 2 3 3
silly.SillyClass.getParent() 1 1 1
silly.SillyClass.getSelfAttriCnt() 1 1 1
silly.SillyClass.getTopParentClassName() 2 2 3
silly.SillyClass.getTotalAssoCnt() 2 2 3
silly.SillyClass.getTotalAttriCnt() 2 2 3
silly.SillyClass.getTotalImplement() 2 3 4
silly.SillyClass.mergeMap(HashMap<String, Integer>,HashMap<String, Integer>) 1 2 2
silly.SillyClass.setParent(SillyClass) 1 1 1
silly.SillyClassGraph.SillyClassGraph(UmlElement...) 1 20 20
silly.SillyClassGraph.addAssociation(UmlAssociation) 1 3 3
silly.SillyClassGraph.addAttribute(UmlAttribute) 2 1 2
silly.SillyClassGraph.addClass(UmlClass) 1 1 1
silly.SillyClassGraph.addExtend(UmlGeneralization) 1 3 3
silly.SillyClassGraph.addImpelement(UmlInterfaceRealization) 1 1 1
silly.SillyClassGraph.addInterface(UmlInterface) 1 1 1
silly.SillyClassGraph.addOperation(UmlOperation) 2 1 2
silly.SillyClassGraph.addParameter(UmlParameter) 2 1 2
silly.SillyClassGraph.check002() 2 2 3
silly.SillyClassGraph.check008() 6 3 6
silly.SillyClassGraph.check009() 6 3 6
silly.SillyClassGraph.checkClass(String) 3 1 3
silly.SillyClassGraph.checkDupInterface(HashMap<String, Integer>) 3 1 3
silly.SillyClassGraph.getClassAssociatedClassList(String) 1 2 2
silly.SillyClassGraph.getClassAssociationCount(String) 1 1 1
silly.SillyClassGraph.getClassAttributeCount(String,AttributeQueryType) 2 2 2
silly.SillyClassGraph.getClassAttributeVisibility(String,String) 1 1 1
silly.SillyClassGraph.getClassCount() 1 1 1
silly.SillyClassGraph.getClassOperationCount(String,OperationQueryType) 1 1 1
silly.SillyClassGraph.getClassOperationVisibility(String,String) 1 1 1
silly.SillyClassGraph.getImplementInterfaceList(String) 1 2 2
silly.SillyClassGraph.getInformationNotHidden(String) 1 1 1
silly.SillyClassGraph.getTopParentClass(String) 1 1 1
silly.SillyCollaboration.SillyCollaboration(UmlInteraction) 1 1 1
silly.SillyCollaboration.addLifeline(UmlLifeline) 1 1 1
silly.SillyCollaboration.addMsg(UmlMessage) 2 1 2
silly.SillyCollaboration.checkDup(String) 3 1 3
silly.SillyCollaboration.getId() 1 1 1
silly.SillyCollaboration.getInComingCnt(String) 1 1 1
silly.SillyCollaboration.getMsgCnt() 1 1 1
silly.SillyCollaboration.getPtcpCnt() 1 1 1
silly.SillyCollaborationManager.SillyCollaborationManager(UmlElement...) 7 8 10
silly.SillyCollaborationManager.checkDup(String) 3 1 3
silly.SillyCollaborationManager.getIncomingMessageCount(String,String) 1 1 1
silly.SillyCollaborationManager.getMsgCnt(String) 1 1 1
silly.SillyCollaborationManager.getPtcpCnt(String) 1 1 1
silly.SillyInterface.SillyInterface(UmlInterface) 1 1 1
silly.SillyInterface.addParentInterface(SillyInterface) 1 1 1
silly.SillyInterface.checkCircleExtend() 5 3 5
silly.SillyInterface.getId() 1 1 1
silly.SillyInterface.getName() 1 1 1
silly.SillyInterface.getParents() 1 1 1
silly.SillyInterface.getParentsCnt() 2 2 3
silly.SillyInterface.getTotalParents() 2 2 3
silly.SillyInterface.mergeMap(HashMap<String, Integer>,HashMap<String, Integer>) 1 2 2
silly.SillyOperation.SillyOperation(UmlOperation) 1 1 1
silly.SillyOperation.addParameter(UmlParameter) 1 1 2
silly.SillyOperation.getId() 1 1 1
silly.SillyOperation.getName() 1 1 1
silly.SillyOperation.getVisibility() 1 1 1
silly.SillyOperation.isHasReturnValue() 1 1 1
silly.SillyOperation.isNeedParameter() 1 1 1
silly.SillyRegion.SillyRegion(UmlRegion,SillyStateMachine) 1 1 1
silly.SillyRegion.addEndState(UmlFinalState) 2 1 2
silly.SillyRegion.addInitState(UmlPseudostate) 2 1 2
silly.SillyRegion.addState(UmlState) 1 1 1
silly.SillyRegion.addTransition(UmlTransition) 5 5 5
silly.SillyRegion.checkDup(String) 3 1 3
silly.SillyRegion.getCanReachCnt(String) 1 1 1
silly.SillyRegion.getId() 1 1 1
silly.SillyRegion.getMachine() 1 1 1
silly.SillyRegion.getStateCnt() 1 1 1
silly.SillyRegion.getTransCnt() 1 1 1
silly.SillyState.SillyState(String,String) 1 1 1
silly.SillyState.addNext(SillyState) 1 1 1
silly.SillyState.getCanReachCnt() 4 3 4
silly.SillyState.getId() 1 1 1
silly.SillyState.getName() 1 1 1
silly.SillyState.getNextSet() 1 1 1
silly.SillyStateMachine.SillyStateMachine(UmlStateMachine) 1 1 1
silly.SillyStateMachine.addEndState(UmlFinalState) 2 1 2
silly.SillyStateMachine.addInitState(UmlPseudostate) 2 1 2
silly.SillyStateMachine.addRegion(UmlRegion) 1 2 2
silly.SillyStateMachine.addState(UmlState) 2 1 2
silly.SillyStateMachine.addTransition(UmlTransition) 2 1 2
silly.SillyStateMachine.checkDup(String) 3 1 3
silly.SillyStateMachine.getCanReachCnt(String) 1 1 1
silly.SillyStateMachine.getName() 1 1 1
silly.SillyStateMachine.getStateCnt() 1 2 2
silly.SillyStateMachine.getTransCnt() 1 2 2
silly.SillyStateMachine.inThisStateMachine(UmlElement) 1 1 1
silly.SillyStateMachineManager.SillyStateMachineManager(UmlElement...) 16 13 18
silly.SillyStateMachineManager.checkDup(String) 3 1 3
silly.SillyStateMachineManager.getStateCount(String) 1 1 1
silly.SillyStateMachineManager.getSubsequentStateCount(String,String) 1 1 1
silly.SillyStateMachineManager.getTransitionCount(String) 1 1 1
silly.SillyStateMachineManager.initStateMachine(UmlElement) 1 2 2
Class OCavg WMC
Main 1 1
SillyGeneralInteraction 1 20
silly.SillyAssociation 1 5
silly.SillyClass 2.85 74
silly.SillyClassGraph 2.88 69
silly.SillyCollaboration 1.38 11
silly.SillyCollaborationManager 3.2 16
silly.SillyInterface 2 18
silly.SillyOperation 1.14 8
silly.SillyRegion 1.73 19
silly.SillyState 1.5 9
silly.SillyStateMachine 1.75 21
silly.SillyStateMachineManager 4.33 26
Package v(G)avg v(G)tot
1 21
silly 2.32 276
Module v(G)avg v(G)tot
Project14 2.12 297
Project v(G)avg v(G)tot
project 2.12 297

可见,主要复杂度集中在类图初始化阶段与类图合法性检测部分。尤其是三个规则检测部分,这三个部分涉及到一些比较"算法"的东西,本人目前还是没有办法降低其复杂度,这兴许是一个遗憾吧。

BUG

这次的难度主要集中在不知道规则是什么,每个查询要查询的是什么,在后期官方作出明确之后,基本没啥难度了。

本次lsj同学测出了我的时序图在查询一个没有inComing消息的lifelineinComing时,会出现空指针问题,这是由于,我的查询HashMap只有在该lifelineinComing时才会建立相关Key,并且查询时是直接使用get,所以会出现上述问题,将查询时的get改为getOrDefault即可。

本次在强测中没有BUG。

单元小结

这个单元在架构设计上,确实是很有助于大家写出漂亮的架构,但是,在烤漆学习UML的各种神秘规则,还是太难做到。

架构设计与OO方法的演进

首先,我认为,OO的精髓在于,把功能按层次划分给不同的对象。

随意关键点有如下两点

  • 模拟
  • 层次
  • 分工

第一单元

技术图片

这一单元的分工我认为有以下几点

  • 加法、乘法运算
  • 求导运算
  • 字符串解析

层级我分为了以下几个层级

  • 表达式层级
  • 项层级
  • 因子层级

其中因子层级中的表达式因子又可以包含一个表达式。

同时,值得注意的是,我的设计中,常数因子并没有作为Factor的属性存在,而是作为一个因子存在,这样的好处是符合数学常识,处理起来更为优雅。

将字符串解析分离成一个类,极大降低了表达式类的复杂度。

第二单元

技术图片

本次作业中,我给Floor加入了访问权限这一设定,每一部电梯通过自己私有的Rail可以查询自己是否有权限在当前楼层停靠。为了便于实现换乘的功能,我引入了TemRequest这个概念(为了保证类图的简洁直观,并未在图中体现),这类对象的作用在于,划分行动路径,把难以一步到位的请求分割成多个可一步到位的请求,把这些可以一步到位的请求称之为TemRequest。本次设计中,我引入了Person类,其保存了乘客的楼层位置信息TemRequest请求是否分配给电梯等信息。前文提到,我们需要实现对请求的分割,RequestDivider所完成的即是这一工作。至于识别请求可否一步到位,这个任务交给了RegionJudger来完成。

技术图片

技术图片

此次设计中,各个线程在没有Dispatcher权限的时候,甚至无法给自己下达指令,无法改变自己的状态。这么设计看似有丶违背常理,但是这么做有一个好处,我可以保证线程在Dispatcher权限下查询到的各个电梯的信息,在完成此次任务分配时都一定时有效的信息,即,与此时各个电梯的状态完全一致。

第三单元

技术图片

本次作业我采用的是分层Floyd的算法。由于这次需要计算的最短路径种类较多,每个各写一个Floyd过于麻烦,所以我实现了一个计算类FloydHelper用于封装Floyd算法。此次作业由于处于各种ddl的并发期,我没能很好地去设计架构,更多的是直接一股脑地塞进了一个SillyRailwaySystem类中,使得这个类的长度几近500行,这是设计上的失败。

第四单元

基于UML本身的树形结构,我的设计基本上是基于其原本的树形结构进行设计的,故结构相对有条理。前文已经叙述过了,此处不再赘述。

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

在第一单元第一次作业我再次使用C++搭建评测机的搭到心态爆炸式,我就意识到,我需要一个可以复用、快速改装的评测机模板,于是我就搭建一套评测机体系。

评测机架构

  1. 数据生成器
  2. 待测程序
  3. 标准程序
  4. Judge
  5. 结果存储

这其中,每次作业都可以复用的是待测程序,标准程序的调用方法,结果存储的文件结构。

那么,为了代码复用,简化开发流程(留下了每次用C++从头rush评测姬的血泪)我利用java的继承和多态机制

实现了快速改装的评测姬。

下面简要介绍一下一些可能不那么丑的设计思路

1.文件结构
技术图片

评测姬具有初始化文件夹与评测日志的方法。

每个从者对应一个文件夹,Ruler作为标程。

每个文件夹下存放对应从者的代码文件夹,以及各种测试数据的文件夹

技术图片

各种测试数据的文件夹内存放着对应数据的测试结果以及统计信息。

2.测试数据生成器

测试数据生成器实现一个统一的“供弹”接口,使得我们的评测姬可以选择不同类型的测试数据。

这么做至少有一个好处,比如说,一个人的程序会在幂的整数带有正号的情况下崩溃,但是你又想找到他运算的BUG。那么,就可以为他设计一个幂不带正号的测试数据,从而便于区分非同质BUG。

JUnit使用

在第三单元中,官方推荐了JUnit这个工具,本人也尝试了一下,其功能确实强大,但是那段时间比较忙,没有时间深入学习,甚是遗憾。

评测机遇到的问题

  • 输出时间戳与自己设定输入时间间的误差影响评测

    解决方案

    1、根据第一条输出的时间与第一条输入的时间进行误差修正,同时放宽对时间的判断的限度,加入0.05s的允许误差。

    2、使用hdl封装的接口,据说用这个可以基本消除这个误差,orz

  • 管道被写满

    之前的评测由于输出较短,一般不会出现这种情况,所以本人没有意识到这个问题,第七次作业中,本人给自己的代码对拍时,发现会各种TLE,后来发现是管道写满了,写入被阻塞,导致TLE

    解决方案

    在不断轮训等待“输入时间窗口”的时候判断被测程序是否往管道中写入了信息,如果写入就读出保存。

    在Java可以通过ImputStreamavailable()方法判断管道内是否已经被写入了信息(如果被写入,那么available != 0)。

  • 使用Java对调用输出流的println一类的方法后,待测程序没有接收到

    解决方案

    随手flush是个好文明。

  • 遇到死锁的程序判处其TLE并防止它继续耗费资源

    解决方案

    不断通过sleep轮训process.isAlive()与是否超过最大时限,如果超过最大时限(轮训结束,且process.isAlive()为真)则判处TLE,同时杀进程。

    关于杀进程

    ubuntu16.04:直接process.destroy()可以把整个进程给杀干净。

    win10:仅仅通过process.destroy无法杀死整个进程,可以通过taskkill来杀死超时的进程。

  • 关于CPU时间的测试

    解决方案

    水群里一位同学的方案,使用time命令可以查询CPU时间,本人尚未亲自尝试,下次窝窝作业可以一试。

    顺便,time命令好像win没有诶,哭哭。

  • 第四单元中我的对拍器出现了神必问题。在往被测程序的进程中写入时会卡死,我认为可能是缓冲区写满的问题,但照理来说,被测程序也一直在读取,不应该出现这个问题。

    解决方案

    直接改成命令行重定向输入,从文本输入。问题得以解决。

小黄鸭

在检查自己bug的时候,我认为,最有效的办法是反复地读、梳理自己的代码思路,从计组到OO,我都是靠这个方法保证 0 bug的。

学习收获

技术图片

  • 快速改装的评测机
  • 良好的代码风格
  • 优秀的重构能力
  • 良好的层次设计
  • 良好的规格设计
  • UML的深入理解
  • 从各个地方找灵感的骚操作
  • 增强了人际交往能力
  • 增强了小黄鸭debug时保持心平气和的能力
  • 增强了熬夜能力

改进建议

  1. 建议更改第四单元模式

    当前第四单元问题是在烤漆学习一个比较复杂的规则还要再完成一些不那么简单的代码任务,个人认为不太合理。建议第四单元的UML学习应该有一个官方的指导手册,将各个查询规则予以明示,不然大家自己摸索出来的结果,自己也不敢确认,也要找助教、老师确认,这种挤牙膏式的工作学习方式,过于低效。

  2. 建议实验课内容降低难度

    当前实验课的问题在于,上午教,下午用,如果没有大量的提前预习,将难以完成,所以建议降低上机难度。

  3. 加大寒假作业难度

    目前看来,寒假作业的难度仅仅起到了让大家学会Java最最基本的内容,这对于OO学习是远远不够的,建议课程组加大寒假作业难度。

    个人认为可以在输入解析处理和基本的多线程操作上入手。

以上是关于OO终章的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud 教程 | 终章

bbs终章(总结)

Angular: 属性装饰器ViewChild终章

终章——软工提问回顾与个人总结

设计模式终章----手写IOC容器

禅道试水终章