222222

147 阅读35分钟

课程目标 12 对应选择填空判断问答题

课程目标 43 对应设计题

单选1010、填空1010、判断1010、问答45

论述152+102

 

  1. 考查目标

1.1  课程目标  1

➢软件生命周期活动****

按照软件生命周期,开发分为制定计划、需求分析、软件设计、程序编写、软件测试和运行维护等6 个基本活动。 

➢造成重构的主要原因 改进软件设计、使软件更容易理解、有助于查找错误

代码重复、代码结构问题、设计不足或变更、代码规范问题、性能和安全考虑、适应需求变化

重构是一个持续的过程,有助于保持代码的质量、可读性和可维护性,从而提高软件的整体质量和用户体验

软件重构与交付(单选题)(填空题)

代码重构是增量迭代开发不可或缺的技术,已经成为现代软件开发的基本技术,并在很多常用的 IDE 中成为标准模块。

代码重构就是在不改变软件外部行为的前提下改善它的内部结构。

代码重构主要有三个时机:

给程序增量地添加功能的时候、定位错误的时候、评审代码的时候

软件开发和软件维护这两个阶段之间的开发活动统称为软件交付。

其作用是让最终用户使用开发的软件。

软件交付的基本活动包括构建、打包、发布、安装和部署。

 

➢交互设计的基本原则

学习性。指的是新的用户能用它进行有效的交互并获得最大的性能。

灵活性。是指用户和系统能以多种方式交换信息。

健壮性。是指在决定成就和目标评估方面对用户提供的支持程度

➢异常处理的模式

终止模式和恢复模式。

终止模式:假设错误非常关键,导致程序无法返回到异常发生的地方继续执行。一旦抛出异常,就表明错误已无法挽回,也不能回来继续执行。

恢复模式;认为异常处理程序的工作是修正错误,重新尝试调用出问题的方法,并认为二次处理能成功。恢复模式希望处理异常后程序能继续执行。

try{

包含可能发生异常的语句

}

catch(Exception e){

处理异常的语句

}

finally{

类似分支语句中case的缺省情况default

}

➢软件开发过程

瀑布式开发过程、增量开发模型、个体软件过程

➢瀑布式开发过程

瀑布式开发过程,也叫软件生存期模型。

按照软件生命周期,开发分为制定计划、需求分析、软件设计、程序编写、软件测试和运行维护等6 个基本活动。

各项活动严格按照线性方式进行,自上而下、相互衔接。每项活动的结果需要验证通过后,才能继续进行下一项活动。

核心思想是按工序将问题化简,将功能的实现与设计分开,即采用结构化的分析与设计方法将逻辑实现与物理实现分开。

主要问题在于:

阶段划分僵硬, 每个阶段不能缺省, 而且产生大量文档, 增加了工作量;

开发是线性的, 只有等到整个过程的末期才能见到开发成果—— 可运行软件, 不利于快速响应变化的需求;

早期的错误要等到开发后期的测试阶段才能发现, 可能带来严重的后果, 增加了开发的风险。

➢模块化软件构造

模块化是把问题分解成容易理解、便于控制、便于实现的子问题的一个重要手段,是实现控制复杂性的方式。

模块化通过把一个程序分解成简单独立、互相作用的模块,对不同的模块设定不同的功能,来实现大型、复杂的程序。

模块是可组合、可更换的程序单元。良好设计的模块只完成一个特定的或一组相关的子功能。

所有模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。

程序没有明确清晰的“算式”、“习题”的含义,没有使用相应的变量、数据结构等程序设计的方式表达。

若干独立的功能集中在一个函数中不利于发现和修改程序中的错误,也不便扩充程序的功能。

程序修改后,每次都要重新设计一些检验的数据,运行程序观察结果,判断修改过的程序是否正确。我们希望能保留下检测数据、运行结果及其判断,无须改变地反复使用。

编程缺乏规范。

50 道100 以内的加减法口算习题。

循环50 次, 每次产生一个运算题: 随机地产生两个100 以内的整数, 随机地从两个运算符“ 加法” 和 “ 减法” 中挑选一个, 就可以生成一道口算题; 然后打印输出一套练习题。

//代码 2.1:一个 Java 程序,产生并逐行输出 50 道 100 以内的加减法算式package cbsc.cha2;
import java.util.Random;

public class BinaryOperation 0 {
public static void main(string[]args){
short m=0,n=0,ov=0;
char o='+';
Random random=new Random();
for(int i=0;i<50;i++){
ov=(short)random.nextInt(2);
m=(short)random.nextInt(101);
n=(short)random.nextInt(101);
if(ov==1){
o='+';
}else{
o='-';
}
System.out.println(""+(i+1)+":\t"+m+o+n+"=");
}
}
}

➢面向对象软件构造

“软件构造”指的是通过编码、验证、单元测试、集成测试和调试的组合,详细地创建可工作的、有意义的软件。

抽象与封装‌:通过抽象和封装提高代码的可读性、可维护性和可重用性,应对企业级应用复杂的业务逻辑和数据结构。

‌依赖管理‌:使用依赖管理工具管理大量第三方库和模块,确保版本兼容性。

‌多线程与并发处理‌:利用Java的多线程特性或第三方库提高并发性和应用程序吞吐量。

‌测试与调试‌:通过彻底的测试和调试确保企业级应用的可靠性。

‌定制化解决方案‌:根据企业业务需求创建定制化软件,实现业务集成、数据安全和隐私保护等。

面向对象软件构造是一种程序设计方法或规范,它使用对象、类、继承、封装、多态等基本概念来进行程序设计。以下是对面向对象软件构造的详细解释:

一、基本概念

1.对象(Object):

对象是系统中用来描述客观事物的一个实体,是构成系统的一个基本单位。

对象由一组属性和对这组属性进行操作的一组服务组成。属性即对象的状态,服务即对象的行为。

对象的生命周期包括生成、使用和消除三个阶段。当不存在对一个对象的引用时,该对象成为一个无用对象,Java的垃圾收集器会自动将其回收。

2.类(Class):

类是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述。

类包括属性和方法两个主要部分。属性即字段(Fields),方法即函数(Methods)。类的方法是它的应用程序编程接口(API),定义了用户如何与实例交互。

在Java中,类实现包括类声明和类体。类声明中包括修饰符、类名、父类名和接口列表等。类体中包括成员变量和方法等。

3.接口(Interface)

接口在Java编程语言中是一个抽象类型,用于设计和表达ADT(抽象数据类型)的语言机制。

接口是抽象方法的集合,只有方法名,没有方法的实现。一个类通过继承接口的方式,来继承接口的抽象方法。

一个类可以实现多个接口,但一个类只能继承一个抽象类(在Java中)。接口无法被实例化,但是可以被实现。

二、面向对象编程的不同特征

1.封装(Encapsulation)

封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

设计良好的代码隐藏了所有的实现细节,干净地将API与实施分开,模块只能通过API进行通信,对彼此的内在运作不了解。

封装的好处包括将构成系统的类分开,减少耦合;加快系统开发速度;减轻维护的负担;启用有效的性能调整;增加软件复用等。

实现封装的方法包括修改属性的可见性来限制对属性的访问(一般限制为private),并对每个值属性提供对外的公共方法访问(即创建一对赋取值方法,用于对私有属性的访问)。

2.继承(Inheritance)与重写(Overriding)

继承是从父类继承得到父类的字段和方法,主要目的是为了代码复用。子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。重写的好处在于子类可以根据需要,定义特定于自己的行为。

在Java中,默认情况下方法是可重写的。子类只能添加新方法,无法重写父类中被声明为final或private的方法。当子类包含一个覆盖父类方法的方法时,它也可以使用关键字super调用父类方法。

3.多态(Polymorphism)

多态是指允许将子类类型的对象视为父类类型的对象使用。一个接口或父类可以引用子类对象,而调用的具体方法是子类实现的版本。

Java的多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)。

方法重载是指在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。方法重写则是指子类对父类的方法进行重新编写。

三、面向对象软件构造的实践

在面向对象软件构造的实践中,需要遵循一些原则来确保代码的质量、可读性和可维护性。这些原则包括:

单一职责原则:一个类应该只有一个引起它变化的理由,即一个类只负责一个功能领域中的相关职责。

开放封闭原则:软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改的。即对于扩展是开放的,对于修改是封闭的。

里氏替换原则:子类必须能够替换它们的基类而不会导致错误。这确保了使用基类的地方可以透明地使用子类对象。

接口隔离原则:使用多个专门的接口,而不是使用单一的总接口,来降低类之间的耦合度。

依赖倒置原则:高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

综上所述,面向对象软件构造是一种强大的程序设计方法,它通过使用对象、类、接口、继承、封装和多态等概念来构建软件系统。在实践中,需要遵循一些原则来确保代码的质量和可维护性。

敏捷开发(判断题)

敏捷开发是应对快速变化的需求的一种软件开发能力。以用户需求进化为核心,采用迭代、循序渐进的方法进行软件开发。

敏捷开发采用了更加人性化、个性化的沟通表达方式,特别是使用隐喻而非常规术语或形式化技术。

敏捷开发通过宣言表示了它的4 个核心价值观。

个体和互动胜过流程和工具。

工作的软件胜过详尽的文档。

客户合作胜过合同谈判。

响应变化胜过遵循计划

敏捷开发是一个统称,很多软件开发方法都认为自己是敏捷的。典型的敏捷开发方法包括极限编程(Extreme Programming,XP)、Scrum、水晶方法(Crystal)、特性驱动开发(Feature Driven Development,FDD)、动态系统开发方法(Dynamic Systems Development Method,DSDM)

1.2  课程目标

 2 ➢测试层次

为了能系统、全面地测试软件,测试可以先从程序的基本单元开始,然后按照一定方式——如软件集成的顺序,逐步测试集成后的程序,直至测试完成整个软件。

按照软件的构成,测试可以划分为4 个阶段或层次:单元测试、集成测试、系统测试和验收测试

(1)单元测试

对程序基本单元( 函数、方法、类或对象及构件或服务) 进行的测试。关注程序单元的基本功能、算法实现、数据结构等内部组织结构。

(2)集成测试

对两个及以上相互关联的程序单元测试。具有调用关系的函数、具有继承或聚合关系的类, 以及具有合作关系的子系统、软件使用或依赖独立的外部系统, 甚至是软硬件的交互。集成测试的重点是检测程序模块的接口、模块之间的交互及开发的软件与外部系统的交互。

(3)系统测试

对整个软件的测试称为系统( 级) 测试。重点是检测软件是否满足了用户需求、完成既定的功能和任务。同时, 还要检测运行速度、存储占用、事务处理、数据量及是否稳定、可靠、安全、易用、易维护等非 功能需求。

(4)验收测试

确保软件准备就绪, 最终用户可以在用户环境执行软件的既定功能和任务。验收测试是在产品发布之前进行的测试活动, 也称交付测试。

持续集成/持续测试(CI/CD)是一种技术,它允许开发人员在软件开发过程中自动执行测试,以快速发现和修复错误。在软件构造过程中,使用持续集成/持续测试可以帮助开发人员自动化地构建、测试和部署软件,从而提高开发效率

 

➢软件构造概念和重要性 (问答题)

提高代码质量、促进团队协作、降低 维护成本等。提高软件质量、缩短开发周期、降低维护成本、增强系统可扩展性以及提升团队协作效率。

“软件构造”指的是通过编码、验证、单元测试、集成测试和调试的组合,详细地创建可工作的、有意义的软件。

重要性:

构造是软件开发的中心活动。

把重心放在构造, 能显著提升个体程序员的生产率。

构造的产品—— 源程序代码, 常常是唯一准确的软件描述。

构造是确保唯一要完成的活动。

➢ 基于软件构造的数据库设计

基础步骤:需求分析、概念结构设计、逻辑结构设计、物理结构设计、数据库实施、数据库运行和维护

考虑因素:模块化设计、接口设计、代码质量

优化策略:索引优化、查询重构、数据分化、缓存利用

数据库的安全性和可靠性设计:访问控制、数据加密、数据备份和恢复

/代码 8.2:使用SQL代码创建Question关系时添加适当的完整性约束条件/

CREATE TABLE Question(

QuestionID VARCHAR(9) FOREIGN KEY,

Factorl INT CHECK(Factor1<=100),

Operator CHAR(2) CHECK(Operator IN (‘+’,‘-’,‘*’,‘/’)),

Factor2 INT CHECK(Factor2<=100),

Result INT CHECK(Result<=100),

CategoryID CHAR(9),

FOREIGN KEY (CategoryID) REFERENCES Category(CategoryID)

);

1.3  课程目标  3

➢快速原型法

快速原型是快速建立起来的可以在计算机上运行的程序,它所能完成的功能往往是最终产品能完成的功能的一个子集。

使用快速原型的目的是:在获得用户交互基本需求说明的基础上,快速建立一个可以运行的软件,使用户及时运行和看到交互的形式和使用效果,并对交互需求说明进行补充和精化,提出改进意见;开发人员进一步修改完善,如此循环迭代,直到得到一个用户满意的模型为止。

➢防御性编程

防御编程的基本思想是:程序员要预计其他程序员的过错、无效的输入、甚至有害的数据及使用者的过失,即使这种事情罕见,也要采取适当措施保护自己的程序。

保护程序无效输入破坏的基本原则:检查每个输入参数的数据;特别要检查从程序外部进入程序的数据。一旦发现了无效数据,就要决定处理的方式。基本的方式有处理错误和使用异常。

处理错误:

继续运行程序、返回中性无害的数据。

用最接近的有效数据替换无效数据。

在日志中记录警告信息并继续运行程序。

调用错误处理程序或对象。

屏幕显示错误信息。

尽可能在局部处理错误。

返回一个错误编码,让特定程序处理这个错误。

防御性编程(Defensive programming)是防御式设计的一种具体体现,它是一种预见代码可能出现问题并提前采取措施来防止这些问题发生的编程风格。以下是对防御性编程的详细解释:

防御性编程旨在保证对程序的不可预见的使用不会造成程序功能上的损坏。它强调在编程过程中预见并防范潜在的错误和异常情况,从而增强软件的健壮性和稳定性。

二、核心原则

1.假设输入总是错误的:不依赖外部输入的绝对正确性,对所有输入进行验证和清理。

2.最小化错误的影响范围:通过异常处理、错误隔离等措施,限制错误对系统整体的影响。

3.使用断言进行内部检查:在代码的关键位置加入断言,确保程序状态符合预期。

4.代码清晰易懂:编写易于理解和维护的代码,便于团队成员发现潜在问题。

5.持续测试:通过单元测试、集成测试等手段,不断验证软件的正确性和稳定性。

三、关键策略

1.输入验证:永远不要信任用户输入,始终验证所有来自用户、API或其他外部来源的数据。这包括检查数据类型、长度、格式和范围。

2.使用验证库或工具:利用现有的库或框架提供的验证功能,可以简化验证过程并提高代码的可读性。

3.对所有输入进行消毒:防止跨站脚本攻击(XSS)等安全漏洞。使用专门的库或函数对输入进行转义或编码。

4.处理空值或未定义的值:使用JavaScript的可选链操作符(?.)和空值合并运算符(??)来有效地处理可能为空或未定义的值,避免出现TypeError。

5.在访问对象属性之前进行检查:在访问嵌套对象属性之前,确保父对象存在。

6.为函数参数设置默认值:避免函数在缺少参数时产生意外行为。

7.使用try...catch块:捕获可能抛出的异常,并提供适当的错误处理机制。

8.记录错误信息:使用console.error或专门的日志记录工具记录错误信息,以便调试和监控。

9.向用户显示友好的错误信息:避免将原始错误信息直接展示给用户,而是提供更易理解和有帮助的提示。

10.处理异步操作中的错误:使用.catch()方法捕获Promise或async/await函数中的错误。

总结:防御性编程是一种细致、谨慎的编程方法,通过提前考虑并防范可能出现的错误,从而有效减少软件漏洞和故障。它强调在编程过程中不仅要实现功能,还要确保程序在面对错误输入、异常情况和并发操作时能够稳定运行。通过采用防御性编程的实践,可以提高代码的健壮性、可维护性和安全性,并减少潜在的错误和问题。

➢开发数据库应用程序

基本流程:需求分析、设计阶段、选择技术栈、环境搭建、数据库创建和管理、后端开发、前端开发、测试、部署、文档和支持

示例流程(以Python+Django+PostgreSQL为例)

需求分析:确定一个简单的博客系统。

设计:设计用户、文章、评论等实体及其关系。

技术栈:选择Python+Django作为后端,PostgreSQL作为数据库,React作为前端。

环境搭建:安装PostgreSQL,配置Django项目。

数据库创建:在PostgreSQL中创建博客系统的数据库和表。

后端开发:

编写模型(Models)定义数据表。

创建视图(Views)和URL路由。

实现API接口。

前端开发:

使用React设计用户界面。

编写与后端API交互的代码。

测试:进行单元测试、集成测试和系统测试。

部署:使用Django的collectstatic命令打包静态文件,配置Gunicorn和Nginx部署到生产环境。

文档和支持:编写用户手册和API文档,提供技术支持。

 

1.4  课程目标 4

➢ER 图 

➢用例图

➢类图

➢SQL 语句

创建:

CREATE TABLE 语句定义基本表。

CREATE TABLE <表名> (

<列名><数据类型>[ 列级完整性约束条件]  [, <列名><数据类型>[ 列级完整性约束条件]]

[,<表级完整性约束条件>]

);

 

插入元组的INSERT语句:

 

INSERT插入语句的一般格式为:

INSERT INTO <表名>[( <属性列1 >, <属性列2 >,……)] VALUES( <常量1 >, <常量2 >,……);

插入如下题目:编号1001 的题目为19 + 20 = 39 ,题目属于基础的四则运算。

INSERT INTO Question VALUES(' 1004 ', 20 ,'-', 19 , 1 , 1 );

 

删除:

 

DELETE 语句的一般形式为:

DELETE FROM <表名> [ WHERE <条件>];

删除编号为1001 的题目:

DELETE FROM Question WHERE Question ID=’1001 ’;

删除所有的题目:

DELETE FROM Question;

 

更新:

UIPDATE 语句的一般形式为:

UPDATE<表名> SET <列名>=<表达式>[, <列名>=<表达式>]… [ WHERE<条件>];

如,修改Question 关系中编号1002 的题目运算结果为30 :

UPDATE Question SET Result= 30 WHER Question ID=' 1002 '

 

选择:

SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>]…

FROM <表名或视图名> [,<表名或视图名>]…

[WHERE <条件表达式>]

[ORDER BY <列名> DESC | ASC]

[GROUP BY <列名> HAVING <条件表达式>];

 

 

2 题库

1.  熟悉相关设计模式,并了解使用场景。例如:工厂方法

使用场景:工厂方法模式的使用场景非常广泛,特别是在需要简化对 象创建过程、提高代码扩展性和可维护性、以及实现动态对象创建 等情况下,工厂方法模式都是一个非常有效的解决方案

工厂方法模式类图:

 

MVC 架构

MVC架构,即模型-视图-控制器(Model-View-Controller)架构,是一种软件工程中的软件架构模式。它将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller),以实现代码的分层组织和关注点的分离。以下是对MVC架构的详细解释和代码分层的说明:

 

MVC架构的基本概念

 

‌模型(Model)‌:

 

负责处理应用程序的核心业务逻辑和数据管理。它与数据库进行交互,完成数据的增删改查等操作,并对数据进行封装和抽象。模型不关心数据的展示方式,只负责提供数据服务。在MVC架构中,可以有多个模型,每个模型负责不同的业务领域。

 

‌视图(View)‌:

 

负责数据的展示和用户交互。它根据模型提供的数据,以用户友好的方式呈现给用户,并接收用户的输入。视图通常与模型直接关联,并响应用户的操作更新模型数据。视图可以是图形界面、命令行界面等,具体形式取决于应用程序的需求。

 

‌控制器(Controller)‌:

 

负责接收用户的请求并做出相应的响应。控制器接收用户的输入,调用相应的模型进行处理,并将结果传递给视图进行展示。控制器充当了模型与视图之间的协调者,确保数据的正确流动和处理。控制器还负责处理业务逻辑和流程控制,以确保应用程序的正确运行。

3.防御性编程

import java.io.*;

 

public class FileReaderUtil {

 

public String readFileContent(String filePath) { // 检查文件路径是否为空或 null

if (filePath == null || filePath.isEmpty()) {

throw new IllegalArgumentException("文件路径不能为空"); }

 

//  尝试读取文件内容

try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {

StringBuilder content = new StringBuilder(); String line;

while ((line = reader.readLine()) != null) {

content.append(line).append(System.lineSeparator()); }

return content.toString();

} catch (FileNotFoundException e) {

System.err.println( “文件未找到” + filePath);

return "文件未找到: " + filePath; } catch (SecurityException e) {

System.err.println("没有权限: " + filePath);

return "没有权限: " + filePath; } catch (IOException e) {

System.err.println("文件读取异常: " + filePath); return "文件读取异常: " + filePath;

} }

 

思考:软件构造在企业级项目中的应用

主要体现在以下几个方面:

抽象与封装‌:通过抽象和封装提高代码的可读性、可维护性和可重用性,应对企业级应用复杂的业务逻辑和数据结构。

‌依赖管理‌:使用依赖管理工具管理大量第三方库和模块,确保版本兼容性。

‌多线程与并发处理‌:利用Java的多线程特性或第三方库提高并发性和应用程序吞吐量。

‌测试与调试‌:通过彻底的测试和调试确保企业级应用的可靠性

‌定制化解决方案‌:根据企业业务需求创建定制化软件,实现业务集成、数据安全和隐私保护等。‌

 

  1.             是一种软件架构风格,它将网络上的所有事务都抽象为资源,所有的操作都是无状态的。(A)

A.rest   B.soape   C.ria  D.uddj

2.             是一种用于注册web服务和调用 web服务的技术。(A)

A. uddj   B.interface   C.data   D.api

3.以下哪个不属于soa的实现方法(D)

A.服务注册中心  B.服务请求者  C.服务提供者  D.服务熔断者

4.soa指(A)

A.面向服务的体系结构       B.面向数据库的体系结构 

C.面向控制层的体系架构    D.面向 web 的体系架构

5.RIA 指(A)

A.富互联网程序    B.轻互联网程序

C.Web应用程         D.桌面应用程序

9.哪些不是交互设计的基本原则:(D)

A.学习性   B.灵活性   C.健壮性   D.便利性

10.异常处理一般有哪些模式?(A)

A.终止模式  B.暂停模式  C.跳过模式  D.重试模式

二、填空题

1.在软件构造中,             是构建高质量软件的基础,它包括了代码的结构、设计和实现等方面。

软件设计

2.面向对象编程中的“     ”特性允许对象隐藏其内部状态和行为,仅通过公共接口与外部进行交豆。

封装

3.单元测试是对软件中的最小可测试单元进行的测试,通常这些单元是        或        。

函数、方法

三、判断题

8.在软件构造中,模块化是指将软件划分成多个相互独立的模块,每个模块完成特定的功能,并且模块之间不存在任何依赖关系。

错(模块化确实是将软件划分成多个模块,但模块之间可能存在必要的依赖关系,以实现整体功能)

9:在软件设计中,抽象类是一种特殊的类,它不能被实例化,但可以包含具体的方法实现。

对(抽象类确实不能被实例化,并且它可以包含抽象方法(无实现)和具体方法(有实现))

10.在面向对象编程中,接口是一种特殊的类,它定义了类必须实现的方法,但本身不包含任何实现。

对(接口确实定义了类必须遵循的契约,即必须实现的方法,但接口本身不提供这些方法的实现)

四、问答题(每题5分,共20分)

1.请解释什么是软件构造,并列举其在软件开发过程中的重要性。

“软件构造”指的是通过编码、验证、单元测试、集成测试和调试的组合,详细地创建可工作的、有意义的软件。它不仅包括核心的编程任务,还涉及详细设计(如数据结构与算法设计)、单元测试、集成与集成测试以及其他相关活动。软件构造可以被视为设计的延续,即依据规划的软件构造方案建造真正产品的过程。

重要性:确保软件质量、提高开发效率、促进团队协作、降低维护成本、降低技术风险等。

  1. 简述面向对象编程中的“封装”原则,并说明其如何提高代码的可维护性。

原则:(1)将类的属性设置为私有(private),只能通过类的方法(公共接口)进行访问和修改。(2)提供公共接口(公有方法)来操作类的属性,以实现对类的封装。(3)尽量将类的属性设置为只读或只写,通过公共方法来获取或设置属性的值,以保证数据的安全性和一致性。

如何提高代码的可维护性:信息隐藏与代码隔离、简化复杂性、增强代码的安全性和灵活性、支持版本控制和代码复用

3.描述工厂方法模式的应用场景,并绘制其UML类图。

工厂方法模式是一种创建型设计模式,它提供了一种创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让类的实例化推迟到子类。该模式适用于以下场景:

(1)创建对象时需要根据不同条件选择不同类:例如,一个图形编辑软件可能需要根据用户的选择创建不同类型的形状(如圆形、矩形、三角形等)。通过定义不同的工厂类,可以轻松地根据用户的选择创建相应的形状对象。

(2)隐藏具体类的实现细节:工厂方法模式通过将对象的创建过程封装在工厂类中,从而隐藏了具体类的实现细节。这使得客户端代码可以独立于具体产品的实现进行编译和运行,增加了系统的灵活性和可扩展性。

(3)需要创建复杂对象:当创建对象的过程涉及多个步骤或依赖关系时,可以使用工厂方法模式来简化对象的创建过程。工厂类可以处理这些复杂的依赖关系,并向客户端提供一个简单的接口来创建对象。

(4)系统需要高内聚低耦合:工厂方法模式有助于实现系统的高内聚和低耦合。通过将对象的创建过程封装在工厂类中,可以减少客户端代码与具体产品类之间的依赖关系,从而提高系统的可维护性和可扩展性。

UML类图绘制正确:正确绘制工厂方法模式的UML类图,包括抽象产品、具体产品、抽象工厂和具体工厂等要素。

图例说明清晰:对UML类图中的各个元素进行简要说明,使读者能够理解其含义。

4.请解释多态性的概念,并说明它在面向对象编程中的优势。

概念:同一个方法调用在不同的对象上可以产生不同的行为。

Java的多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)。

优势:提高代码灵活性、增强代码可扩展性、简化代码结构、促进代码的复用、支持抽象和封装、实现更高级别的抽象。

五、设计题(共30分)

1.有一个线购物系统,包含以下核心功能:

商品的管理(包括添加、删除、修改商品信息)用户的管理(包括注册、登录、个人信息管理)。

购物车功能,允许用户添加、删除、修改购物车中的商品。

订单管理,包括订单的创建、支付、取消和查询。

思考上述系统的功能和设计,完成以下三道设计题:

(1)分析该系统中会有存在哪些类和页面

(2)采用MVC(Model-View-Controller)架构模式设计系统,并画出相应的架构图。

(3)完成购物车的用例图

(1)商品类、用户类、订单类等,负责业务逻辑和数据处理包含商品展示页面、用户登录页面、购物车页面、订单页面等,负责与用户交互。

(2)MVC架构图

Model层:包含商品类、用户类、订单类等,负责业务逻辑和数据处理

View层:包含商品展示页面、用户登录页面、购物车页面、订单页面等,负责与用户交互。

Controller层:处理用户的请求,调用Model层的方法完成业务逻辑,并将结果返回给View层展示。

(3)参与者: 用户

用例:

1.查看购物车:用户可以查看购物车中的商品列表,包括商品名称、数量、价格等信息。

2.添加商品到购物车:用户可以从商品列表中选择商品并添加到购物车中。

3.从购物车中删除商品:用户可以从购物车中删除不需要的商品。

4.修改购物车中商品的数量:用户可以增加或减少购物车中某个商品的数量。

5.清空购物车:用户可以清空购物车中的所有商品。

6.结算购物车:用户可以选择购物车中的商品进行结算,进入订单确认和支付流程。

 

 

2.设计一个关系型数据库,使用适当的数据库管理系统(MySQL)

(1)绘制ER图(实体-关系图),展示各个实体及其之间的关系。

(2)根据ER图,创建数据库表,并定义每个表的字段、数据类型和约束条件(如主键、外键、非空约束等)。

(3)编写SQL语句,实现图书信息、读者信息的添加、删除、修改和查询功能,实现借阅记录和归还记录的添加、查询功能。

实现提示:

实体:图书(Book)、读者(Reader)、借记录(BorrowRecord)、归还记录(ReturnRecord)。

关系:读者可以借阅多本图书,图书可以被多个读者借阅,通过借阅记录表来关联读者和图书。每本图书的借阅情况会有多条归还记录,但每条归还记录只对应一条借阅记录。

字段设计:

图书表:图书ID(主键)、书名、作者、出版日期、ISBN、库存数量等。读者表:读者ID(主键)、姓名、性别、注册日期、借阅数量限制等。

借阅记录表:借阅ID(主键)、读者ID(外键)、图书ID(外键)、借阅日期、应还日期等。

归还记录表:归还ID(主键)、借阅ID(外键,唯一)、归还日期等。

SQL语句示例:

添加图书:

 INSERT INTO Book (BooklD, Title, Author, PublishDate, ISBN, StockQuantity) VALUES (?, ?, ?, ?, ?,?);

查询读者:SELECT*FROM Reader WHERE ReaderlD= ?;

借阅图书:INSERT INTO BorrowRecord (BorrowlD, ReaderlD, BooklD, BorrowDate, DueDate) VALUES (?, ?, ?,?, ?);

归还图书:INSERT INTO ReturnRecord (ReturnID, BorrowID,ReturnDate) VALUES(?,?,?);并更新图书库存和借阅记录状态

 

3.在软件开发中,文件操作是一个常见的任务,但文件操作往往伴随着各种潜在的异常,如文件不存在、文件权限不足、读取过程中发生IO错误等。请编写一个防御性编程风格的Java方法,该方法用于安全地读取文件内容,并妥善处理可能发生的各种异常。

要求:

方法签名:public String readFileContent(String filePath)

方法应实现以下功能:

(1)检查文件路径是否为空或null,如果是,则抛出IllegalArgumentException。尝试读取文件内容,并返回文件内容的字符串表示。

(2)捕获并处理以下异常:

FileNotFoundException:文件不存在时,记录日志并返回特定的错误信息:"File not found: " + filePath。

SecurityException:没有文件读取权限时,记录日志并返回特定的错误信息:"No permission to read the file: " + filePath。

IOException:读取过程中发生IO错误时,记录日志并返回特定的错误信息:"IO error while reading the file: " + filePath。

使用try-with-resources语句确保文件资源被正确关闭。

记录异常信息时,可以使用System.err.println来模拟日志记录。

// 尝试读取文件内容

import java.io.*;

public class FileReaderUtil {

public String readFileContent(String filePath) {

// 检查文件路径是否为空或null

if (filePath == null || filePath.isEmpty()) {

throw new IllegalArgumentException("File path cannot be null or

empty");

}

try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {

StringBuilder content = new StringBuilder(); String line;

while ((line = reader.readLine()) != null) { content.append(line).append(System.lineSeparator());

}

return content.toString();

} catch (FileNotFoundException e) { System.err.println("File not found: " + filePath); return "File not found: " + filePath;

} catch (SecurityException e) {

System.err.println("No permission to read the file: " + filePath); return "No permission to read the file: " + filePath;

} catch (IOException e) {

System.err.println("IO error while reading the file: " + filePath); return "IO error while reading the file: " + filePath;

}

}

public static void main(String[] args) { FileReaderUtil fileReader = new FileReaderUtil();

// 测试文件路径为空

try {

System.out.println(fileReader.readFileContent(""));

} catch (IllegalArgumentException e) { System.out.println(e.getMessage());

}

// 测试文件不存在

System.out.println(fileReader.readFileContent("non_existent_file.txt"));

// 测试文件权限不足(需要手动创建一个无法读取的文件进行测试)

// System.out.println(fileReader.readFileContent("/path/to/unreadable/file.txt"));

// 测试正常读取(需要手动创建一个可读的文件进行测试)

// System.out.println(fileReader.readFileContent("/path/to/readable/file.txt"));

}

}

 

 

4.在软件开发过程中,随着功能的增加和修改,代码中可能会出现冗余的逻辑。这些冗余逻辑不仅增加了代码的复杂性,还可能导致维护困难、性能下降等问题。现在,你有一个简单的订单处理系统,其中包含了一些冗余的代码逻辑。你的任务是识别并重构这些冗余逻辑,使代码更加简洁、易读和可维护.

原始代码:

public class OrderProcessor {

public double calculateTotal(Order order) {

pouble total = 0.0;

 

// 处理普通商品

for (OrderItem item : order.getItems())

{ if(item.getType().equals("regular"))

{

total += item.getPrice() * item.getQuantity();

}

}

// 处理折扣商品(这里有一段冗余的逻辑)

for (OrderItem item : order.getItems()) {

if (item.getType().equals("discount")) {

total += item.getPrice() * item.getQuantity() * 0.8; // 假设折扣是20%}

}

}

// 处理特殊商品(这里也有一段冗余的逻辑)

for (OrderItem item : order.getItems())

{ if (item.getType().equals("special"))

{

//  假设特殊商品有额外的处理逻辑,比如价格计算方式不同

total += calculateSpecialItemPrice(item);

}

}

return total;

}

 

private double calculateSpecialItemPrice(OrderItem item) {

// 特殊商品的价格计算逻辑

return item.getPrice() * item.getQuantity() * 1.1; // 假设特殊商品有10%加价

}

// ... 其他方法和类定义 ...

}

 

class Order {

private List items;

// ... 其他属性和方法 ...

}

 

class OrderItem

 { private String type;

private double price;

private int quantity;

}

 

重构要求:

描述上述代码中冗余的部分,并给出重构思路(5分)

重构代码(5分)

重构后的代码示例(仅给出思路,具体实现可能有所不同):

 

public class OrderProcessor {

public double calculateTotal(Order order)

{ double total = 0.0;

for (OrderItem item : order.getItems())

 { switch (item.getType()) {

case "regular":

total += item.getPrice() * item.getQuantity();

break;

case "discount":

total += item.getPrice() * item.getQuantity() * 0.8; // 折扣20%

break;

case "special":

total += calculateSpecialItemPrice(item);

 break;

default:

// 可以考虑抛出异常或记录日志,表示遇到了未知的商品类型

throw new IllegalArgumentException("Unknown item type: " +item.getType());

}

}

return total;

}

private double calculateSpecialItemPrice(OrderItem item) {

// 特殊商品的价格计算逻辑

return item.getPrice() * item.getQuantity() * 1.1; // 特殊商品加价10%

}

 

// ... 其他方法和类定义 ...

}

 

// Order 和 OrderItem 类保持不变

说明:

在重构后的代码中,使用了switch语句来替代多个if语句,从而消除了冗余的循环。通过将商品类型的处理逻辑集中在一个循环中,提高了代码的可读性和可维护性。

如果未来需要添加新的商品类型,只需在switch语句中添加相应的分支即可。