BUAA_2022_OO_Unit4_Summary

BUAA_2022_OO_Unit4_Summary

长风破浪会有时,直挂云帆济沧海。

第1章 第四单元架设计

任务总览

本单元要求实现一个UML解析器。主要分为类图、顺序图、状态图,同时根据简单规则检查UML图中可能出现的问题。

预处理

由于本次作业元素的输入顺序随机,而许多元素之间存在依赖或者父子关系,所以只按照元素顺序输入会导致某些元素无法找到他的parent或者关联元素,因此需要自顶向下进行解析。具体的,我主体分为以下三个层次进行解析:

 

所属第一层第二层第三层第四层
class UmlClass、UmlInterface、UmlAssociationEnd UmlAttribute、UmlOperation、UmlGeneralization、UmlInterfaceRealization、UmlAssociation UmlParameter
sequence UmlCollaboration UmlInteraction UmlParameter、UmlLifeline、UmlEndpoint UmlMessage
stateMachine UmlStateMachine UmlRegion UmlState、UmlPseudostate、UmlFinalState、UmlTransition UmlEvent、UmlOpaqueBehavior

需要注意的是,由于代码规格的限制,一方面我们可以将上述解析过程拆分成四个解析方法,一方面也可以通过引入单例的Parser类存储解析结果,MyImplementation将其作为自己的内部属性,需要时进行查询。

同时在储存这些元素的考虑上,一方面将不同的UML元素进行封装,增加一些新的属性和方法,构建同属元素之间的图结构,一方面采用HashMap(按ID快速查询)、HashSet(快速查询是否存在)构建全局存储关系,方便在访问时快速的的查找和删减。

具体的封装思路如下:

class diagram

MyClass

封装UMLClass类,主要作用是查询有关类图时可以快速的定位到特定元素以及其相关联的元素。主要用法:

  • 属性:类的操作个数、操作可见性、操作耦合度、属性耦合度、R002(不能含有重名元素);

  • 关联:类的子类数目、实现的全部接口、继承深度、所有相关联的UmlAssociationEnds、R003(不能含有重复继承)。

    为此MyClass主要有以下组成:

复制代码

public class MyClass {
private final HashMap<String, UmlAttribute> attributes; //属性
   private final HashMap<String, MyAssociationEnd> associationEnds;//相关联的UmlAssociationEnd
   private final HashMap<String, MyOperation> operations; //方法
   private MyClass father; //父类
   private final HashMap<String, MyClass> sonClasses; //子类
   private final HashMap<String, MyInterface> interfaces; //直接实现的接口
   private final HashMap<String, String> interfaceList;   //所有实现的接口
   public void getInterfaceList(HashMap<String, String> names) {}
   public int getDepthOfInheritance() {}
   public int getAttributeDegree(ArrayList<ReferenceType> tmp, String classId) {}
   public HashSet<AttributeClassInformation> checkForUml002() {}
   public HashSet<UmlClassOrInterface> circled() {}
}

复制代码

MyInterface

属于UmlInterface的继承,与UmlClass等级相同。主要使用方面:

  • 属性:成员UmlAttribute,

  • 关联:继承的所有接口、R003(检查循环继承)、R004(检查重复继承)。

public class MyInterface {
private HashMap<String, UmlAttribute> attributes = new HashMap<>();         //属性
   private HashMap<String, MyAssociationEnd> associationEnds = new HashMap<>();//associationEnd
   private HashMap<String, MyOperation> operations = new HashMap<>();          //方法
   private HashMap<String, MyInterface> fatherInterfaces = new HashMap<>();    //所有父接口
   private HashMap<String, MyInterface> sonInterfaces = new HashMap<>();       //所有子接口
public boolean hasDuplicatedGeneralization(HashMap<String, String> interfaceList) {}
public boolean isNotAllPublic() {}
public HashSet<UmlClassOrInterface> duplicatedGeneralization() {}
public HashSet<UmlClassOrInterface> circled(HashMap<String, MyInterface> interfaces) {}
public void dfs(MyInterface start, HashMap<String, Boolean> used) {}
}

MyOperation

属于UmlOperation的继承,MyClass的下属元素。使用范围如下:

  • 属性:操作可见性、操作耦合度;

  • 关联:操作等价性判断、元素正确性判断。

为此MyOperation主要有以下组成:

public class MyOperation {
private final HashMap<String, UmlParameter> parameters;
private boolean hasReturn;
public boolean hasWrongParameter() {}
public boolean isSameOperation(MyOperation other) {}
public int getOperationDegree() {}
}

sequence diagram

MyInteraction

继承UmlInteraction,为UmlCollaboration子元素。在命令中,需要用到它的UmlLifeline、UmlMessage、UmlEndpoint等元素。设计如下:

复制代码

public class MyInteraction {
private final HashMap<String, MyLifeline> lifelines = new HashMap<>();
   private final HashMap<String, UmlLifeline> initLifelines = new HashMap<>();
   private final HashMap<String, String> lifelinesWithName = new HashMap<>();
   private final HashMap<String, MyMessage> messages = new HashMap<>();
   private final HashMap<String, UmlEndpoint> endpoints = new HashMap<>();
public void addMessage(MyMessage message) {}
}

复制代码

MyLifeline

继承UmlLifeline,为MyInteraction的下属元素,主要为了实现给定 UML 顺序图和参与对象,找出能创建该参与对象的另一个参与对象、收到了多少个 Found 消息,发出了多少个 Lost 消息、检查R007、R008等,具体设计:

public class MyLifeline {
 private final HashMap<String, MyMessage> foundMessages = new HashMap<>();
   private final HashMap<String, MyMessage> receivedMessages = new HashMap<>();
   private final HashMap<String, MyMessage> lostMessages = new HashMap<>();
   private final HashMap<String, UmlLifeline> creators = new HashMap<>();
public boolean deletedThenReceive() {}
}

封装后很多消息被赋予了特殊的意义,如收到的消息、发出的消息、创造消息等等,这样这些消息实际上在解析式就可以归类,查询时可达到o(1)的效果。

MyMessage

继承UmlMessage,属于MyLifeline之间的关联,设计此类主要为了解决消息的时间逻辑,方便R007的查询。

其余元素封装完全是为了出于方便因素,无实际价值。

state diagram

MyStateMachine

继承UmlStateMachine,状态图里的最高级。主要为了建立与region之间的父子关系,实际上没有这个类也可以,因为StateMachine和Region的一一对应关系,但是出于层次化结构设计、完整度之间的考虑,我认为是有必要设计的。

复制代码

public class MyStateMachine {
   private MyRegion region;
}

复制代码

MyRegion

继承UmlRegion,MystateMachine的子元素。主要存储所有的状态节点和迁移,方便指令的查询。用处:

  • 属性:状态的数量;

  • 关联:判断关键状态(去除想应节点前后的DFS)、找出所有迁移的触发事件、R009查询等。

主要设计如下:

public class MyRegion {
private HashMap<String, MyState> ends = new HashMap<>();
   private HashMap<String, MyState> states = new HashMap<>();
   private HashMap<String, MyTransition> transitions = new HashMap<>();
   public boolean stateIsCriticalPoint(MyState myState) {}  //检查是否为关键节点
}

 

Mystate

UmlState/UmlPseudostate/UmlFinalState的统一,MyRegion的子元素。命令有关操作需要知道一个状态能状态到哪些状态、有哪些UmlTransition转出。所以储存节点的种类、后续节点、传进来的迁移、传出去的迁移等关键信息。还可用于R009的检查。

public class MyState {
   private Integer type;
   private HashMap<String, MyState> nextStates = new HashMap<>();
   private HashMap<String, MyTransition> fromTransitions = new HashMap<>();
   private HashMap<String, MyTransition> toTransitions = new HashMap<>();
public boolean checkAmbiguous(HashMap<String,MyTransition> transitions) {}
}

 

MyTransition

继承UmlTransition,为MyState之间的关联,主要为了方便建立events的所属关系。

public class MyTransition {
private String guard;
private HashMap<String, UmlEvent> events = new HashMap<>();
private UmlOpaqueBehavior opaqueBehavior;
}

 

综上可以完成整个UML图的构建,总体上采图+缓存的结构。

 

第2章 架构设计思维及OO方法理解演进

第一单元

第一单元为表达式的解析和化简。表达式本身有较强的层次结构,所以我们在解析表达式的同时建立起层次结构设计意识。

这是我们首次系统地接触面向对象编程,要求我们首先根据表达式、项、因子之间的逻辑关联建立起层次结构,中间借助工厂模式、单例模式、解析器等方法搭建系统框架,同时在作业迭代中思考代码的可拓展性、可维护性,这让我意识到自己的代码已经达到小项目级别,需要我们有较强的逻辑思维和维护能力。

 

第二单元

第二单元为多线程电梯调度。强调线程之间的互斥和同步,需要我们对锁机制和调度策略有较为深刻的理解。

锁机制的了解旨在要求我们对正确的、适当的共享资源进行上锁,如果不加思考上锁则导致电梯运行过慢,多线程退化为类似单线程,而关键资源如果不上锁会出现线程不安全的情况。同时除了synchronized以外尝试使用了更加灵活的不同种类的lock,及对应的用condition唤醒的方法。

对于电梯调度的思考,也需要根据电梯线程的特点进行设计,要求满足效率不低于标准设计、符合电梯运行逻辑、可正常退出的原则,因此合理的调度算法设计也是较为重要的一部分。

从单部电梯到多层多栋横竖动态增加电梯,每次作业都在上一次基础上进行拓展,实现了代码复用。

 

第三单元

第三单元为根据JML规格编写代码。要求我们熟悉掌握JML规格语法,设计出符合性能要求且完全正确的代码。

掌握JML规格,首先需要我们明白JML的语法和各种限制,学会根据JML规格写出符合要求的正确代码,也尝试自己通过代码编写相应的JML,体会到了规格化设计的重要性。

此外Junit单元测试也要求我们学会更为精细化的测试。

当然算法部分不是本单元的测试重点,但是实际工业上的代码时要求性能的,因此在符合规格要求喜庆便携性能良好、拓展性强的代码,也是我们应当培养的能力。

 

第四单元

第四单元主要是实现UML的解析器。要求我们了解UML元素之间的联系和相应规则的检查。

清晰了解各元素之间的关系后,需要我们建立层次结构来解决相应的问题,同时利用图和缓存结构实现快速定位和递归操作。通过整体的架构设计,我们更清晰的了解UML元素之间的关联和合作,掌握的更加透彻。

规格的检查让我们了解行业内对于UML图的限制和要求,在设计检查方法时,我们也初步了解了UML图所要符合的约束关系。

 

总结

  • 架构设计是为了将复杂的整体需求拆分成一个个可以精细化解决的模块。拆分过程也需要遵循一定的规则和逻辑,如从功能性、父子性、关联性考虑。

    • 开发时可以化多为一,针对性地就一个子任务集中精力;

    • 测试时可以模块单独测试,快速发现问题;

    • Debug时可以快速定位,避免满篇查找;

    • 迭代时可以更具需求增删模块,减少重构风险。

  • 面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。通过一学期的学习,我发现面向对象编程的优势:

    • 易维护: 采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。

    • 质量高:在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。

    • 效率高:在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。

    • 易扩展:由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。

 

第3章 测试理解与实践的演进

白盒测试与黑盒测试

测试主要分白盒测试与黑盒测试。白盒测试需要你对系统内部的结构和工作原理有一个清楚的了解,并且基于这个知识来设计你的用例,在黑盒测试中,被测对象的内部结构,运作情况对测试人员是不可见的,测试人员对被测产品的验证主要是根据其规格,验证其与规格的一致性。

白盒测试需要仔细阅读程序员的代码,掌握他们的结构和漏洞。由于时间有限,且代码量较大,导致我在互测中很少使用。 黑盒测试只需要构造评测机和测试数据,然后多个小伙伴进行对拍就大功告成。但是一方面由于数据生成的随机性,且往往不符合测试点限制,需要花费较多时间在测试生成逻辑上。同时测试数据针对性不强,往往需要手动构造特定刁钻数据去Hack其他人,比较耗时。然后一般没有标准的答案输出,因此仅靠对拍很难完全保证结果的正确性和复现性。

作业测试

第一单元是表达式解析,采用递归法进行数据构造,并手造一些极端数据正确性检验可通过python的sympy库进行,主要采用黑盒测试。 第二单元是多线程,数据生成随机构造,当然也需要手搓一些较为极端的数据,正确性检验需要凭借状态机逻辑来判定输出的合法性。但是由于测试结果较难复现,所以评测的收益很低。 第三单元是JML规格编写。数据可根据不同指令的组合进行构造,实际上难度较低。正确性检验只能依靠小伙伴的代码对拍。由于整体架构已经给出,尝试了白盒测试方法,收益较高。 第四单元为UML图,数据编写既可以手搓UML图,通过官方测试包转化为标准输入,也可以直接根据输入规格,随机构造,正确性检验只能依靠小伙伴的代码对拍。无互测,不能体现收益。

感想

  • 黑盒测试为整体检查,所有参与成员可以统一检查,期望效益最高,但是依赖于强数据的生成能力。

  • 白盒测试针对性强,培养阅读代码的能力,但是需要耗费大量精力,而且在小组参加的测试中需要逐一比对,收益很低。

  • 测试能力培养非常关键。写出代码往往比验证代码简单得多,四个单元的测试工作,极大的培养了我针对具体要求构造专门测试数据的能力,同时也增强了我自己发现错误的能力,对于我以后架构的设计和代码的书写有着重要的帮助。

 

第4章 课程收获

  • 学习了课程要求掌握的面向对象程序设计方法、多线程编程、规格化编程、uml建模等知识;

  • 学会了递归下降(parser),为以后写编译器奠定基础;

  • 了解了多线程的互斥和同步,对于操作系统理论知识的巩固;

  • 使用各种设计模式辅助架构设计,如第一单元的工厂模式、第二单元的单例调度器、实验课的流水线架构、理论课的迭代器模式,对优秀的架构有了更深的理解;

  • 掌握有关图的结构和算法;

  • 规范了代码风格,能够主动按照代码规格要求书写代码;

  • 对于java独有知识如JVM、容器的底层逻辑有了非常深刻的了解,掌握了java代码编写的规范和要求;

  • 熟悉了面向对象编程方法,并尝试主动运用到项目结构的设计中去。

  • 掌握了自动化测试程序的代码编写,对测试流程和数据构造有了更深的理解;

  • 收获了很多痛苦和快乐,建立了很多友谊。

 

第5章 改进建议

  • 预习部分引入第一单元的学习,并给予更多时间预习,方便同学们快速的适应oo的紧张环境。

  • 实验课应该有及时反馈,不能让实验课的作用仅体现在后续设计中提供模版,实验课上的实验代码理解也是很重要的,因为程序员往往不愿意阅读别人的代码,现在实验课给予了我们这种机会,应该将反馈结果解释给我们,让我们可以发掘自己的理解是否有失偏颇;

  • 中测提供额外的较强测试点,供同学的调试,这样不仅节省了同学们大量无效debug的时间,还可以提高oo整体的分数水平(bushi;

  • 用专题展示帖和专属讨论区代替研讨课。根据我自己的亲身感受,研讨课的展示效果并不明显(可能原因在于同学们很难在短时间内把握分享者的主体架构和思想),小组讨论让大家积极交流,但实际上有时候大家的重点不在于讨论的过程,而仅仅是自己可以在下次设计中怎样处理,这样偶尔会导致讨论中心不够明确,同学的观点也比较混乱,难以达成统一。

 


鸣谢:oo课程组和助教们的无私奉献;学长积极的提供思路的查找错误;同学的互相测试和讨论。



posted @ 2022-06-27 19:41  `Demon  阅读(46)  评论(0编辑  收藏  举报