drawio和EA是架構設計時經常使用的畫圖工具。
drawio學習門檻低,使用靈活,但是功能僅僅限于畫圖。
EA學習門檻高,但是功能更加的豐富:
①在畫圖方面,EA嚴格滿足UML標準,EA中的圖和類是關聯的,如果修改了一個類的名字或者函數等,在引用這個類的圖中也會自動修改
②EA還可以將架構設計時定義的類、接口等導出為代碼
③EA可以導入已有的代碼,生成類圖
類似于表寫代碼,drawio類似于一種純編輯器的代碼,沒有其它輔助的功能,EA類似于一個有豐富功能的IDE,可以有各種各樣的提示,提供一種沉浸式的使用環境。
當討論uml,最常見的使用場景就是類圖。常用的類之間的關系包括實現、繼承、聚合、組合、關聯、依賴,而這六種關系中實現和繼承關系類似,聚合和組合類似,依賴和關聯類似。
本文中的類圖使用EA來畫。
1實現和繼承
實現:
實現說的是類實現接口。在c++中,沒有接口這個概念,抽象類可以看做接口。c++中的抽象類,是至少有一個函數是純虛函數,這樣的類不能實例化對象。如果以java語言的標準來定義c++中的接口和抽象類,那么接口中的函數都是純虛函數,都沒有自己的實現,抽象類中是至少有一個函數是純虛函數,也可以有不是純虛函數的函數。但是在很多時候,接口和抽象類的邊界也沒有這么清晰。接口可以看做是對類的行為的約束,值提供規則,不提供實現,就像很多行業中的標準制定者,只定義標準,不提供實現,比如網絡通信協議標準、autosar標準等。
接口只定義標準,不提供實現,所以一個類對接口進行實現,就是實現的關系。當然,一個接口也可以繼承一個接口。
如下代碼,有一個接口類Shape,有兩個實現類Rectangle和Circle。Rectancle和Circle是對Shape的實現。
#include <iostream>
#include <cmath>
#include <stdexcept>// Shape 接口類
class Shape {
public:virtual ~Shape() {} // 虛析構函數// 獲取面積virtual double getArea() const = 0;// 獲取周長virtual double getPerimeter() const = 0;// 打印形狀信息virtual void printInfo() const {std::cout << "Area: " << getArea()<< ", Perimeter: " << getPerimeter() << std::endl;}
};// 矩形類
class Rectangle : public Shape {
private:double width, height;public:Rectangle(double w, double h) : width(w), height(h) {if (width <= 0 || height <= 0) {throw std::invalid_argument("Width and height must be positive");}}double getArea() const override {return width * height;}double getPerimeter() const override {return 2 * (width + height);}void printInfo() const override {std::cout << "Rectangle with width: " << width<< ", height: " << height << std::endl;Shape::printInfo();}
};// 圓形類
class Circle : public Shape {
private:double radius;static constexpr double PI = 3.14159265358979323846;public:Circle(double r) : radius(r) {if (radius <= 0) {throw std::invalid_argument("Radius must be positive");}}double getArea() const override {return PI * radius * radius;}double getPerimeter() const override {return 2 * PI * radius;}void printInfo() const override {std::cout << "Circle with radius: " << radius << std::endl;Shape::printInfo();}
};int main() {try {// 創建各種形狀Shape* shapes[] = {new Rectangle(4, 5),new Circle(3)};// 測試每個形狀for (Shape* shape : shapes) {shape->printInfo();std::cout << std::endl;delete shape;}} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;}return 0;
}
Rectangle和Circle堆Shape的實現關系,就可以表示為下圖。實現關系用?三角形加虛線來表示。
繼承:
實現是對一個沒有實現的接口類進行實例化,進行實現,是一個從抽象到具體的過程。而繼承是為了復用,繼承說的是派生類和基類之間的關系。
還是以上邊的圖形類代碼為例子,如果我們新增一個正方形的類,正方形是一個長和寬相等的特殊的矩形,所以正方形可以繼承矩形。那么正方形類的代碼如下:
// 正方形類
class Square : public Rectangle {
public:Square(double length) : Rectangle{length,length} {}
};
實現是為了多態,繼承是為了復用,可以看到,正方形類可以服用矩形類的函數和屬性,正方形類只需要實現自己的構造函數即可。
繼承關系用三角形加實現來表示。實現和繼承的區別是,一個是虛線,一個是實線。
2聚合和組合
聚合和組合,兩者均是表示整體與部分的關系,但是也是有區別的:
聚合:整體不存在了,部分還可以單獨存在
比如汽車和輪胎、發送機、變速箱的關系,汽車是由輪胎、發動機、變速箱等聚合而成,但是輪胎、發動機、變速箱可以單獨生產,在不有裝車之前就可以存在,如果汽車要處理掉,這輛車可以拆卸,拆卸之后,汽車不存在了,汽車的零件還可以單獨存在。
組合:整體不存在了,部分也就不存在了
比如公司和部門的關系,我們沒有見過不依賴于公司而獨立存在的部門,部門都是存在于公司內的,當公司倒閉,那么部門也就不存在了。
可以看到部分和整體之間的關系,組合的依賴更強。
下圖是聚合的關系,聚合用空心菱形加實線表示。
下圖是組合關系,組合用實心菱形加實現表示。
3依賴和關聯
依賴關系也可以叫引用關系,也可以叫使用關系,說的是在一個對象中使用了另一個對象,那么就可以說前者依賴后者。比如程序員在工作中會用到電腦,那么就可以說兩者的關系是依賴關系。
依賴關系如下圖所示,依賴用箭頭和虛線表示。
個人感覺關聯和依賴之間的概念,并不是很清晰,沒有很清晰的邊界,有時候關聯可以看做依賴,依賴也可以看做關聯。?關聯關系分單向關聯、雙向關聯、自關聯、多維關聯。
單向關聯,箭頭加實線表示。
雙向關聯,實線兩端均有箭頭,也可以兩端均沒有箭頭。
自關聯,指向自身的實線表示,可以帶箭頭,也可以不帶。
多維關聯,表示多個對象之間的關聯關系,可以用一個菱形來表示,當然也可以在多個對象之間兩兩關聯來表示。