---
# System prepended metadata

title: 類之間的關係
tags: [design pattern]

---

# 類之間的關係 

###### tags: `design pattern`

**<a href="https://hackmd.io/eJCtKmTbTB6GvJgqWToqJA">點此回到 Design pattern 目錄 </a>**

[toc]


## UML 統一建模語言
- 類
	- 上面是類別名稱
	- 中間是類別屬性
		- `[可見性]屬性名:類型[=默認值]`
	- 下面是類別方法
		- "+" 代表 public，"-" 代表private，"#" 代表protected，"~" 表示friend
		- `[可見性]名稱(參數列表)[:返回類型]`
![](https://i.imgur.com/hvIoHFB.png)
- 接口(Interface): 一種特殊的類，**它具有類的結構但不可被實例化，只可以被子類實現。它包含抽象操作，但不包含屬性**。它描述了類或組件對外可見的動作。在 UML 中，接口使用一個帶有名稱的小圓圈來進行表示。
![](https://i.imgur.com/96SHoaE.png)
- 類圖：來顯示系統中的類、接口、協作以及它們之間的靜態結構和關係的一種靜態模型。主要用於軟體系統的結構化設計，幫助簡化軟體系統的理解，是系統分析與設計階段的產物。

## 類之間的關係 
在軟體系統中，類通常不會孤立存在的，類與類之間存在各種關係。根據類與類的之間的耦合度從弱到強排列，UML中的類圖有以下幾種關係：**依賴關係、關聯關係、聚合關係、組合關係、泛化關係、實現關係**。其中泛化和實現的耦合度相等，他們耦合度是最強的。


### 依賴關係 use a 在方法行為中使用
- 一種使用關係，他是物件之間耦合度最弱的一種關聯方式，是臨時性的關聯。
- 在程式碼中，**某個類的方法通過<font color=red>局部變數</font>、方法的參數或者對静態方法的調用来訪問另一個類（被依赖類）中的某些方法來完成一些職責**
- 生活中例子： 人(class) 打電話(class)，打電話是人（類）的一個方法，電話是被依賴類。
- 依赖關係使用**帶箭頭的虛線**來表示
#### 使用 use a 
- B作為A的成員函數參數
- B作為A的成員函數的局部變量
- A的成員函數調用B的靜態方法


#### 人使用手機打電話
![](https://i.imgur.com/mwOmm55.png)
```cpp=
#include <iostream>
class MobilePhone
{
public:
    void transfer()
    {
        std::cout << "語音傳送" << std::endl;
    }
};
class Person
{
private:
    std::string name;
public:
	// 方法的參數
    void call(MobilePhone mp)
    {
        mp.transfer();
    }
};
int main(){
    MobilePhone mp;
    Person().call(mp);	
    return 0;
}

```
#### 人使用電腦、手機打遊戲
```python=
class Person:
    def play(self, deivce):
    # 通過方法的參數
        deivce.run()
        print("終於能打遊戲")

class Computer:
    def run(self):
        print("電腦已經打開,已經登入")
        
class Phone:
    def run(self):
        print("手機版英雄聯盟已登入")

if __name__ == "__main__":

    computer = Computer()
    phone = Phone()
    person = Person()
    person.play(computer)
    # 電腦已經打開,已經登入
    # 終於能打遊戲
    person.play(phone)
    # 手機版英雄聯盟已登入
    # 終於能打遊戲
```
#### 駕駛開車
```cpp=
#include<iostream>
class Car{
    public:
        static void run(){
            std::cout<<"Car is running"<<std::endl;
        }
};
class Driver{
public:
    // B作為A的成員函數參數
    void drive1(Car car){
        car.run();
    }
    // B作為A的成員函數的局部變量
    void drive2(){
        Car car ;
        car.run();
    }
    // A的成員函數調用B的靜態方法
    void drive3(){
        Car::run();
    }
};
int main(){
    Car car;
    Driver().drive1(car);
    Driver().drive2();
    Driver().drive3();
    return 0;
}
```

### 關聯關係
- 對象之間的一種引用關係，表示一類對象與另一類對象的關係。關聯關係是類與類之間最常用的一種關係。又**可分為一般關聯、聚合關係、組合關係**。
- 生活中例子：老師與學生、師父與徒弟、丈夫與妻子、學生與學生證、班級與學生、學生與課程、客戶與訂單、訂單與商品
- 在程式碼中通常將**一個類的對象作為另一個類的<font color=red>成員變量</font>来實現關聯關係**
> 可以為雙向、單向關聯（通常我們需要避免使用雙向關聯關係），是一種"has a"關係
![](https://i.imgur.com/njGgTaT.png)

- 雙向的關聯可以用**兩個箭頭或者沒有箭頭的實線表示**。單向的關聯用帶一個箭頭的實現表示，箭頭從使用類指向被關聯類。也可以在關聯線的兩端標注角色名，代表兩種不同的角色。

#### 老師教學生
```cpp=
#include <iostream>
#include <list>
class Student;
class Teacher;
class Teacher
{
private:
    std::string name;
    std::list<Student> stus; // 成員變量

public:
    Teacher(std::string name) : name(name){};
    void append_student(std::string st);
    void teach();
    const std::string GetName() const;
};
class Student
{
private:
    std::string name;
    std::list<Teacher> teas;  // 成員變量

public:
    Student(std::string name) : name(name){};
    void append_teacher(std::string t);
    void study();
    const std::string GetName() const;
};

void Student::append_teacher(std::string t)
{
    teas.push_back(Teacher(t));
}
void Student::study()
{
    for (Teacher teacher : teas)
    {
        std::cout << "Student " << this->name << " study from teacher:  " << teacher.GetName() << std::endl;
    }
}
const std::string Student::GetName() const { return name; }

void Teacher::append_student(std::string st)
{
    stus.push_back(Student(st));
}
void Teacher::teach()
{
    for (Student student : stus)
    {
        std::cout << "Teacher: " << Teacher::name << " teaches " << student.GetName() << std::endl;
    }
}
const std::string Teacher::GetName() const { return name; }
int main()
{
    Student student("Amy");
    student.append_teacher("Yoyo");
    student.append_teacher("Tim");
    student.study();

    Teacher teacher("Yoyo");
    teacher.append_student("Jason");
    teacher.append_student("Tim");
    teacher.teach();

    return 0;
	
	// Student Amystudy from teacher:  Yoyo
	// Student Amystudy from teacher:  Tim
	// Teacher: Yoyo teaches Jason
	// Teacher: Yoyo teaches Tim
}
```

#### 男生與女朋友吃飯、看電影
```python=
#  一對一
class Boy:

    def __init__(self, name,  girlFriend=None):
        # 在初始化的时候可以给一個對象的屬性設置成另一個類的對象
        self.girlFriend = girlFriend  # 一個男孩有一個女朋友

    def chi(self):
        if self.girlFriend:
            print(f"帶著他的女朋友{self.girlFriend.name}去吃飯")
        else:
            print("單身狗, 吃什麼吃? 滾去學習")

    def movie(self):
        if self.girlFriend:
            print(f"帶著他的女朋友{self.girlFriend.name}去看電影影")
        else:
            print("單身狗, 看什麼看? 滾去學習")


class Girl:
    def __init__(self, name):
        self.name = name

b = Boy("小明")
g = Girl("ABC")
b.chi()  # 單身狗, 吃什麼吃? 滾去學習

# 給小明介绍了一個女朋友
b.girlFriend = g
b.chi() # 帶著他的女朋友ABC去吃飯

g2 = Girl("QWER")
b.girlFriend = g2 # 換了女朋友
b.chi() # 帶著他的女朋友QWER去吃飯

```
### 聚合關係 has-a 屬性
- 聚合（Aggregation）關係是關聯關係的一種，是強關聯關係，是整体和部分之間的關係，是 has-a 的關係。
- 聚合關係也是通過**成員對象(屬性)實現**的，其中成員對象是整體對象的一部分，但是**成員對象可以脱离整体對象而獨立存在。**
	- 聚合、組合只能配合語義，結合上下文才能夠判斷出來，而只給出一段代碼讓我們判斷是關聯，聚合，則是無法判斷的。
- 生活中例子：學校與老師的關係，學校包含老師但如果學校停辦了，老師依然存在。
	- 電腦與CPU、公司與員工、班級與學生
- 聚合關係可以用帶**空心菱形的實線**來表示，菱形指向整体


![](https://i.imgur.com/ihWhkVr.png)

#### 大學裡有很多老師
```cpp=
#include<iostream>
#include<list>
class Teacher{
private:
    std::string name;
public:
    Teacher(std::string name):name(name){};
    void teaching(){
        std::cout<<"Teacher name is"<<name<<std::endl;
    }
    std::string GetName(){
        return name;
    }
};
class School{
private:
    std::list<Teacher> teas;
public:
    void GetAllTeacher(){
        for(Teacher t:teas){
            std::cout<<t.GetName()<<" ";
        }
    }
    void RollIn(std::string name){
        teas.push_back(Teacher(name));
    }
};
int main(){
    School school;
    school.RollIn("Yoyo");
    school.RollIn("Tim");
    school.RollIn("Tom");
    school.GetAllTeacher();
}

```

#### 車是一輛私家車，是司機財產的一部分
```cpp=
#include<iostream>
class Car{
public:
    void run(){
        std::cout<<"Car is running"<<std::endl;
    }
};
class Driver{
private:
    Car mycar;
public:
    // 使用成員變量形式實現關聯
    void drive(){
        mycar.run(); // 車是我自己的車，我“擁有”這個車
    }
};
int main(){
    Driver driver;
    driver.drive();
    return 0;
}
// 車是一輛私家車，是司機財產的一部分。則相同的代碼即表示聚合關係了
```
```python=
class School:
    def __init__(self, name):
        self.__teach_list = []
    def teas(self,teach):
        self.__teach_list.append(teach)
    def teaching(self):
        for t in self.__teach_list:
            t.work()
            
class Teacher:
    def __init__(self, name):
        self.name = name
    def work(self):
        print(f'{self.name}在上課')
        
x = School('中央學校')
t1 = Teacher('教師1')
t2 = Teacher('教師2')
t3 = Teacher('教師3')
t4 = Teacher('教師4')

x.teas(t1)
x.teas(t2)
x.teas(t3)
x.teas(t4)

x.teaching()
'''
教師1在上課
教師2在上課
教師3在上課
教師4在上課
'''
```


### 組合關係 cxmtains-a 在建構子或是類宣告時就new()
- 组合（Composition）關係也是關聯關係的一種，也表示類之間的整體與部分的關係，但它是一種更強烈的聚合關係，是 cxmtains-a 關係。
- 在组合關係中，整體對象可以控制部分對象的生命周期，一旦整體對象不存在，部分對象也將不存在，**部分對象不能脫離整體對象而存在。**
	- 聚合、組合只能配合語義，結合上下文才能夠判斷出來，而只給出一段代碼讓我們判斷是關聯，聚合，則是無法判斷的。
- 生活中例子：頭和嘴的關係，没有了頭，嘴也就不存在了。
	- 人與四肢
- 组合關係用帶**實心菱形的實線**來表示，菱形指向整體。


![](https://i.imgur.com/O46ryHh.png)


#### 頭與嘴的關係
```cpp=
#include<iostream>
#include<list>
class Mouth{
public:
    void eat(){
        std::cout<<"eat"<<std::endl;
    }
};
class Head{
private:
    Mouth mouth;
public:
    Head(){
        mouth = Mouth();
		
    }
    void eat(){
        mouth.eat();
    }
};
int main(){
    Head head;
    head.eat();
    
}
```
```java=
public class Person {  
    public Head head;  
    public Body body;  
      
    public Person(){  
        head = new Head();  
        body = new Body();  
    }  
}  
```

#### 車是司機的必須有的財產，要想成為一個司機必須要先有輛車
```cpp=
#include<iostream>
class Car{
public:
    void run(){
        std::cout<<"Car is running"<<std::endl;
    }
};
class Driver{
private:
    Car mycar;
public:
    // 使用成員變量形式實現關聯
    void drive(){
        mycar.run(); // 車是我自己的車，我“擁有”這個車
    }
};
int main(){
    Driver driver;
    driver.drive();
    return 0;
}
//車是司機的必須有的財產，要想成為一個司機必須要先有輛車，車要是沒了，司機也不想活了。而且司機要是不干司機了，這個車就砸了，別人誰也別想用。那就表示組合關係了。
```


### 泛化關係 is-a
- 泛化是耦合度最大的關係之一。
- 常見的父類與子類之間的關係，是一種**繼承**關係，是 is-a 的關係。
- 在程式碼實現時，使用物件導向的繼承機制來實現泛化關係。
	- 類可以繼承**抽像類**，類繼承父類都屬於這種關係
- 生活中例子：Student 類和 Teacher 類都是 Person 類的子類。
- 泛化關係用**帶空心三角箭頭的實線**來表示，箭頭從子類指向父類。

![](https://i.imgur.com/MFzcjra.png)

```cpp=
#include<iostream>
#include<list>

class Person{
protected:
    std::string name;
    int age;
public:
    Person(std::string name, int age):name(name), age(age){};
    void move(){};
    void say(){};
};
class Student:public Person{
private:
    std::string studentNumber ;
public:
    Student(std::string studentNumber, std::string name, int age):studentNumber(studentNumber), Person(name, age){};
    void study(){
        std::cout<<"Student "<<name<<" id is "<<studentNumber<<" and age is "<<age<<std::endl;
    }
};
class Teacher:public Person{
private:
    std::string teacherNumber;
public:
    Teacher(std::string teacherNumber, std::string name, int age):teacherNumber(teacherNumber), Person(name, age){};
    void teach(){
        std::cout<<"Teacher "<<name<<" id is "<<teacherNumber<<" and age is "<<age<<std::endl;
    }
};
int main(){
    Teacher teacher("105522098", "Tim", 29);
    teacher.teach();
    Student student("101201504","Amy",12);
    student.study();
	// Teacher Tim id is 105522098 and age is 29
	// Student Amy id is 101201504 and age is 12
}
```


### 實現關係
- 實現關係是**接口與實現類之間的關係**。
- 類實現了接口，類中的方法實現了接口中所聲明的所有的抽象方法。
- 父類中宣告(抽象)方法但不實現，（接口類）子類中override父類方法並實現
- 生活中例子：汽車和船實現了交通工具。
- 實現關係使用**帶空心三角箭頭的虛線**來表示，箭頭從實現類指向接口。
    
    
![](https://i.imgur.com/KrIcCY5.png)

```cpp=
#include<iostream>
class Vehicle{
public:
    virtual void move() = 0;
};
class Car:public Vehicle{
public:
    void move(){
        std::cout<<"Car moves"<<std::endl;
    }
};
class Ship:public Vehicle{
public:
    void move(){
        std::cout<<"Ship moves"<<std::endl;
    }
};
int main(){
    Car car;
    car.move();
    Ship ship;
    ship.move();
    return 0;
	// Car move
	// Ship moves
}
```
## SUMMARY




| 關係       | 依賴關係                               | 關聯關係         | 聚合關係             | 組合關係             | 泛化關係 | 實現關係 |
| ---------- | -------------------------------------- | ---------------- | -------------------- | -------------------- | -------- | -------- |
| 描述       | 某類通過方法調用另一個依賴類 |                  |           聚合           |         組合       |     繼承      |   接口   |
| 生活中例子 | 人們用手機打電話的關係                 | 老師教學生的關係 | 學校與老師的關係     |        頭與嘴的關係              |   學生和老師都是人的關係       |     汽車和船都是交通工具的關係     |
| UML表示    | 箭頭由使用類指向被依賴類虛線       |  無箭頭實線       | 空心菱形指向整體對象實線 | 實心菱形指向整體對象實線 |  空心三角形箭頭從子類指向父類的實線        |       空心三角形箭頭從實體類指向接口的虛線   |
| 關鍵字     |     use-a                                   |                  | has-a                |    cxmtains-a                  | is-a     |      接口    |



| 帶箭頭的虛線 | 實線箭頭 | 實心菱形實線 | 空心菱形實線 | 空心三角形虛線 | 空心三角形實線 |
| ------------ | -------- | ------------ | ------------ | -------------- | -------------- |
| 依賴         | 關聯     | 組合         | 聚合         | 實現           | 繼承           |




- 箭頭方向從子類指向父類
- **空心三角形箭頭**表示實現或繼承：虛線表示實現、實線表示繼承
- **菱形**表示組合或聚合：空心聚合、實心組合
- **線條**表示依賴或關聯：虛線依賴、實線關聯

### 關聯、組合、聚合
- 關聯是通過
	- 局部變數
	- 方法行參
	- 靜態方法
- 關聯關係
	- 成員變量
- 聚合
	- 整體與部分，可獨立存在
- 組合
	- 整體與部分，不可分割
## 參考資料
- [UML類別圖說明](https://ithelp.ithome.com.tw/articles/10241208)
- [Python教程：类之间的依赖关系与组合关系](https://www.cnblogs.com/q455674496/p/10145226.html)
- [C++类与类之间的存在的几种关系以及UML类图简单说明（依赖、关联、聚合、组合、泛化（继承）、实现](https://blog.csdn.net/JMW1407/article/details/107169451)



