# SSAD_homework1 **Repository Path**: fusion-lin/ssad_homework1 ## Basic Information - **Project Name**: SSAD_homework1 - **Description**: SSAD作业 - **Primary Language**: Unknown - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-09-19 - **Last Updated**: 2024-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Software System Analysis and Design 第一次作业 > 数学强基2101 林福春 2215110055 [toc] ## 问题描述 一般地,面向对象分析与设计中存在三种基本事件处理的机制,除了普通的方法调用外,常常也会用到回调函数,而 J2EE 中还提供了一种基于监听方式的事件处理机制,请查阅资料,对 Action 以及ActionListener 的机制进行分析,完成一个分析示例。 同时,请将这三种方法或其它更多的事件处理方法在代码实现过程中的优劣进行比较和分析,并形成详细的分析总报告。 ## 问题调查 ### 事件处理方法介绍 #### 普通方法调用 普通方法调用是非常常见的事件处理机制,它通过直接调用对象的方法来响应事件。当调用方法完成后,再回到原对象继续顺序执行。 该方法的优点是简单易用,不需要额外的配置或代码。但是,它也有一些缺点,例如,当方法调用耗时较长时,可能会导致界面卡顿,影响用户体验。并且该方法得拓展性较差,难以维护。 #### 回调函数 回调函数相较于直接调用是一种更灵活的事件处理方式,通常由调用者传递一个函数(或函数引用)到被调用者,当事件触发时,被调用者调用这个回调函数。这种模式可以实现松耦合,便于模块化和重用。 ![回调函数示意](./assets/callback.png) 但相对于普通方法调用,回调函数的代码可能会更加复杂,同时,回调函数的调用时机和调用次数也需要明确,否则可能会导致逻辑错误。因为回调函数引入了额外的复杂性,当回调函数的层级较深时,可能导致“回调地狱”。 > 在缺少函数类型的参数的面向对象的程序语言中,例如Java,回调可以用传递抽象类或接口来模拟。回调的接收者会调用抽象类或接口的方法,这些方法由调用者提供实现。(后续的尝试中,使用了runnable接口作为回调函数的实现) #### 监听器模式 在 Java 中,尤其是在 J2EE 和 Swing 中,常见的事件处理方式是基于监听器(Listener)模式。这种模式通常通过注册监听器来实现,当事件发生时,系统会通知所有的监听器。监听器的设计通常用于 UI 框架或事件驱动的系统。 ![监听器示意](./assets/listener.png) 监听器模式的主要优点是松耦合,可以方便地添加、删除或替换监听器,实现代码的模块化和重用。同时,监听器模式也支持事件的多播,即多个监听器可以同时响应同一个事件,拓展性强。 > ActionListener 与 Action > 在 Swing 或 J2EE 中,ActionListener 是一种典型的监听器,用于监听组件的操作事件。ActionListener 接口提供了 actionPerformed 方法,当用户与 UI 组件交互时(例如点击按钮),该方法会被触发。 #### 观察者模式 观察者模式是一种行为设计模式,用于在对象之间建立一对多的依赖关系。它与监听器模式类似,但更关注对象状态变化的同步更新。当一个对象(目标对象)状态发生变化时,它会通知所有观察者对象,触发它们相应的行为。 其特点与监听器类似主要优点为松耦合,并且目标对象与观察者之间的依赖性较低。而且动态增加或移除观察者非常方便。 ![观察者模式](./assets/observer.png) > 观察者模式的UML图基本表述了观察者模式的运行方式即: > - Subject维护一个Observer列表,并将所有Observer添加到列表中; > - Subject的状态发生改变时,它会调用方法通知所有Observer; > - Observer收到通知时,会调用方法更新自身的状态。 ### 处理方法实现代码 #### 普通方法调用 - ClassA.java ```java package syncCall; import java.util.Date; public class ClassA { public void runA() throws InterruptedException { System.out.println("Running method runA in ClassA"); System.out.println("ClassA is running on:" + (new Date()).getTime()); ClassB instanceClassB = new ClassB(); instanceClassB.runB(); System.out.println("Method runA in ClassA finished"); } } ``` - ClassB.java ```java package syncCall; public class ClassB { public void runB() throws InterruptedException { System.out.println("Running method runB in ClassB"); Thread.sleep(2000); System.out.println("Method runB in ClassB finished"); } } ``` 在代码实现中,ClassA 调用 ClassB 的 runB 方法,当 runB 方法执行时,ClassA 会等待 runB 方法执行完毕后才会继续执行。 - 执行结果: ``` Running method runA in ClassA ClassA is running on:1726740638267 Running method runB in ClassB Method runB in ClassB finished Method runA in ClassA finished ``` #### 回调函数 - CallBackDemo.java ```java package callBack; public class CallbackDemo { public static void runDemo() throws Exception{ System.out.println("Running method runA in ClassA"); ClassB b = new ClassB(); b.setCallBack(()->{ System.out.println("Running callback method in ClassA"); System.out.println("Callback method in ClassA finished"); }); b.runB(); System.out.println("Method runB in ClassB finished"); } } ``` - ClassB.java ```java package callBack; public class ClassB { private Runnable callbackFunction; public void setCallBack(Runnable callbackFunction){ this.callbackFunction = callbackFunction; } public void runB() throws Exception{ if (callbackFunction == null){ throw new Exception("Callback function is not set"); } System.out.println("Running method runB in ClassB"); //execute callback function callbackFunction.run(); System.out.println("Method runB in ClassB finished"); } } ``` 在该代码实现中,ClassA 调用 ClassB 的 runB 方法,当 runB 方法执行时,ClassB 会执行回调函数,然后继续执行。 > 这是简单的同步回调示例代码。对于异步场景对应的代码实现不再赘述,异步回调的实现方式与同步回调类似,只是回调函数的执行不阻塞主线程。 #### 监听器模式 - Listener.java ```java package listener; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Listener { public void tryButtonListener() { JFrame frame = new JFrame("Button Example"); JButton button = new JButton("Click Me"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Button clicked via ActionListener!"); } }); frame.add(button); frame.setSize(200, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } } ``` 本段代码直接使用了Java Swing库中的监听器模式,通过给按钮添加监听器,当按钮被点击时,监听器会自动执行相应的操作。代码定义了一个名为tryButtonListener的方法,该方法创建了一个窗口和一个按钮,并为按钮添加了一个监听器。当按钮被点击时,监听器会输出一条消息到控制台。 #### 观察者模式 - Subject.java ```java public class Subject { private List observers = new ArrayList<>(); public void attach(Observer observer) { observers.add(observer); } public void detach(Observer observer) { observers.remove(observer); } public void notifyObservers(String message) { for (Observer observer : observers) { observer.update(message); } } public void setState(String message) { notifyObservers(message); } } ``` - Observer.java ```java package observer; public interface Observer { void update(String message); } ``` - ObserverDemo.java ```java package observer; class StateChangeObserver implements Observer { private String name; public StateChangeObserver(String name) { this.name = name; } @Override public void update(String msg) { System.out.println(name + ": State changed to: " + msg); } } public class ObserverDemo { public static void runDemo() { Subject subject = new Subject(); StateChangeObserver observer1 = new StateChangeObserver("Observer 1"); StateChangeObserver observer2 = new StateChangeObserver("Observer 2"); subject.attach(observer1); subject.attach(observer2); subject.setState("New state1"); subject.detach(observer1); subject.setState("New state2"); } } ``` 在该代码实现中,Subject 类维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。Observer 接口定义了一个 update 方法,用于接收通知。因仅用作原理学习,简单定义了StateChangeObserver 类实现了 Observer 接口,并在 update 方法中输出接收到的消息。ObserverDemo 类演示了如何使用 Subject 和 Observer 类来创建一个观察者模式。 ### 处理方法对比 #### 优缺点对比 | 模式 | 优点 | 缺点 | |---------------|--------------------------------------------------------------|------------------------------------------------------------------| | **同步调用** | 1. 简单易理解,逻辑顺序清晰。 | 1. 当被调用方执行时间较长时,调用方会阻塞,降低性能。 | | | 2. 适合短期执行的任务,不涉及并发操作。 | 2. 不适合异步或并发场景。 | | **回调** | 1. 解耦调用方和被调用方的逻辑,适合异步处理。 | 1. 多层嵌套时,可能导致“回调地狱”,代码可读性下降。 | | | 2. 允许在任务完成时通知调用方,适合异步或事件驱动场景。 | 2. 实现回调函数增加复杂性,尤其是在并发环境下。 | | **监听器** | 1. 解耦事件源与监听器,适合事件驱动的场景。 | 1. 当监听器较多时,事件分发可能带来性能问题。 | | | 2. 允许动态增加或移除监听器,多监听器同时监听同一事件源。 | 2. 监听器的生命周期管理复杂,特别是动态增删时。 | | **观察者模式**| 1. 目标对象与观察者松耦合,便于扩展和维护。 | 1. 当观察者较多时,通知所有观察者可能影响性能。 | | | 2. 动态增加或移除观察者非常方便,便于实现对象间的状态同步。 | 2. 通知无差别,无法针对不同观察者进行差异化通知(除非改进实现)。| #### 场景对比* > 总结于gpt-o1-preview模型,并对内容进行学习与真实性验证。 | 模式 | 优点 | 缺点 | 适用场景 | 线程模型 | 扩展性 | 复杂度 | |-----------------|--------------------------------------------------------------|------------------------------------------------------------------|----------------------------------------------------------------|------------------------------------|-------------------------|-------------------------| | **同步调用** | 1. 简单易理解,逻辑顺序清晰。 | 1. 阻塞调用方,执行时间较长的操作影响性能。 | 简单顺序任务、短时间操作。 | 单线程,调用方需等待结果返回。 | 不易扩展,耦合较强。 | 最低,调用者直接调用被调用者。 | | | 2. 不涉及异步或并发处理。 | 2. 不适合需要异步或并发的场景。 | 适合本地计算、数学运算等任务。 | | | | | **回调** | 1. 允许异步处理,调用方与被调用方解耦。 | 1. 回调嵌套可能导致“回调地狱”,代码维护性差。 | 异步操作:如文件IO、网络请求。 | 支持异步,线程不会阻塞调用方。 | 容易扩展,但需防止回调链过长。 | 中等,回调函数嵌套增加复杂性。 | | | 2. 调用方不阻塞,适合异步任务。 | 2. 复杂并发场景下容易出错。 | 异步事件处理或任务完成通知。 | | | | | **监听器** | 1. 事件驱动设计,解耦事件源和监听器,适合多个监听器响应同一事件。 | 1. 监听器过多时,事件广播可能影响性能。 | GUI 应用程序、用户交互事件、设备驱动程序。 | 通常异步,允许事件源和监听器在不同线程。 | 高,多个监听器可以自由添加或删除。 | 较高,需维护监听器的注册和移除。 | | | 2. 动态添加或移除监听器灵活。 | 2. 监听器管理复杂,动态增删需要额外控制。 | 需要对特定事件进行统一响应的系统,如按钮点击、窗口变化。 | | | | | **观察者模式** | 1. 动态响应对象状态变化,目标对象与观察者松耦合。 | 1. 观察者过多时,通知机制可能导致性能下降。 | 数据模型与视图同步,状态变化通知,如MVC框架中的视图和模型联动。 | 异步或同步,视实现方式。 | 高,可以动态添加观察者,但可能增加复杂性。 | 较高,需维护观察者列表和通知机制。 | | | 2. 易于扩展,便于状态变化的多对象通知。 | 2. 通知不能差异化,所有观察者同时收到相同通知。 | 需要在状态变化时通知多个对象的系统,如数据同步、系统日志。 | | | | ## 总结 本次作业通过对比不同设计模式的特点、适用场景和优缺点,以及对应简单实现。帮助我更好地理解了它们在实际软件开发中的应用。在设计模式的选择过程中,我们需要根据具体的需求和场景来权衡模式的优缺点,并选择最适合的设计模式。同时,我们也需要关注模式的扩展性和复杂性,以确保代码的可维护性和可扩展性。 在软件开发中,选择合适的模式对于提高代码的可维护性、可扩展性和性能至关重要。根据不同的场景和需求,我们可以选择不同的设计模式来实现松耦合、异步处理和事件驱动等目标。在设计模式的选择过程中,我们需要权衡模式的优点和缺点,并考虑具体的应用场景和性能要求。 ## 代码源文件 Git仓库: [Gitee](https://gitee.com/fusion-lin/ssad_homework1)