OO 第四单元总结

OO第四单元总结

架构设计

第四单元作业是对UML图进行解析,并支持若干种对图中元素的查询和模型有效性检查。

官方包已经对输入数据进行了预处理,将UML图中元素存储在UMLElement的不同子类中传入MyImplementation类。但官方包的类,如UmlClass类中没有存储属性、操作、父类等的字段,而这些又是查询时需要的,所以我对官方包中的每一种元素对应建立了自己的类,这些具有共同的父类MyUmlElement,并添加了一些字段,用于存储需要查询的信息。

public class MyUmlClass extends MyUmlElement implements MyUmlClassOrInterface {
   private final UmlClass umlClass;
   private final Visibility visibility;
   private MyUmlClass parent;
   private final ArrayList<MyUmlClass> subClasses;
   private final ArrayList<MyUmlInterface> interfaces;
   private final ArrayList<MyUmlAttribute> attributes;
   private final HashMap<String, ArrayList<MyUmlOperation>> operations;
   private int operationCount;
   private final ArrayList<MyUmlAssociationEnd> endsOnTheOtherSide;

  ...

}

现在的问题是如何将这些信息添加到MyUmlClass这样的类中。由于UML图中的元素是通过parentId、referenceId等方式表现其中的关系,所以需要保存id与元素类之间的关系。另一个问题是UML图元素传入的顺序不能保证,有可能存在解析一个元素时,其parentId对应的元素还没有出现的情况。因此解析元素的过程中,需要进行两次遍历。这两次遍历均在一个单独的类MyUmlParser类中进行,解析后再传入MyImplementation类中。

第一次遍历由newElements方法完成,不解析元素,只对每个UmlElement建立相应的MyUmlElement类,并存入一个以id为键的HashMap中。由于第三次作业中R007需要考虑元素传入的顺序,另外声明了一个名为umlElementsInOrder的ArrayList记录元素传入的顺序。

  private final HashMap<String, MyUmlElement> umlElements;

  private void newElements(UmlElement... elements) {
       for (UmlElement element: elements) {
           MyUmlElement myUmlElement = null;
           if (element instanceof UmlClass) {
               myUmlElement = new MyUmlClass((UmlClass) element);
          } else if (element instanceof UmlInterface) {
               myUmlElement = new MyUmlInterface((UmlInterface) element);
          } else if (element instanceof UmlAttribute) {
               myUmlElement = new MyUmlAttribute((UmlAttribute) element);
          } else if (element instanceof UmlOperation) {
               myUmlElement = new MyUmlOperation((UmlOperation) element);
          } else if (element instanceof UmlParameter) {
               myUmlElement = new MyUmlParameter((UmlParameter) element);
          }
           
          ...
           
           if (myUmlElement != null) {
               umlElements.put(element.getId(), myUmlElement);
               umlElementsInOrder.add(myUmlElement);
          }
      }
  }

第二次遍历由parseElements完成。按照umlElementsInOrder的顺序遍历所有元素,通过parentId等将各元素联系起来,并存储一些需要查询的元素。对每种元素均建立了一个parse*()方法,统一由parseElements()调用。

   private void parseElements() {
       for (MyUmlElement element: umlElementsInOrder) {
           if (element instanceof MyUmlClass) {
               parseClass((MyUmlClass) element);
          } else if (element instanceof MyUmlInterface) {
               parseInterface((MyUmlInterface) element);
          } else if (element instanceof MyUmlAttribute) {
               parseAttribute((MyUmlAttribute) element);
          } else if (element instanceof MyUmlOperation) {
               parseOperation((MyUmlOperation) element);
          } else if (element instanceof MyUmlParameter) {
               parseParameter((MyUmlParameter) element);
          }
           
          ...
           
      }
  }

例如,parseClass()方法将MyUmlClass存入classes中。classes是一个以类名为键,以存储了所有同名类的ArrayList为值的HashMap(需要按名查询的元素均按这种方式存储),并检查R001以及更新classCount。

   private void parseClass(MyUmlClass umlClass) {
       if (isEmptyName(umlClass.getName())) {
           emptyNameFlag = true;
      }
       ArrayList<MyUmlClass> sameNameClasses =
               classes.computeIfAbsent(umlClass.getName(), k -> new ArrayList<>());
       sameNameClasses.add(umlClass);
       classCount++;
  }

当需要通过parentId连接元素间的关系时,以在类中添加操作为例:

首先在MyUmlClass类中增加一个addOperation方法,然后在parseOpernation方法中在umlElements中按umlOperation的parentId找到其所属的MyUmlClass,并调用MyUmlClass的addOperation方法。其他元素的关系处理也都大同小异。按这种方法就可以将UML图中的元素联系起来。

   private void parseOperation(MyUmlOperation umlOperation) {
       if (isEmptyName(umlOperation.getName())) {
           emptyNameFlag = true;
      }
       MyUmlElement umlElement = umlElements.get(umlOperation.getParentId());
       if (umlElement instanceof MyUmlClass) {
          ((MyUmlClass) umlElement).addOperation(umlOperation);
      }
  }

MyImplementation类的构造方法中,构造一个MyUmlParser并将UML元素传入,MyUmlParser负责解析元素和处理部分便于在此时处理的有效性检查(如R007),然后将解析后的信息传入MyImplementation。MyImplementation类中直接存储的只有所有的UmlClass、UmlInterface、UmlStateMachine、UmlInteraction,其他UML元素存储在上述四种元素对应的类中。查询UML图中元素和部分不在MyUMlParser中的有效性检查由MyImplementation调用其中存储的元素中的相关方法完成。

 

架构设计思维及OO方法理解的演进

第一单元中还没有面向对象的思想,几乎完全是照着试验代码模仿的,不能体现出对面向方法的理解。第一单元的代码只能说是可以完成任务,但编写时没有太多关于架构思考。

第二单元的重点在于多线程,大体上的架构主要是套用流水线模式、生产者消费者等模型。但由于太过在意架构上的一些问题,影响了电梯调度方式,导致电梯性能不佳。

第三单元中,由于有官方包的限制,大体上的架构已经确定,没有太多可以自由发挥之处。

第四单元中,编写代码前首先进行了很多思考,得到了一个自己比较满意的架构,且是所有单元中最好的一个架构,低耦合高内聚相比于前三个单元做的比较好,解析UML过程中两轮遍历的设计非常实用,后续作业的迭代也比较方便,不需要做太大修改。我切身感受到了良好的架构对于迭代开发的益处。

 

测试理解与实践的演进

第一单元中,使用python编写测试数据生成程序随机生成测试数据,并手动构造了一些边界数据和针对容易疏忽的情况的的数据,并用sympy库检验正确性。成功地找到了自己和别人的bug。

第二单元的电梯调度程序调度方法因人而异,而且有多线程的随机性,难以检验正确性,手动构造数据难以覆盖所有情况。最后疏忽了测试环节,导致程序出现了大问题。

第三、四单元中输出确定,输入数据比较容易构造,可以自动化生成数据和别人对拍。也可以根据JML或要求的查询内容手动构造数据。

 

课程收获

逐渐形成了面向对象的编程思维。学会了java中的一些基本机制和常用的类的使用方法以及一些设计模式,代码能力有所提高。

 

建议

由于刚开始时对java还不太了解,第一单元压力比较大。对此可以适当减轻第一单元作业的难度或增加pre的内容。

增加实验提交时的反馈以及公布实验得分。

理论课中学习的一些内容没有在代码作业中练习到,可以在每单元的训练中增加相关内容。

posted @ 2022-06-28 12:13  leaqcsxpny  阅读(150)  评论(0编辑  收藏  举报