文章目錄
- 軟件建模詳細設計概述
- 軟件建模詳細設計目標
- 軟件建模詳細設計原則
- 開閉原則
- 里氏 (Liskov) 替換原則
- 依賴倒置原則
- 接口分離原則
- 單一職責原則
- 最少知識原則(迪米特法則)
- 高內聚原則
- 松耦合原則
- 可重用原則
- 軟件建模詳細設計內容
- UML 軟件靜態結構視圖建模
- 軟件靜態結構細化設計
- 類的聚合關系細分
- 專屬聚合 (Exclusive0wns)
- 從屬聚合 (0wns)
- 擁有聚合 (Has)
- 成員聚合 (Member)
- 泛化與繼承
- 實現繼承
- 擴展繼承
- 限制繼承
- 方便繼承
- 高級類圖建模
- 可見性
- 導出信息
- 限定關聯
- 關聯類與具體化類
- 接口與抽象類
- 接口
- 抽象類
- 接口與抽象類的區別
- 類內聚與耦合
- 類內聚與耦合的啟發規則
- 類耦合的種類
- 類之間 6 種關系的耦合強度依次增強
- 迪米特法則應用
- 設計類圖建模實踐
- UML軟件動態交互視圖建模
- 順序圖
- 通信圖
- 類操作
- 發現操作
- 類操作的表示
- 順序圖高級交互技術
- 創建與銷毀臨時對象
- 片段
- 交互引用
- UML軟件狀態機視圖建模
- UML 軟件的實現視圖建模
- 實現視圖
- 構件圖
- 構件和構件之間的依賴關系
- 構件與接口之間的關系
- 端口
- 端口連接
- 部署圖
- 包圖
- 包
- 包的可見性
- 包的嵌套
- 包圖
- 圖書借閱管理系統軟件建模設計實踐
- 系統需求
- 系統靜態模型設計
- 系統動態模型設計
- 圖書搜索功能邏輯設計
- 圖書借閱功能邏輯設計
- 圖書歸還功能邏輯設計
- Book對象的狀態機圖設計
- Borrower對象的狀態機圖設計
- 靜態結構的類圖模型細化設計
軟件建模詳細設計概述
軟件建模詳細設計目標
軟件建模詳細設計是在軟件架構模型設計基礎上,對系統中各模型進一步細化設計,給出軟件程序實現的設計方案。
軟件建模詳細設計原則
開閉原則
- 軟件實體(類、構件等)應該對功能擴展具有開放性,對代碼修改具有封閉性。
- 當應用需求改變時,在不修改軟件實體源代碼的前提下,就可以擴展模塊的功能,使其滿足新的需求。
案例:“環境監測器”功能構件設計
開閉原則設計的好處:
- 在不修改原有代碼基礎上,可以擴展新功能。
- 提高代碼的可復用性及可維護性。
里氏 (Liskov) 替換原則
- 子類可以擴展基類的功能,但不能改變基類原有的功能。
- 子類在繼承基類時,除了添加新的方法且完成新增功能外,不要重寫基類的方法代碼。
- 子類必須遵守基類與外部類之間的隱含約定。
里氏替換原則設計的好處:
- 解決了繼承重寫父類帶來的可復用性變差問題。
- 提高代碼的可復用性及可維護性。
案例:鳥類飛行時間計算模塊程序設計
//鳥類
class Bird {protected double flySpeed;public void setSpeed(double speed) {flySpeed = speed;}public double getFlyTime(double distance) {return (distance / flySpeed);}
}//燕子類
class Swallow extends Bird {
}//幾維鳥類
class BrownKiwi extends Bird {public void setSpeed(double speed) {flySpeed = 0;}
}
package principle;public class LSPtest {public static void main(String[] args) {Bird bird1 = new Swallow();Bird bird2 = new BrownKiwi();bird1.setSpeed(120);bird2.setSpeed(120);System.out.println("如果飛行300公里: ");try {System.out.println("燕子將飛行" +bird1.getFlyTime(300) + "小時.");System.out.println("幾維鳥將飛行" +bird2.getFlyTime(300) + "小時.");} catch (Exception err) {System.out.println("發生錯誤了!");}}
}
按照里氏替換原則重新設計該模塊
完整代碼:
// 動物類
class Animal {protected double runSpeed;public void setRunSpeed(double speed) {runSpeed = speed;}public double getRunTime(double distance) {return (distance / runSpeed);}
}// 鳥類
class Bird extends Animal {protected double flySpeed;public void setFlySpeed(double speed) {flySpeed = speed;}public double getFlyTime(double distance) {return (distance / flySpeed);}
}// 燕子類
class Swallow extends Bird {// 燕子類可以繼承 Bird 類的飛行行為
}// 幾維鳥類
class BrownKiwi extends Animal {// 幾維鳥不能飛,所以我們不實現飛行相關的方法// 而是使用 Animal 類的跑步行為// 如果需要,這里可以覆蓋跑步速度設置方法或行為
}
依賴倒置原則
- 基于抽象類編程,不依賴具體類編程。
- 面向接口編程,不要面向實現編程。
依賴倒置原則設計的好處:
- 降低客戶類與實現類之間的耦合性,容易實現代碼可擴展性。
- 降低并行開發可能出現的風險性。
案例:顧客購物模塊程序設計
顧客類 (Customer) 通過韶關網店(ShaoguanShop)類實現購物,其代碼如下:
class Customer {public void shopping(ShaoguanShop shop) {//購物System.out.println(shop.sell());}
}
顧客若想從婺源網店 (WuyuanShop) 購物,需要對顧客類 (Customer )代碼修改:
class Customer {public void shopping(WuyuanShop shop) {//購物System.out.println(shop.sell());}
}
解決方法:定義“婺源網店”和“韶關網店”的共同接口 Shop ,顧客類 (Customer )面向該接口編程,其代碼修改如下:
class Customer {public void shopping(Shop shop) {//購物System.out.println(shop.sell());}
}
完整代碼:
// 定義 Shop 接口
interface Shop {String sell();
}// 韶關網店實現 Shop 接口
class ShaoguanShop implements Shop {@Overridepublic String sell() {// 實現賣出商品的方法,返回結果return "韶關網店銷售的商品";}
}// 婺源網店實現 Shop 接口
class WuyuanShop implements Shop {@Overridepublic String sell() {// 實現賣出商品的方法,返回結果return "婺源網店銷售的商品";}
}// 顧客類,面向 Shop 接口編程
class Customer {public void shopping(Shop shop) {// 購物System.out.println(shop.sell());}
}
接口分離原則
- 一個構件對外應提供不同服務的專用接口,而不要試圖去建立一個很龐大的接口供所有依賴它的構件去調用,避免客戶端依賴不必要的接口方法
- 提供者構件應該為每個不同訪問構件類型提供一個特定的接口
接口分離原則設計的好處:
- 符合高內聚低耦合設計思想,提高代碼可擴展性。
- 使得類具有很好的可讀性、可擴展性和可維護性。
案例:學生成績管理程序
- 學生成績管理程序一般包含插入成績、刪除成績、修改成績、計算總分、計算均分、打印成績信息、查詢成績信息等功能。
- 如果將這些功能全部放到一個接口中對外提供訪問服務,這顯然不太合理。
- 合理的做法是對外提供不同的功能服務訪問接口。
單一職責原則
- 一個類應該有且僅有一個功能職責,否則該類應該被拆分。
- 單一職責原則的核心就是控制類的粒度大小,將對象解耦,提高其內聚性。
單一職責原則設計的好處:
- 降低類的復雜度。一個類只負責一項職責,其代碼邏輯肯定要比負責多項職責簡單得多。
- 提高類代碼的可讀性。復雜性降低,自然其可讀性會提高。
- 提高類代碼的可維護性。可讀性提高,那自然更容易維護了。
案例:學生工作管理
學生工作主要包括學生生活輔導和學生學業指導兩個方面的工作:
- 生活輔導主要包括班委建設、出勤統計、心理輔導、費用催繳、班級管理等工學業指導主要包括專業引導、學習輔導、科研指導、學習總結等工作。
- 如果將這些工作交給一位老師負責顯然不合理,好的做法是生活輔導由輔導員負責,學業指導由學業導師負責。
最少知識原則(迪米特法則)
迪米特法則的定義是:只與你的朋友交談,不跟“陌生人”說話。在程序設計中,如果兩個軟件實體沒有直接關系,那么就不直接調用,可以通過第三方轉發該調用。
迪米特法則具有以下好處:
- 降低了類之間的耦合度,提高了模塊的相對獨立性。
- 由于耦合度降低,從而提高了類的可復用性和系統的擴展性。
案例:明星與經紀人的關系
- 明星需全身心投入藝術,所以許多日常事務交給經紀人去處理,如與粉絲的見面會,與媒體公司的業務洽淡等。
- 經紀人是明星的朋友,而粉絲和媒體公司對明星來講是陌生人。
高內聚原則
內聚性意味著構件或者類只封裝那些密切關聯的屬性和操作。
按照內聚性的級別,內聚性分為以下三種,它們的內聚性依次變弱:
- 功能內聚:模塊內的所有元素緊密配合完成同一個功能,即滿足單一職責原則。
- 分層內聚:系統采用分層結構,高層能夠訪問低層服務,但低層不能訪問高層服務。
- 通信內聚:模塊內的所有元素都訪問相同數據。通信內聚通常用于數據的查詢、訪問和存儲。
一般來說,內聚性越高,構件越易于實現、測試和維護。
松耦合原則
耦合性指不同對象之間相互關聯的程度。如果一個對象過多地依賴于其它對象來完成自己的工作,這會增加測試、修改的難度,同時降低了對象的可重用性和可移植性。
在詳細設計中要盡可能保持對象之間松耦合。但是軟件必須進行內部和外部的通信,因此耦合是必然存在的。當兩個對象必須相互聯系時,應該通過類的公共接口實現耦合,不應該直接依賴于類的實例。
補充例子:
// 定義一個通信接口
interface Communicator {void sendMessage(String message);String receiveMessage();
}// 實現Communicator接口的Email類
class EmailCommunicator implements Communicator {public void sendMessage(String message) {System.out.println("Sending email with message: " + message);}public String receiveMessage() {// 假設接收到消息return "Email message received";}
}// 實現Communicator接口的SMS類
class SMSCommunicator implements Communicator {public void sendMessage(String message) {System.out.println("Sending SMS with message: " + message);}public String receiveMessage() {// 假設接收到消息return "SMS message received";}
}// 使用Communicator接口的Client類
class Client {private Communicator communicator;public Client(Communicator communicator) {this.communicator = communicator;}public void communicate() {communicator.sendMessage("Hello World!");System.out.println(communicator.receiveMessage());}
}// 在應用程序中使用Client和Communicator
public class CommunicationApp {public static void main(String[] args) {Communicator email = new EmailCommunicator();Communicator sms = new SMSCommunicator();Client emailClient = new Client(email);Client smsClient = new Client(sms);emailClient.communicate();smsClient.communicate();}
}
在這個例子中,Client
類不直接與 EmailCommunicator
或 SMSCommunicator
類耦合。相反,它通過 Communicator
接口與它們通信。這允許 Client
類在不知道通信細節的情況下發送和接收消息。因此,如果將來引入了一個新的 Communicator
實現,例如 WhatsAppCommunicator
,Client
類不需要任何修改就能使用它,只需在創建 Client
實例時傳入新的通信器即可。這就是通過類的公共接口實現耦合的一個典型例子。
可重用原則
- 為了提高工作效率、減少錯誤、降低成本,要充分考慮軟件元素的重用性。
- 重用性有兩個方面的含義:盡量復用已有的類代碼,如開發環境提供的類庫;在設計新類時考慮將來的可重用性。
- 設計一個可重用的軟件比設計一個普通軟件的代價要高,但是隨著這些軟件被重用次數的增加,分攤到它的設計和實現成本就會降低。
補充例子:
import java.util.ArrayList;
import java.util.List;// 一個通用的接口定義了操作的契約
interface Operable {void operate();
}// 一個具體的類實現了Operable接口
class Printer implements Operable {@Overridepublic void operate() {System.out.println("Printing a document...");}
}// 另一個具體的類實現了Operable接口
class Scanner implements Operable {@Overridepublic void operate() {System.out.println("Scanning a document...");}
}// 設計一個工作流程類,它可以利用Operable接口操作任何設備
class Workflow {private List<Operable> operations = new ArrayList<>();public void addOperation(Operable operable) {operations.add(operable);}public void runAll() {for (Operable operable : operations) {operable.operate();}}
}// 演示如何重用Workflow類來操作不同的設備
public class OfficeAutomationApp {public static void main(String[] args) {// 創建設備的實例Printer printer = new Printer();Scanner scanner = new Scanner();// 創建工作流并添加操作Workflow officeWorkflow = new Workflow();officeWorkflow.addOperation(printer);officeWorkflow.addOperation(scanner);// 運行所有操作officeWorkflow.runAll();}
}
在這個例子中,Operable
接口允許我們定義一個可以操作任何設備的通用契約。Printer
和 Scanner
類都實現了這個接口,使得它們可以在 Workflow
類中互換使用。這樣的設計不僅提高了類的重用性,而且在設計新類時也考慮到了將來的可重用性。例如,如果我們想添加一個新設備類如 FaxMachine
,我們只需要讓它實現 Operable
接口,就可以輕松地添加到現有的工作流程中而不需要修改 Workflow
類的代碼。
此外,這個例子還展示了如何復用Java開發環境提供的類庫,例如 ArrayList
類,來管理 Operable
對象的集合。這種復用標準類庫可以提高開發效率,減少錯誤,并降低開發成本。
以下客戶功能模塊存在什么設計問題?該如何完善?
接口分離原則, IClient
接口提供的方法太多或不是所有的客戶端都需要這些方法,它違反了接口分離原則。客戶端不應該被迫依賴于它們不使用的方法。 根據不同的職責將 IClient
接口拆分為更小的接口。詳細代碼如下:
// 接口負責連接管理
public interface IConnectionManager {void connect();void disconnect();
}// 接口負責數據的發送和接收
public interface IDataTransfer {void send();void receive();
}// 客戶端類實現這兩個接口
public class Client implements IConnectionManager, IDataTransfer {@Overridepublic void connect() {// 實現連接邏輯}@Overridepublic void disconnect() {// 實現斷開連接邏輯}@Overridepublic void send() {// 實現發送數據邏輯}@Overridepublic void receive() {// 實現接收數據邏輯}
}
問題探討:以下客戶數據圖形分析模塊存在什么設計問題?該如何完善?
違背單一職責原則,應將該類進行拆分,CustomerDataChart
類負責圖表的創建和顯示,一個 CustomerDAO
類負責數據訪問操作,以及一個 DBUtil
類負責數據庫連接。
代碼如下:
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;class DBUtil {public Connection getConnection() {// 實現數據庫連接的邏輯// 例如使用 JDBC 連接到數據庫return null; // 應返回實際的數據庫連接對象}
}class CustomerDAO {private DBUtil util;public CustomerDAO(DBUtil util) {this.util = util;}public List<Customer> findCustomers() {// 使用 util.getConnection() 獲取數據庫連接// 執行查詢并返回客戶列表return new ArrayList<>(); // 應返回實際查詢到的客戶列表}
}class CustomerDataChart {private CustomerDAO dao;public CustomerDataChart(CustomerDAO dao) {this.dao = dao;}public void createChart() {// 使用 dao.findCustomers() 獲取數據// 根據獲取的數據創建圖表}public void displayChart() {// 顯示圖表的邏輯}
}public class Main {public static void main(String[] args) {// 創建 DBUtil 實例DBUtil dbUtil = new DBUtil();// 使用 DBUtil 實例來創建 CustomerDAO 實例CustomerDAO customerDAO = new CustomerDAO(dbUtil);// 使用 CustomerDAO 實例來創建 CustomerDataChart 實例CustomerDataChart dataChart = new CustomerDataChart(customerDAO);// 創建圖表dataChart.createChart();// 顯示圖表dataChart.displayChart();}
}
圖形展示如下:
軟件建模詳細設計內容
- 在分析類基礎上,對軟件靜態結構模型的類圖進行詳細設計。各個功能構件均需給出設計類模型。
- 針對構件功能,分析對象之間的交互關系,給出功能構件邏輯實現的動態行為交互圖模型詳細設計。
- 分析對象生命周期的狀態變化,給出對象狀態變遷的狀態機圖模型詳細設計。
- 對反映軟件物理實現模型的構件圖和部署圖進行詳細設計。
UML 軟件靜態結構視圖建模
軟件靜態結構細化設計
- 靜態結構視圖用來表示軟件系統的類程序組成結構,同時也反映各個類中封裝的數據與操作方法。
- 靜態結構視圖支撐面向對象程序編程。
類的聚合關系細分
專屬聚合 (Exclusive0wns)
專屬聚合是類之間語義最強的聚合,部分類與整體類之間滿足 4 個條件:
- 部分類對整體類具有存在依賴性,刪除整體類時,部分類也被刪除。
- 具有傳遞性,如果對象 C1是 B1 的一部分,并且 B1 是 A1 的一部分, C1 也是 A1 的一部分。
- 具有非對稱性,如果 B1 是 A1 的一部分,則 A1 不是 B1 的一部分。
- 具有固定性,如果 B1 是 A1 的一部分,則它絕不是 Ai (i!=1)的一部分。
從屬聚合 (0wns)
從屬聚合的語義比專屬聚合稍弱,部分類與整體類之間滿足 3 個條件:
- 部分類對整體類具有存在依賴性,刪除整體類時,部分類也被刪除。
- 具有傳遞性,如果對象 C1 是 B1 的一部分,并且 B1 是 A1 的一部分,則 C1 是 A1 的一部分。
- 具有非對稱性,如果 B1 是 A1 的一部分,則 A1 不是 B1 的一部分。
擁有聚合 (Has)
擁有聚合的語義比從屬聚合弱,部分類與整體類之間滿足 2 個條件:
- 具有傳遞性,如果對象 C1 是 B1 的一部分,并且 B1 是 A1 的一部分,則 C1 是 A1 的一部分。
- 具有非對稱性,如果 B1 是 A1 的一部分,則 A1 不是 B1 的一部分。
成員聚合 (Member)
成員聚合的語義最低,僅僅是將一組對象組合在一起。
- 不具有存在依賴性、傳遞性、非對稱性、固定性。
- Member 聚合具有組合獨立對象的特征。
- 表示一組對象作為一個高層復合對象來考慮。
- 一個對象可以同時屬于一個以上的復合對象,因此 member 聚合的多重性可以是多對多。
泛化與繼承
- 泛化是指多個類的公共特征抽象到一個更一般化的類中,它描述類之間的一般與具體關系。
- 繼承是指子類復用基類(超類)屬性與操作的機制。
實現繼承
實現繼承是指在子類中組合基類(超類)的特征,并允許在必要時以新的實現來重載這些特征。
擴展繼承
在擴展繼承中,子類組合來自超類的特征,并增加新的特征。
限制繼承
限制繼承是指子類組合來自超類的特征,并重載部分繼承來的特征。
問題:重載導致子類沒有包括超類的所有特征,會帶來維護上的問題,可替換性原則失效。
方便繼承
某些類之間具有一定相似的實現,但它們之間不存在泛化關系,即沒有概念上的分類關系。方便繼承是指將它們中一個類作為其它類的超類。
問題:子類沒有包括超類的所有特征,子類與超類之間沒有泛化關系,可替換性原則無效。盡量避免采用這種方式實現繼承。
高級類圖建模
軟件開發的實際情況很復雜,復雜的問題不會有簡單的解決方案。本節引入類圖建模的高級概念,包括:
- 可見性
- 導出信息
- 限定關聯
- 關聯類與具體化類
可見性
通過設置可見性可以確定類內部的封裝程度,決定其他類能否訪問該類的元素。應用于類的屬性和操作的可見性類型包括:
+ public
公共可見性: 該元素對任何類都可見;- private
私有可見性: 該元素只對本類可見;# protected
保護可見性: 該元素對本類及子類可見;~ package
包可見性: 該元素對同一包中的元素可見.
在設計良好的面向對象系統中,大多數操作是公共的,大多數屬性是私有的。
public class Visibility {private int privateAttribute;public int publicAttribute;protected int protectedAttribute;int packageAttribute; // 默認訪問修飾符,也稱為包訪問權限private void privateOperation() {}public void publicOperation() {}protected void protectedOperation() {}void packageOperation() { // 默認訪問修飾符,也稱為包訪問權限}
}
( 1 )保護可見性
- 保護可見性指類的屬性或操作對本類及其子類可見。
- 應用于繼承的情況,允許子類對象訪問父類對象中的屬性和操作。
( 2 )包可見性
- 具有包可見性類的屬性和操作能夠被處于同一包中的其他類訪問。
- 包可見性是 Java 語言默認的可見性。
( 3 )友元可見性友元可見性用于 c++語言。當一個類需要訪問另一個類的私有屬性或操作時,可以采用友元可見性,在授予友元關系的類中聲明友元操作。
class Book {
private:void putOnBookShelf();
};class BookShelf {
public:friend void Book::putOnBookShelf();
};
導出信息
導出信息是指從其他元素計算得到的信息。它作為一種冗余信息,其目的是為了增加可讀性和信息存取速度。導出信息包括導出屬性和導出關聯。
- 導出屬性是指可以由其他信息導出的屬性。
- 導出關聯是指由其它關聯導出的關聯。
- 導出信息的 UML 表示是在導出屬性名或導出關聯名前面加上一條斜線“/”。
限定關聯
限定關聯是一對多或多對多關聯的約束條件表示。限定關聯在二元關聯的一端設定屬性限定符,即在框中給出一個或多個屬性用于約束條件。
關聯類與具體化類
如果兩個類之間是多對多的關聯,且每個關聯實例都具有屬性值,這時可以使用關聯類。關聯類具有一個約束,要求對于每一對鏈接起來的類實例,只能作為關聯類的一個實例。如果不能滿足這個約束的話,就需要將關聯具體化,即使用一個具體化類來代替關聯類。這個具體化類與之前的兩個類均具有二元關聯。
接口與抽象類
接口
接口用于定義一個類或者構件的功能操作函數集。
- 接口定義一組操作,但不定義操作的實現。
- 接口一般不定義屬性,只有定義操作,并且所有操作都隱含是公共的、抽象的可視性。
- 在實現接口的類中,這些操作都要被實現。
- 一個類可以實現多個接口,一個接口可以被多個類實現
接口是一種特殊的類,所有的接口都是構造型為<<interface>>
的類。在UML中,接口的表示方式有三種: 第一種是接口的圖標表示,第二種是接口的修飾表示,第三種是接口的構造型標簽表示。
- 如果接口使用 構造型標簽表示,實現關系線是一條末端帶有空心三角的虛線,箭頭指向接口,虛線上可以加上構造型關鍵字《implement》。
- 如果接口使用圖標表示,實現關系線則是一條實線,沒有箭頭,實線上也可以加上構造型關鍵字《implement》。
- 一個類使用接口,表明該類依賴該接口,用指向接口的一條虛線箭頭表示,箭頭上可以加上構造型關鍵字《use》。
案例: IElectricalEquipment接口定義了turnOn操作和turnoff操作,TV類和Player類均實現了該接口,即實現了turnOn操作和turnoff操作,RemoteControl類使用了IElectricalEquipment接口。
抽象類
抽象類指不具有實例的類,它通常作為父類,不創建實例對象。抽象類的子類可以實例化。
- 抽象類一般至少有一個抽象操作。
- 抽象類的作用是為其子類描述它們的公共屬性和行為。
- UML 采用斜體表示抽象類的名稱,也可以使用 {abstract) 約束來表示。
補充代碼:
// 定義一個 Vehicle 基類
abstract class Vehicle {private String color; // 車輛顏色屬性public Vehicle(String color) {this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}// 定義一個抽象方法 drivepublic abstract void drive();
}// Plane 類繼承自 Vehicle
class Plane extends Vehicle {public Plane(String color) {super(color);}// 實現 drive 方法public void drive() {System.out.println("Plane is flying.");}
}// Bus 類繼承自 Vehicle
class Bus extends Vehicle {public Bus(String color) {super(color);}// 實現 drive 方法public void drive() {System.out.println("Bus is driving on the road.");}
}// Driver 類與 Vehicle 有關聯關系
class Driver {private Vehicle vehicle;// Driver 可以駕駛任何類型的 Vehiclepublic void driveVehicle(Vehicle vehicle) {this.vehicle = vehicle;vehicle.drive();}
}
接口與抽象類的區別
- 抽象類是對一組具有相同屬性和方法、邏輯上有關系的類抽象。
- 接口是對一組具有相同屬性和方法、邏輯上不相關的類抽象。
- 抽象類可提供部分方法的代碼實現,而接口不實現任何方法的代碼實現。
- 抽象類體現一種泛化關系,而接口體現契約關系。
- 抽象是在問題域分析中發現,而是接口是在設計發現。
類內聚與耦合
類內聚是指類內部元素之間相關性的程度。類耦合是指類之間聯系的程度。
-
內聚和耦合彼此相關聯,更強的內聚會導致更弱的耦合,反之亦然。
-
良好的程序設計應確保在類的內聚與耦合之間達到良好平衡。
類內聚與耦合的啟發規則
為了取得類內聚和類耦合之間的平衡, Riel提出了一些啟發規則:
- 屬性及其相關操作封裝在一個類中;
- 類之間只通過公共接口產生依賴;
- 每個類應該只實現單一職責功能;
- 類中不存在一個操作子集只與一個屬性子集相關的情況;
- 系統功能應盡可能在各個類中均勻分配。
類耦合的種類
( 1 ) x 類有屬性指向 Y 類的實例
( 2 ) x 類有方法引用了 Y 類的實例,如x 使用 Y 類對象作為局部變量;或者返回 Y 類對象
( 3 ) x 類對象調用 Y 類對象方法發送消息
( 4 ) Y 類是 X 類的直接或間接子類
( 5 )X 類方法中輸入參數為 Y 類的實例。
( 6 ) Y 類是一個接口,而 X 類實現了該接口。
類之間 6 種關系的耦合強度依次增強
1 )依賴關系一耦合度最弱
2 )關聯關系一耦合度其次弱
3 )聚合關系一耦合度一般
4 )組合關系一耦合度較高
5 )繼承關系一耦合度高
6 )實現關系一耦合度最高
迪米特法則應用
類耦合對于對象之間協作實現特定功能是必要的,但它應該盡量被限制在類的層次內,即層內耦合,層間耦合應該最小化。
為了限制類間任意通信,采用迪米特法則(最少知識原則)設計,指明在類操作中只允許將以下對象作為消息目標,即沒有直接關系的類之間不交互通信,僅僅有直接關系類進行通信。
- 當前對象本身
- 當前操作中作為參數的對象
- 當前對象的屬性指向的對象
- 當前操作創建的對象
- 全局變量引用的對象
設計類圖建模實踐
- 設計類圖建模是在分析類圖基礎之上對系統分析類圖進行細化與完善設計,從而在設計層面描述系統的靜態結構。
- 在設計類圖中除了詳細定義各種類屬性與方法外,還需定義類之間關系的設計約束。
Power Designer 建模實踐:雇員職位管理設計類圖建模
ChatGPT4:
- Person 類:
- 屬性:
name
(String),sex
(char) - 方法:
getName()
返回 String,getSex()
返回 char - 這個類似乎代表了具有基本屬性如姓名和性別的普通人。
- 屬性:
- Employee 類(繼承自 Person 類):
- 屬性:
username
(String),password
(String) - 方法:
username
和password
的獲取和設置方法,一個接受用戶名和密碼并返回布爾值的login()
方法,以及重寫的getName()
和getSex()
方法。 Employee
類繼承自Person
類,這意味著每個Employee
都是Person
,但具有特定于雇員的額外屬性和行為,如登錄系統的憑證。
- 屬性:
- Post 類:
- 屬性:
postname
(String) - 方法:
getPostname()
返回 String,setPostname()
接受一個 String,addPost()
返回一個布爾值。 - 這個類似乎代表
Employee
可以持有的職位或職稱。1..1
表示每個Employee
有且僅有一個Post
。
- 屬性:
- Department 類:
- 屬性:
departmentname
(String) - 方法:
departmentname
的獲取和設置方法,addDepartment()
返回布爾值。 - 代表組織中的一個部門。每個
Employee
與一個Department
關聯,而一個Department
可以有多個雇員(1..*
關系)。
- 屬性:
- Power 類:
- 屬性:
powername
(String) - 方法:
powername
的獲取和設置方法。 - 代表與
Post
關聯的權力或權限。每個Post
有且僅有一個Power
,表示一對一的關系。
- 屬性:
- Poweritem 類(繼承自 Power 類):
- 屬性:
itemname
(String),isvalue
(int) - 方法:
itemname
和isvalue
的獲取和設置方法,addItem()
返回布爾值。 - 這個類繼承自
Power
類,暗示每個權限可以有多個Poweritem
實例,這些是更具體的權限或許可。
- 屬性:
類圖還展示了類之間的關聯關系:
- 一個
Employee
與一個Person
相關聯(繼承關系)。 - 一個
Employee
可以有一個或多個Post
(0..*
到1..1
的關系),但每個Post
都確切地與一個Employee
相關聯。 - 每個
Post
與一個Power
關聯,每個Power
與一個Post
關聯(1..1
關系)。 - 一個
Department
可以有多個Employee
實例,但每個Employee
都與一個Department
相關聯。
這個模型旨在管理組織中雇員的角色和責任。它捕捉了不同組織實體如人員、職位、部門以及與這些職位相關聯的權限之間的層次結構和關系。該系統可用于跟蹤員工的職位角色、所屬部門及其在組織內的相應權限。使用繼承和關聯確保了清晰、可維護的結構,能夠準確反映組織結構。
1 .分析說明如下模型中類之間設計該關系依據是什么?以及它們的耦合度高低。
2 .針對如下 UML 設計類圖給出 Vehicle 類、 Car 類、 Bus 類的 Java 編程代碼。
1)
2)
import java.util.ArrayList;
import java.util.List;// Vehicle.java
public interface Vehicle {void run();
}// Car.java
public class Car implements Vehicle {public String name;public List<Driver> drivers;public Car(String name) {this.name = name;this.drivers = new ArrayList<>();}@Overridepublic void run() {// Car的run實現}public void beforeRun(Oil oil) {// Car的beforeRun實現}public void addDriver(Driver driver) {this.drivers.add(driver);}
}// Bus.java
public class Bus extends Car {public Bus(String name) {super(name);}// Bus可能有特定的行為和屬性,這里只提供基礎構造方法
}
完整代碼:
import java.util.ArrayList;
import java.util.List;// Vehicle.java
interface Vehicle {void run();
}// Car.java
class Car implements Vehicle {public String name;public List<Driver> drivers;public Car(String name) {this.name = name;this.drivers = new ArrayList<>();}@Overridepublic void run() {// Car的run實現}public void beforeRun(Oil oil) {// Car的beforeRun實現}public void addDriver(Driver driver) {this.drivers.add(driver);}
}// Bus.java
class Bus extends Car {public Bus(String name) {super(name);}// Bus可能有特定的行為和屬性,這里只提供基礎構造方法
}// Driver.java
class Driver {public String name;public int age;public List<Car> cars;public Driver(String name, int age) {this.name = name;this.age = age;this.cars = new ArrayList<>();}public void addCar(Car car) {this.cars.add(car);}
}// Oil.java
class Oil {public String type;public Oil(String type) {this.type = type;}public void add() {// Oil的add方法實現}
}
UML軟件動態交互視圖建模
軟件動態交互視圖用于描述系統功能的對象之間動態交互行為。順序圖和通信圖均屬于動態交互模型圖。
順序圖
順序圖也稱序列圖,它按照從上至下順序描述對象之間的消息交互,其目的是展示功能邏輯的設計實現。
- 順序圖由一組對象及它們之間的消息組成,強調消息的先后時間順序。
- 順序圖也包括生命線、激活、片段等圖形元素。
通信圖
通信圖是另一種類型的動態交互圖,它用于表現對象之間的協作關系。通信圖由對象以及其關聯組成,它強調對象間的連接關系,對象之間消息通信順序使用序列號表示。
類操作
類操作(方法)是指類所能提供的服務或功能函數。類將一組操作函數作為服務提供給系統中的其他類訪問。
發現操作
- 可從順序圖或通信圖的對象交互中發現類操作。順序圖和通信圖中的每一條消息,都必須有目標對象的一個操作為其服務。
- 根據類的職責確定操作,如數據記錄創建、讀取、更新和刪除操作。
類操作的表示
在 UML 中,類操作的表示語法為:
[可見性] 操作名稱 [(參數表)] [:返回類型] [{屬性字符串}]
操作名稱是動詞或動詞短語,單詞首字母小寫。如果有多個單詞,則將這些單詞連接起來,第一個單詞首字母小寫,其他單詞首字母大寫。
ChatGPT4:
類圖代表了使用數據傳輸對象(DTO)模式的用戶注冊系統設計。這種模式在面向對象設計中常用于軟件應用子系統之間的數據傳輸。這里是類圖的每個部分的詳細解釋:
UserDTO 類:
這是一個簡單的 DTO 類,包含兩個屬性:
userAccount
:一個String
類型,用來存儲用戶的賬戶名。userPassword
:一個String
類型,用來存儲用戶的密碼。
它還提供了兩個屬性的獲取和設置方法,允許其他類訪問和修改這些值:
getUserAccount()
:返回userAccount
字符串。setUserAccount(String userAccount)
:設置userAccount
字符串。getUserPassword()
:返回userPassword
字符串。setUserPassword(String userPassword)
:設置userPassword
字符串。
RegisterForm 類:
這個類似乎代表用戶填寫的注冊表單。它包含:
user
:UserDTO
的一個實例,用來保存與用戶相關的表單數據。userDao
:IUserDAO
接口的一個實例(下面會解釋),可能用于將用戶添加到某種類型的數據存儲中。
IUserDAO 接口:
這是一個定義用戶數據訪問對象(DAO)契約的接口,包含以下方法:
addUser(UserDTO user)
:一個接受UserDTO
對象并返回一個表示添加用戶到數據存儲是否成功的boolean
值的方法。
OracleUserDAO 類:
這是 IUserDAO
接口的一個具體實現,專門設計用來與 Oracle 數據庫交互。它實現了 IUserDAO
接口的 addUser
方法。
關系:
RegisterForm
與UserDTO
有一個組合關系,由實心菱形表示,意味著RegisterForm
包含并控制UserDTO
實例的生命周期。RegisterForm
與IUserDAO
有一個關聯關系,由箭頭表示,意味著它使用IUserDAO
接口來訪問用戶數據。IUserDAO
與OracleUserDAO
有一個實現關系,由虛線和空心箭頭表示。這意味著OracleUserDAO
承諾實現IUserDAO
定義的契約。
案例:發現操作
順序圖高級交互技術
創建與銷毀臨時對象
- 當創建臨時對象時,發送者發出一個實例化消息后將創建臨時對象。
- 當銷毀臨時對象時,發送者發出消息銷毀臨時對象,并回收其擁有的資源。
- 在順序圖中,在對象的生命線上標記“×”符號,表示對象被銷毀。
片段
在模塊功能詳細設計中,需要描述功能處理的控制邏輯,如展示條件分支、循環處理,以及并行執行。在順序圖中采用不同類型的片段來表示這些控制邏輯。
UML 2.x中預定義了如下片段:
- opt選項片段
- alt條件片段
- loop循環片段
- para并行片段
(1) loop循環片段
在模塊功能邏輯設計中,使用loop片段表示重復執行一些操作。
(2)para并行片段
在模塊功能邏輯設計中,使用para片段表示執行一些并行操作。
(3)alt條件片段
在模塊功能邏輯設計中,使用alt片段表示根據條件執行操作。alt片段表達if-then-else邏輯。
(4) opt選項片段
在模塊功能邏輯設計中,使用opt片段根據當條件執行選項操作。opt片段表達if…then邏輯。
交互引用
在設計有較多相同處理邏輯的模塊時,可以采用交互引用復用已有處理邏輯,被引用的處理邏輯采用標簽 ref 標注。
在如下訂單提交模塊序列圖模型中,包含了哪些處理邏輯?并分別說明各交互對象的對象類型。
訂單結賬和信用卡支付。
Customer、Payment Processor為參與者, Order Checkout、CreditCard Payment為控制類對象, Checkout Page為邊界類對象 ,Order、OrderItem、Item為實體類對象。
UML軟件狀態機視圖建模
狀態機圖是對系統中對象的狀態變化邏輯進行建模,它描述類對象隨時間變化的行為狀態,以及對象受事件影響如何進行狀態變遷。
狀態機圖建模步驟:
- 發現對象狀態
- 分析對象變遷的事件與條件
- 建立對象狀態圖
案例:微波爐工作的狀態機圖
ChatGPT4:
狀態:
- Waiting(等待):微波爐的初始狀態,顯示時間,等待用戶輸入。
- Full Power(全功率):微波爐在全功率模式下工作,功率設置為600W。
- Half Power(半功率):微波爐在半功率模式下工作,功率設置為300W。
- Set Time(設置時間):用戶設置微波爐的工作時間。
- Enabled(啟用):微波爐門關閉,準備開始工作,顯示“Ready”。
- Disabled(禁用):微波爐門開啟,不允許操作,顯示“Waiting”。
- Operation(操作):微波爐開始工作。
轉換和觸發事件:
- 用戶選擇全功率或半功率,微波爐進入相應的功率狀態。
- 在任何功率狀態下,設置時間后,微波爐進入設置時間的狀態。
- 當時間被設置后,如果微波爐門關閉,微波爐變為啟用狀態。
- 如果微波爐門打開,微波爐將變為禁用狀態。
- 從啟用狀態,開始操作將使微波爐進入操作狀態并開始加熱。
- 用戶可以在任何時候取消操作,微波爐將返回到等待狀態。
- 在操作結束后,微波爐將完成加熱過程并返回到等待狀態。
針對如下電梯對象的狀態機圖模型,如何編寫其Java控制程序?
代碼均為簡化代碼,參考思路即可:
1.定義電梯的狀態和事件:
public enum ElevatorState {IDLE,MOVING_UP,MOVING_DOWN,OPEN_DOORS,CLOSED_DOORS
}public enum ElevatorEvent {CALL_UP,CALL_DOWN,MOVE_UP,MOVE_DOWN,DOORS_OPEN,DOORS_CLOSE,TIMEOUT
}
2.創建電梯的控制類
public class ElevatorControl {private ElevatorState currentState;public ElevatorControl() {this.currentState = ElevatorState.IDLE;}public void handleEvent(ElevatorEvent event) {switch (currentState) {case IDLE:switch (event) {case CALL_UP:openDoors();currentState = ElevatorState.OPEN_DOORS;break;case CALL_DOWN:// Similar logic for CALL_DOWNbreak;// Handle other events}break;case MOVING_UP:switch (event) {case MOVE_UP:// Logic for moving upbreak;case TIMEOUT:currentState = ElevatorState.IDLE;break;// Handle other events}break;// Implement logic for other states}}private void openDoors() {// Logic to open elevator doorsSystem.out.println("Doors are opening.");}// Define other methods such as closeDoors(), moveUp(), moveDown(), etc.
}
3.使用控制類響應事件
public class ElevatorSystem {public static void main(String[] args) {ElevatorControl elevator = new ElevatorControl();// Simulate eventselevator.handleEvent(ElevatorEvent.CALL_UP);elevator.handleEvent(ElevatorEvent.DOORS_CLOSE);elevator.handleEvent(ElevatorEvent.MOVE_UP);// Add more events as needed}
}
UML 軟件的實現視圖建模
實現視圖
實現視圖是對系統的實現結構建模,包括如下模型圖:
- 構件圖:建模構件和構件間的接口和依賴關系,表示系統構件組成結構;:
- 部署圖:建模在系統運行時,構件在系統各節點上的物理部署關系及其運行環境;
- 子系統與包圖:子系統可以用包來體現,可使用包圖表示系統邏輯架構。
構件圖
- 構件圖是用來表示系統中構件與構件之間、構件與接口之間關系的圖。
- 構件圖可以從多種層次粒度描述系統實現架構,如系統分解哪幾個子系統,子系統包括哪些構件,構件由哪些類封裝組成。
構件和構件之間的依賴關系
構件與接口之間的關系
- 構件與構件之間通過定義良好的接口進行協作。
- 構件圖中,構件之間的關系表現為依賴關系或實現關系。
- 實現關系指一個構件實現了一個接口,使用實線連接到小圓上。該接口被稱為供給接口。
- 依賴關系指一個構件使用了一個接口,使用實線連接到半圓上。該接口被稱為請求接口。
端口
端口是指構件一個對外交互點。端口顯示為一個小正方形,放置在構件方框的邊界上。
端口連接
在 UML 軟件架構建模設計中,主要使用如下連接器將構件的端口進行連接。
- 裝配連接器:用于連接構件之間的端口
- 委托連接器:用于連接構件供給端口與內部供給端口或內部請求端口與構件請求端口
作為一種選擇, UML 還允許將連接器按如下方案連接構件端口。
若有多個構件使用相同接口訪問另一構件接口, UML 允許將連接器按如下方案連接構件端口。
身份認證構件 (Authentication)端口與訂單構件 (Orders) 端口均使用相同請求接口(lCustomers)連接客戶構件(:Customers )端口的供給接口lCustomers。
委托連接器是將構件的端口連接到實現該行為的對象。它表示消息到達一個端口后,將被傳遞給內部目標對象進行處理。
案例:電子商務系統的構件圖
部署圖
- 部署圖用于對系統的物理實現情況進行建模。
- 部署圖用于表示軟件構件如何部署到硬件環境中,它顯示系統中的不同構件在何處物理地運行,以及彼此之間如何進行通信。
節點
- 節點是指人工制品和構件在系統中部署的硬件設備及其運行環境。
- 節點之間通過通信線路將連接起來實現信息傳輸。
構件在節點中部署
構件需要在節點設備中運行。在建模時,需要將構件在節點中進行部署
案例:電子商務系統部署圖
包圖
- 在 UML 模型圖中包用來對模型元素進行分組,簡化 UML 圖,使其更容易理解。
- 包圖用于對模型自身組織進行建模,它是由一系列模型元素(類、用例等)構成的包所組成的模型,描述包與包之間的關系。
包
UML 中包表示為左上方帶有小矩形的矩形。包的名稱采用字符串,簡單名僅包含名稱字符串,全限定名則以本包所處的外圍包的名稱作為前綴。
包的可見性
包可以控制protected所包含的內部元素的可見性,即外部元素訪問包內元素的權限:
+ public
公共可見性: 對它所在包和內嵌包的對象可見,也對所有引入它的包及其子包中對象可見;- private
私有可見性: 只對它所在包中對象可見;# protected
保護可見性: 對它所在包和內嵌包中對象可見;
包的嵌套
- 包可以擁有其他包,子包又可以擁有自己的子包,從而構成嵌套結構。
- 包的嵌套可以清晰地表現模型元素之間的層次結構關系。
- 但是包的嵌套不宜過深,層數一般以 2到 3 層為宜
包圖
- 包圖描述包與包之間的關系,可用于描述軟件系統的邏輯模型。
- 包與包之間可以存在依賴關系。兩個包之間存在著依賴關系通常是指這兩個包所包含的模型元素之間存在著一個或多個依賴關系。
- 包的依賴關系使用一根虛線箭線表示,箭頭指向被依賴的包。依賴關系可以標上標簽,在《》中包含依賴關系的類型。
案例:應用包圖描述系統的軟件架構
針對如下股票投資分析系統構件模型,分析構件之間關系如何設計?
ChatGPT4:
在這個股票投資分析系統的構件模型中,構件之間的關系是按照客戶端-服務器架構設計的。客戶端包含用戶界面(GUI)、圖形庫(Graphing Lib)、客戶端會話(Client Session)和股票組合(Stock Portfolio)構件。服務器端包含認證服務器(Authentication Server)、服務器會話(Server Session)、股票市場分析器(Stock Market Analyzer)和投資組合數據庫(Portfolio DB)構件。
客戶端的GUI用于展示信息和接收用戶輸入,圖形庫用于數據可視化。客戶端會話管理用戶與系統的交互,而股票組合構件負責管理用戶的個人投資組合信息。這些客戶端構件通過API與服務器端的構件交互。服務器端的認證服務器負責處理登錄和安全認證,服務器會話維護與客戶端的連接狀態。股票市場分析器是系統的核心,負責分析股票市場數據和生成投資建議,而投資組合數據庫存儲用戶的投資組合信息。
客戶端與服務器之間的交互通過網絡進行。例如,用戶通過GUI發起請求,客戶端會話將請求發送到服務器,服務器會話接收請求并與認證服務器進行認證,然后由股票市場分析器處理請求。結果通過服務器會話返回給客戶端會話,并最終通過GUI展示給用戶。
此外,系統還接入了第三方服務,如彭博數據庫(Bloomberg DB),這提供了額外的市場數據,股票市場分析器可能會使用這些數據來增強分析結果的準確性。
針對如下房地產按揭交易系統部署模型,分析各要素之間關系如何設計?
針對如下電商系統軟件架構模型,分析包之間關系如何設計?
《use》關系表示使用依賴,Web Shopping依賴Payment,Mobile Shopping依賴Payment…
《merge》關系表示合并,Web Shopping、Mobile Shopping、Phone Shopping、和Mail Shopping合并之后就擁有了Shopping Cart的全部功能 。也表明了它們共享一些在“Web Shopping”包中定義的通用功能。
《Access》關系表示私有引入,比如代碼中的指定包名類名 。Payment可以訪問Shopping Cart中的特定功能或類,但這種訪問是受限的。
《Import》關系表示公共引入,比如Java中的import之后,就可以直接使用import包中的類了。一旦“Shopping Cart”公共導入了“Inventory”,它就可以直接使用“Inventory”中定義的公共類和接口。
圖書借閱管理系統軟件建模設計實踐
系統需求
系統靜態模型設計
建立系統靜態模型類圖。建立類圖需要確定系統中的類,確定類的屬性,確疋類之間的關系。根據系統動態模型確定類的操作并完善類圖。
( 1 )確定系統中的類
- 系統用戶有管理員 (Administrator) 、圖書管理員 (Librarian) 和借閱者(Borrower) 三個參與者,可將他們抽取為的角色實體類。
- 系統需存儲圖書信息,可抽取出Book類;需要存儲借閱信息,可抽取出Loan類;需要存儲預定信息,可抽取出Reservation類;這些類是系統中的主要業務實體類。
( 2 )確定類的屬性
( 3 )確定類之間的關系,創建系統類圖
系統動態模型設計
系統詳細設計階段,針對用例圖中的每一個用例,考慮如何通過對象之間的協作來完成用例的功能。采用順序圖或通信圖對用例功能進行設計建模,采用狀態機圖對類的行為狀態進行設計建模。
圖書搜索功能邏輯設計
圖書借閱功能邏輯設計
圖書歸還功能邏輯設計
Book對象的狀態機圖設計
Book 對象是圖書管理系統中具有明顯狀態變化的對象,圖書借閱業務都與Book對象狀態有關。Book對象具有如下狀態: 新書 (NewBook) 、可借閱預定(Available) 、被預定 (Reserved) 、被借閱 (Borrowed) 、被刪除 (Deleted)。
Borrower對象的狀態機圖設計
Borrower對象也具有比較明顯的狀態,因此也采用狀態機圖對其建模。
Borrower 對象具 有 如下狀態: 新建 (NewUser) 、未預定未借書 (NoLoanReserve) 、有預定 (HasReserve) 、有借書 (HasLoan) 、有預定有借書 (HasLoanReserve) 、被刪除 (Deleted)。
靜態結構的類圖模型細化設計
-
Borrower類需要提供
- checkBorrower操作
- checkNumBooks操作
- addLoan操作
- deleteLoan操作
-
Book類需要提供
- searchBook操作
- checkBook操作
- setLoanStatus操作
-
Loan類需要提供
- addLoan操作
- deleteLoan操作
- checkOverdue操作
針對圖書借閱管理系統如下構件,如何設計系統部署圖模型,并在部署圖中給出部署關系和運行環境(Apache、JBoss、PostgreSQL、Ubuntu)。
1)頁面構件:borrowBook.jsp、returnBook.jsp、reserveBook.jsp、searchBook.jsp、viewBook.jsp、borrowerInfo.jsp,
2)控制構件:borrowBook.java、returnBook.java、reserveBook.java、searchBook.java、viewBook.java、borrowerInfo.java,
3)實體構件:book.java、reservation.java、loan.java、borrower.java,
4)數據庫表:BookInfo、Reservation、Loan、Borrower、BookType
ChatGPT4:
我們可以按照三層架構來組織這些構件:
- 表示層(通過Apache和JBoss托管): 包括所有的JSP頁面,如
borrowBook.jsp
,returnBook.jsp
等。 - 業務邏輯層(運行在JBoss上): 包括控制和實體構件,如
borrowBook.java
,book.java
等。 - 數據層(使用PostgreSQL): 包含所有數據庫表,如
BookInfo
,Loan
等。
現在,我將詳細設計一個系統部署圖模型來展示這些構件是如何部署和相互作用的:
- Apache Web Server: 托管所有的JSP頁面。這些頁面通過HTTP請求與JBoss服務器通信。
- JBoss Application Server: 處理來自Apache服務器的請求,并運行Java控制和實體構件。JBoss可以部署這些Java構件,并提供必要的運行環境。
- PostgreSQL Database Server: 存儲所有的數據庫表,由JBoss服務器通過JDBC進行訪問和操作。
- Apache Web Server托管JSP頁面,并將請求轉發給JBoss服務器。
- JBoss服務器處理業務邏輯,并與數據庫服務器進行交互以存儲或檢索數據。
- PostgreSQL數據庫服務器存儲和管理數據,為業務邏輯層提供數據支持。
所有這些組件都部署在Ubuntu操作系統上,確保系統兼容性和穩定性。