BUAA-OO第四单元:UML建模
第四单元主要是UML建模语言,体会正向建模的过程与意义,在之前的单元中,我们只是将UML作为一种辅助工具,更多的是先写代码之后画类图等,而第四单元采用正向建模的思想,我们在完成作业时应先做好相应的设计,绘制UML类图、顺序图、状态图等后再实现具体代码,如有变化再回来更新UML图,真正体会工程化建模的必要性。
一、正向建模与实践
正向建模是一种软件开发方法,它遵循从需求到设计再到实现的顺序。在本单元实践中,我们通过以下几个步骤来完成正向建模与开发:
- 需求分析:首先,需要对软件的需求进行分析,确定软件需要实现的功能和性能指标。
- UML类图:使用统一建模语言(Unified Modeling Language, UML)中的类图来表示系统中的类、它们的属性、方法以及类之间的关系。类图帮助开发者理解系统的静态结构。
- 状态图:状态图是UML中用来表示对象在生命周期内可能处于的状态以及在状态之间转换的条件。这有助于设计状态机,比如在用户界面或协议处理中。
- 顺序图:顺序图展示了对象之间交互的时间顺序,它显示了对象如何通过消息传递进行通信。顺序图有助于理解系统动态行为和时间依赖性。
- Java代码实现:在设计阶段完成后,根据UML图来实现具体的Java代码。这包括编写类定义、方法实现以及处理类之间的关系。
- 代码审查与测试:在实现阶段之后,进行代码审查以确保代码符合设计规范和质量标准。然后进行单元测试和集成测试,确保软件按预期工作。
- 迭代与改进:软件开发是一个迭代过程,根据测试结果和用户反馈进行必要的调整和改进。
通过这个流程,我们可以确保软件开发过程中的每一步都是有计划和有组织的,从而提高开发效率和软件质量。
二、第四单元架构设计
1. UML图
因三次作业的迭代之间改动并不大,因此这里以十五次作业的类图、状态图来表示。
类图
状态图
顺序图
由于过程较多,这里以预订书籍的顺序图为例(ordered()):
2. 代码迭代
第十三次作业
本次要求实现一个图书管理系统,能实现图书的余量查询、借阅、预约、取书、返还、开闭馆整理等操作,本次作业中的架构已在上述类图中描述过,下面进行详细的表述:
Library类
本次作业的主体类,可以接受指令并对指令进行处理,在对指令进行处理时,我们会用到借阅处(bro)、书架(bookShelf)、预约处(ao)等对象作为内部属性,这也构成了关联关系,即类图中的箭头,因此画出相应类图后,我们可以实现相应的代码 :
//Library.java
public class Library {
private LibraryPrinter printer = PRINTER;
private HashMap<String, Student> studentTable;
private BookShelf bookShelf;
private Bro bro = new Bro();
private Ao ao = new Ao();
private Arrange arrange;
private LocalDate date = null;
//...
public void dealCommand(LibraryCommand<?> command) {
if (command.getCmd() instanceof LibraryRequest) {
LibraryRequest request = (LibraryRequest) command.getCmd();
LibraryRequest.Type type = request.getType();
switch (type) {
case QUERIED:
queried(request);
break;
case BORROWED:
borrowed(request);
break;
case PICKED:
picked(request);
break;
case ORDERED:
ordered(request);
break;
default:
returned(request);
break;
}
} else if (command.getCmd() instanceof String) {
String string = (String) command.getCmd();
if (string.equals("OPEN")) {
this.open(command.getDate());
} else if (string.equals("CLOSE")) {
this.close();
}
}
}
//... 具体实现函数
}
Student类
为了更方便地记录学生借阅数量的情况,构建了Student
类,类中包括学生是否借阅了某类书以及借阅书籍的相关信息。即Library
类中的studentTable
属性。
Bro/BookShelf/Ao类
分别对应借阅处(Bro
类-borrow and return office
)、书架(BookShelf
类)、预约处(Ao
类-appoint office
类),三者的属性类似,都包含了库存以及书籍的增减操作,这里经过思考其实利用继承的关系实现更能体现高层逻辑的抽象表示,不过这次作业并没有实现(不好意思,犯懒了),其中对于预约处还增加了BookAppoint
类,用于记录预约处中被预约的书籍。
Arrange类
作业要求在开闭馆后需要对处于借阅处、书架、预约处的书籍进行整理和搬运,这里采用构建与Library
类相关联的Arrange
类进行处理,选择只在开馆后进行整理,具体实现如下:
//Arrange.java
public class Arrange {
public Arrange() {
}
public List<LibraryMoveInfo> moveFromAoToBs(Ao ao, BookShelf bs) {
//...
}
public List<LibraryMoveInfo> moveToAo(BookShelf bs, Bro bro, Ao ao) {
//...
}
public List<LibraryMoveInfo> moveFromBroToBs(BookShelf bs, Bro bro) {
//...
}
public List<LibraryMoveInfo> arrange(BookShelf bs, Bro bro, Ao ao) {
List<LibraryMoveInfo> move = new ArrayList<>();
move.addAll(moveFromAoToBs(ao, bs));
move.addAll(moveToAo(bs, bro, ao));
move.addAll(moveFromBroToBs(bs, bro));
return move;
}
}
第十四次作业
本次作业增加了图书捐赠和漂流处的操作,学生可以捐献书,并在达到一定的借阅数量后可以转为正式书籍,只需加入一个Bdc
类(book drift corner图书漂流处)即可,同时需要调整Arrange
类,在Bro
类中加入了popular
内部属性来记录从漂流处转为正式书籍的图书,从而在整理过程中直接从借还处搬运到书架。
第十五次作业
本次作业增加了用户积分机制,具体要求如下:
用户信用分限制
所有用户的初始信用分为10,上限为20(即积分增加时,更新后信用积分=min(更新前信用积分+x,20)),可以引起信用分变化的情况如下:
- 用户每次还书期限内还书成功信用分立即+1,包括归还从书架上借阅的正式图书与图书漂流角内的非正式图书。
- 用户每次成功取到书籍(包括借书成功和取书成功),在该书应归还日期的当日闭馆后,若用户仍未归还图书,该用户信用积分-2。例如某用户应在1月31日及以前归还某书,而实际上用户在2月5日归还了该书,那么在1月31日闭馆后应立刻扣除该用户两个信用积分,而非2月5日用户还书时才扣除。
- 用户每次预约图书成功,且图书馆已经将图书为该用户送至预约处后,若该用户在规定的时间内未能取走该书,书籍逾期的时刻该用户信用分-3(即预约处图书逾期当日闭馆后立刻扣除信用分)。
- 用户捐献图书成功信用分+2,如果后续该图书成为图书馆的热门图书,在整理流程中被送往书架成为图书馆正式书籍,则该书升级为正式图书的时刻捐献该书的用户信用分额外+2。
当信用分为负时,用户发起的如下请求将不会成功:
- 从书架或图书漂流角借书。
- 预约图书。
- 续借图书。
具体实现:增加了Credit
类来记录每个学生的积分:
//Credit.java
public class Credit {
private HashMap<String, Integer> credit;
public Credit() {
this.credit = new HashMap<>();
}
public void addNewStu(String student) {
//...
}
public void changeCredit(String student, int number) {
//...
}
public int getCredit(String student) {
//...
}
}
三、架构设计思维的演进
第一单元:多项式化简
- 递归下降法:在这个阶段,我学习了如何使用递归下降法来解析和化简多项式表达式。这个过程让我理解了递归在问题分解中的强大作用,以及如何将复杂问题转化为更小的、可管理的子问题。
第二单元:电梯调度算法
- 算法优化:通过LOOK算法和影子电梯技术,我掌握了在多线程环境下进行资源调度和性能优化的方法。这不仅提升了我对并发编程的理解,也加深了我对算法在系统设计中重要性的认识。
第三单元:基于JML的规格程序设计
- 规范定义:JML的使用让我认识到了规范和契约在软件开发中的重要性。通过定义程序的正确性条件,我开始更加注重软件的规范性和可验证性,这有助于提高软件质量和可靠性。
第四单元:UML建模
- 可视化建模:通过UML,我学习了如何将抽象的概念和需求转化为可视化的模型。这个过程强化了我对系统架构的理解,以及如何使用模型来指导实际的代码实现和系统设计。
架构设计思维的演进
- 从具体到抽象:我的架构设计思维从具体的代码实现,逐渐转向了更高层次的抽象思考。我开始更加关注问题的定义、解决方案的可行性以及系统的整体架构。
- 模块化和组件化:我学会了如何将系统分解为模块和组件,以及如何通过接口和抽象类来定义它们之间的交互。这有助于提高代码的可维护性和可扩展性。
- 迭代和反馈:我认识到了迭代开发和持续集成的重要性。通过不断的测试和反馈,可以及时发现并解决问题,从而提高开发效率和产品质量。
通过这四个单元的学习,我的架构设计思维得到了全面的提升,从最初的问题分解到最终的系统架构设计,我都能够更加系统和深入地进行思考和决策。
四、测试思维的推进
第一单元:多项式化简
- 基础测试思维:在这个阶段,我通过递归下降法解析和化简多项式表达式,开始意识到测试的重要性。我学习了如何编写基础的单元测试来验证算法的每一步是否按预期工作,确保每个递归函数都能正确处理边界条件和异常情况。
第二单元:电梯调度算法
- 并发测试思维:在电梯调度算法的学习中,我面对了多线程环境下的测试挑战。LOOK算法的使用和影子电梯技术的应用,让我认识到了并发测试的重要性。我开始设计测试用例来模拟并发场景,确保算法在多线程环境下的正确性和性能。
第三单元:基于JML的规格程序设计
- 规范驱动测试思维:JML的使用让我将测试思维提升到了一个新的层次。我开始使用规格语言来定义程序的正确性条件,并学习了如何根据这些规范来设计测试用例。这不仅提高了测试的系统性,也让我更加关注程序行为的规范性。
第四单元:UML建模
- 模型驱动测试思维:在UML建模单元中,我通过正向建模与开发设计了图书管理系统。UML图的使用让我学会了如何从模型的角度来设计测试,识别潜在的测试场景和用例。这个阶段,我的测试思维从代码层面提升到了设计层面,开始考虑如何通过模型来指导测试的设计和执行。
五、课程收获
- 理解面向对象的核心概念:我深入理解了面向对象编程的三大基本特征:封装、继承和多态。这些概念帮助我构建了更加模块化和灵活的代码。
- 掌握类和对象的使用:我学会了如何定义类来创建对象,以及如何使用这些对象来模拟现实世界中的实体和行为。
- 提高代码复用性:通过继承机制,我能够创建基类和派生类,实现了代码的复用,减少了重复代码的编写。
- 增强了抽象思维能力:面向对象编程鼓励使用抽象类和接口来定义行为,这让我更加习惯于从宏观角度思考问题,提高了我的抽象思维能力。
- 学习了设计模式:我接触了多种设计模式,如工厂模式、单例模式、生产者-消费者模式等,这些模式提供了解决特定问题的通用模板,提高了我解决复杂问题的能力。
- 掌握了UML以及JML建模技巧:UML不仅帮助我可视化系统设计,还让我能够更好地与团队成员沟通设计意图和系统结构。JML让我体会到规格程序设计的过程,体会到工程化编程中层层递进,让测试人员以及编程人员都能阅读的规格语言。
口来定义行为,这让我更加习惯于从宏观角度思考问题,提高了我的抽象思维能力。
- 学习了设计模式:我接触了多种设计模式,如工厂模式、单例模式、生产者-消费者模式等,这些模式提供了解决特定问题的通用模板,提高了我解决复杂问题的能力。
- 掌握了UML以及JML建模技巧:UML不仅帮助我可视化系统设计,还让我能够更好地与团队成员沟通设计意图和系统结构。JML让我体会到规格程序设计的过程,体会到工程化编程中层层递进,让测试人员以及编程人员都能阅读的规格语言。
总之,面向对象编程课程不仅提供了一套实用的编程技术,更重要的是培养了我分析问题、设计解决方案以及实现高质量软件的思维方式。这过程中不甚完美也不乏遗憾,但确实是一段难忘的经历,这些收获将伴随我在未来的编程生涯中不断进步和成长。完结撒花~