1111

67 阅读19分钟

考查目标

1.课程目标 1

Ø  软件生命周期活动

‌可行性研究阶段‌、需求分析阶段、‌设计阶段、实现阶段(编码阶段)、‌测试阶段‌、运行与维护阶段‌

 

Ø  造成重构的主要原因

改进软件设计、使软件更容易理解、有助于查找错误

 

Ø  交互设计的基本原则

3条基本原则:

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

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

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

 

Ø  异常处理的模式

异常处理一般有两种模型:终止模式和恢复模式。

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

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

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+"=");
}
}
}

 

Ø  面向对象软件构造

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

抽象与封装、面向对象设计

 

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)

);

 

3.课程目标 3

Ø  快速原型法

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

 

Ø  防御性编程

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

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

防御性编程数据错误出现时的一些建议:

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

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

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

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

5.屏幕显示错误信息。

6.尽可能在局部处理错误。

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

处理错误的方式:使用异常

 

Ø  开发数据库应用程序

需求分析阶段;

概念结构设计阶段;

逻辑结构设计阶段;

物理结构设计阶段;

数据库实施阶段;

数据库运行和维护阶段。

 

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 <条件表达式>];

Ø  代码重构

 

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

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

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

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

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

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

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

 

题库

1.工厂方法模式

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

 

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

 

工厂方法模式类图:

 

 

图例说明:

Factory(工厂角色):即工厂类,它是简单工厂模式的核心,负责实现创建所有实例的内部逻辑;在工厂类中提供了静态的工厂方法createProduct(),它返回一个抽象产品类Product,所有的具体产品都是抽象产品的子类。

Product(抽象产品角色):抽象产品角色是简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口。

其他的是具体产品类:每一个具体产品角色都继承了抽象产品角色,需要实现定义在抽象产品中的抽象方法 。

 

 

2.多态性

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

 

多态性是面向对象编程中的一个核心概念,指的是同一操作可以作用于不同的对象上,表现出不同的行为。多态性主要通过两种方式实现:

方法重载:在同一个类中,可以定义多个同名但参数列表不同的方法。调用时,根据传入参数的类型和数量来决定调用哪个具体的方法。

方法覆盖:子类可以重写父类的方法,实现特定的功能。在运行时,根据对象的实际类型来决定调用哪个版本的方法。

 

多态性的优势:

1、提高代码的可扩展性:多态性允许在不修改现有代码的情况下,添加新的类和方法。例如,可以通过继承和重写来扩展功能,使得系统能够方便地适应新的需求。

2、简化代码:通过使用接口或抽象类,可以定义统一的方法接口,使得不同的类可以以相同的方式被调用,从而简化了代码的复杂性,提高了可读性和维护性。

3、增强代码的灵活性:多态性允许程序在运行时决定调用哪个对象的方法,这种动态绑定使得程序能够根据实际情况灵活地选择具体的实现。

 

例如:在太阳系八大行星系统中,对八大行星进行方法覆盖来实现不同行星的不同轨道和公转周期。通过多态,不仅简化了代码,更提高了代码的可拓展性。

 

3.在线购物系统

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

商品的管理(包括添加、删除、修改商品信息)。

用户的管理(包括注册、登录、个人信息管理)。

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

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

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

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

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

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

 

答案:

(1)该系统有商品类、用户类、订单类等,负责业务逻辑和数据处理;

还有各个业务层的控制类,如商品控制类等,负责接收用户的请求并做出相应的响应;

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

 

(2)架构图如下:

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

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

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

 

(3)购物车的用例图如下:

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

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

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

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

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

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

 

 

4.文件操作,防御性编程

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

要求 :

方法签名: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()) {

           // 如果文件路径为空或者null则抛出异常

           throw new IllegalArgumentException("File path cannot be null or empty");

       }

 

        // 尝试读取文件内容,并通过try,catch捕获并处理三种异常

       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) {

           // 读取过程中发生IO错误时,记录日志并返回特定的错误信息

           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/file1.txt"));

 

       // 测试正常读取,手动创建一个可正常读取的文件进行测试

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

   }

}

 

5. 订单处理系统,重构冗余逻辑

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

原始代码 :

 

public class OrderProcessor {

 

   public double calculateTotal(Order order) {

       double 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;

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

}

重构要求 :

  1. 描述上述代码中冗余的部分,并给出重构思路

  2. 重构代码

 

重构后的代码如下:

import java.util.List;

 

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类是不用重构的

class Order {

    private List items;

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

}

 

class OrderItem {

    private String type;

    private double price;

    private int quantity;

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

}

在重构代码的过程中,发现原始代码中有多次的for循环和if语句,过于冗余,采用switch语句来替代多个if语句可以消除冗余的循环。将处理商品类型的业务逻辑集中在一个循环内,提高了代码的可读性和可维护性,也加强了代码的可拓展性。如果未来需要添加心得商品类型,只需要在switch语句中添加对应的分支即可实现。

 

 

6.MVC 架构

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

MVC架构的基本概念:

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

示例‌:在Java Web开发中,模型层可能包含Service类和DAO类。Service类负责处理业务逻辑,而DAO类负责与数据库进行交互。

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

示例‌:在Web开发中,视图层可能包含HTML、JavaScript、CSS、JSP、jQuery等前端技术实现的页面。

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

示例‌:在Java Web开发中,控制器层可能包含使用Spring MVC框架实现的Controller类,这些类处理HTTP请求,并调用Service层的方法来处理业务逻辑。