本文參考文章:
類圖(Class Diagram)是面向對象系統建模中最常用和最重要的圖,是定義其它圖的基礎。類圖主要是用來描述系統中的類、接口以及它們之間的靜態結構和關係的一種靜態模型。
畫類圖的時候,理清類和類之間的關係是重點。類與類之間的關係有多種:
- 泛化(Generalization)
- 實現(Realization)
- 關聯(Association)
- 聚合(Aggregation)
- 組合(Composition)
- 依賴(Dependency)
其中,關聯關係又包括一般關聯、聚合關係、組合關係。
下面結合實例來說明,分別理解這些不同的關係。
泛化(Generalization)
泛化是一種繼承關係,它指出子類如何特化父類的所有特徵和行爲(例如老虎類是動物類的子類)。
- 關係說明:表示is-a的關係,是對象之間耦合度最大的一種關係,子類繼承父類的所有細節。
- 代碼體現:直接使用語言本身的繼承語法表達。
- 箭頭指向:使用帶三角箭頭的實線表示,箭頭從子類指向父類。
實現(Realization)
實現是一種類與接口的關係,表示類是接口所有特徵和行爲的實現(例如鉛筆刷是刷子接口的實現類)。
- 關係說明:如果將接口看作特殊定義的類,就類似於is-a的關係,具體類實現接口的所有細節。
- 代碼體現:直接使用語言本身的實現語法表達。
- 箭頭指向:使用帶三角箭頭的虛線,箭頭從實現類指向接口。
關聯(Association)
關聯是類與類之間最常用的一種關係,它是一種結構化關係,用於表示一類對象與另一類對象之間有聯繫(如汽車和輪胎、師傅和徒弟、班級和學生等),並沒有整體與部分的關係。
- 關係說明:關聯是對象之間的一種結構化關係,這種關係通常使用類的屬性表達。
- 代碼實現:通常將一個類的對象作爲另一個類的成員變量。
- 箭頭指向:使用帶箭頭的實線表示,箭頭從使用類指向被關聯的類。關聯可以是單向的、雙向的、自關聯的、多重關聯的,其中雙向的關聯可以有兩個箭頭或者沒有箭頭。
聚合(Aggregation)
聚合表示整體與部分的關係。在聚合關係中,部分對象是整體對象的一部分,但是部分對象可以脫離整體對象而獨立存在。例如汽車發動機(Engine)是汽車(Car)的部分,但是汽車發動機可以獨立存在,因此汽車和發動機就是聚合關係。
- 關係說明:表示has-a的關係,是一種不穩定的包含關係,關聯性較強於一般關聯,有整體與局部的關係,並且沒有了整體,而局部也可單獨存在。
- 代碼實現:聚合關係是關聯關係的一種,是強的關聯關係,關聯和聚合在語法上無法區分,必須考察具體的邏輯關係。通常將部分類的對象作爲整體類的成員變量,部分對象通常作爲構造方法、Setter方法或業務方法的參數注入到整體對象中。
- 箭頭指向:使用帶空心菱形的實線,菱形指向整體對象。
組合(Composition)
組合也表示整體和部分的關係。在組合關係中,整體對象可以控制部分對象的生命週期,一旦整體對象不存在,部分對象也將不復存在,部分對象與整體對象之間具有同生共死的關係。例如人的頭(Head)與嘴巴(Mouth), 嘴巴是頭的組成部分,但是如果頭沒了,嘴巴也就沒了,因此頭和嘴巴就是組合關係。
- 關係說明:表示contains-a的關係,是一種強烈的包含關係。組合類負責被組合類的生命週期,是一種更強的聚合關係。部分不能脫離整體存在。
- 代碼實現:組合關係是關聯關係的一種,是強的關聯關係,關聯和組合在語法上無法區分,必須考察具體的邏輯關係。通常將部分類的對象作爲整體類的成員變量,然後通常在整體類的構造方法中直接實例化部分類對象。
- 箭頭指向:使用帶實心菱形的實線,菱形指向整體。
依賴(Dependency)
依賴是對象之間最弱的一種關聯方式,是臨時性的關聯。
- 關係說明:依賴關係是一種使用關係,一個類調用被依賴類中的某些方法而得以完成這個類的一些職責,而被依賴事物的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關係。
- 代碼實現:通常將被依賴類作爲依賴類的方法的局部變量、方法的形參、方法中調用被依賴類的靜態方法。
- 箭頭指向:使用帶箭頭的虛線,指向被依賴者。
綜上所述,各種類圖關係之間,彼此的強弱關係爲:泛化 = 實現> 組合> 聚合> 關聯> 依賴。
#include "blocking_queue.h"
template <typename T>
blocking_queue<T>::blocking_queue(const int max_size) {
this->m_max_size = max_size;
this->m_task_size = 0;
this->m_unfinished_tasks = 0;
this->m_deque = new deque<T>(max_size);
this->m_mutex = new pthread_mutex_t;
this->m_not_empty = new pthread_cond_t;
this->m_not_full = new pthread_cond_t;
this->m_all_tasks_done = new pthread_cond_t;
pthread_mutex_init(this->m_mutex, NULL);
pthread_cond_init(this->m_not_empty, NULL);
pthread_cond_init(this->m_not_full, NULL);
pthread_cond_init(this->m_all_tasks_done, NULL);
}
template <typename T>
blocking_queue<T>::~blocking_queue() {
if(this->m_deque != NULL){
delete this->m_deque;
}
pthread_mutex_destroy(this->m_mutex);
pthread_cond_destroy(this->m_not_empty);
pthread_cond_destroy(this->m_not_full);
pthread_cond_destroy(this->m_all_tasks_done);
delete this->m_mutex;
delete this->m_not_empty;
delete this->m_not_full;
delete this->m_all_tasks_done;
}
template <typename T>
bool blocking_queue<T>::empty() {
bool result = false;
pthread_mutex_lock(this->m_mutex);
result = this->m_task_size == 0;
pthread_mutex_unlock(this->m_mutex);
return result;
}
template <typename T>
bool blocking_queue<T>::full() {
bool result = false;
pthread_mutex_lock(this->m_mutex);
result = this->m_task_size == this->m_max_size;
pthread_mutex_unlock(this->m_mutex);
return result;
}
template <typename T>
int blocking_queue<T>::clear() {
pthread_mutex_lock(this->m_mutex);
this->m_task_size = 0;
this->m_unfinished_tasks = 0;
this->m_deque->clear();
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
template <typename T>
int blocking_queue<T>::size() {
int iResult = 0;
pthread_mutex_lock(this->m_mutex);
iResult = (int)this->m_deque->size();
pthread_mutex_unlock(this->m_mutex);
return iResult;
}
template <typename T>
int blocking_queue<T>::put(const T &task, const bool block, const int timeout) {
struct timespec ts = {0, 0};
struct timeval now = {0, 0};
gettimeofday(&now, NULL);
ts.tv_sec = now.tv_sec + timeout;
ts.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(this->m_mutex);
if(block == false) {
if(this->m_task_size == this->m_max_size) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_FULL;
}
}
else if(timeout < 0){
while(this->m_task_size == this->m_max_size) {
pthread_cond_wait(this->m_not_full, this->m_mutex);
}
}
else{
while(this->m_task_size == this->m_max_size) {
int iResult = SUCCESS;
iResult = pthread_cond_timedwait(this->m_not_full, this->m_mutex, &ts);
if(iResult == ETIMEDOUT) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_TIMEOUT;
}
else if(iResult != SUCCESS) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_OTHER;
}
}
}
this->m_deque->push_back(task);
this->m_task_size += 1;
this->m_unfinished_tasks += 1;
pthread_cond_signal(this->m_not_empty);
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
template <typename T>
int blocking_queue<T>::put_nowait(T& task) {
return put(task, false);
}
template <typename T>
int blocking_queue<T>::get(T& task, const bool block, const int timeout) {
struct timespec ts = {0, 0};
struct timeval now = {0, 0};
gettimeofday(&now, NULL);
ts.tv_sec = now.tv_sec + timeout;
ts.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(this->m_mutex);
if(block == false){
if(this->m_task_size == 0) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_EMPTY;
}
}
else if(timeout < 0){
while(this->m_task_size == 0) {
pthread_cond_wait(this->m_not_empty, this->m_mutex);
}
}
else {
while(this->m_task_size == 0) {
int iResult = SUCCESS;
iResult = pthread_cond_timedwait(this->m_not_empty, this->m_mutex, &ts);
if(iResult == ETIMEDOUT) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_TIMEOUT;
}
else if(iResult != SUCCESS) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_OTHER;
}
}
}
memcpy(&task, &this->m_deque->front(), sizeof(T));
this->m_deque->pop_front();
this->m_task_size -= 1;
pthread_cond_signal(this->m_not_full);
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
template <typename T>
int blocking_queue<T>::get_nowait(T& task) {
return get(task, false);
}
template <typename T>
int blocking_queue<T>::task_done() {
int unfinished = 0;
pthread_mutex_lock(this->m_mutex);
unfinished = this->m_unfinished_tasks - 1;
if(unfinished < 0) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_VALUE;
}
else if(unfinished == 0) {
pthread_cond_broadcast(this->m_all_tasks_done);
}
this->m_unfinished_tasks = unfinished;
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
template <typename T>
int blocking_queue<T>::join() {
pthread_mutex_lock(this->m_mutex);
while(this->m_unfinished_tasks != 0) {
pthread_cond_wait(this->m_all_tasks_done, this->m_mutex);
}
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
#ifndef _BLOCKING_QUEUE_H
#define _BLOCKING_QUEUE_H
#include <iostream>
#include <deque>
#include <pthread.h>
#include <sys/time.h>
#define SUCCESS 0
#define ERROR_EMPTY 1
#define ERROR_FULL 2
#define ERROR_TIMEOUT 3
#define ERROR_ARGUMENT 4 // 寮傚父鐨勫弬鏁?
#define ERROR_VALUE 5 // 鍙橀噺鐨勫彇鍊煎紓甯?
#define ERROR_OTHER 9999 // 鍏朵粬鏃犻渶鍏崇郴鐨勯敊璇?
using namespace std;
template <typename T>
class blocking_queue{
public:
blocking_queue(const int max_size){
this->m_max_size = max_size;
this->m_task_size = 0;
this->m_unfinished_tasks = 0;
this->m_deque = new deque<T>(max_size);
this->m_mutex = new pthread_mutex_t;
this->m_not_empty = new pthread_cond_t;
this->m_not_full = new pthread_cond_t;
this->m_all_tasks_done = new pthread_cond_t;
pthread_mutex_init(this->m_mutex, NULL);
pthread_cond_init(this->m_not_empty, NULL);
pthread_cond_init(this->m_not_full, NULL);
pthread_cond_init(this->m_all_tasks_done, NULL);
}
~blocking_queue(){
if(this->m_deque != NULL){
delete this->m_deque;
}
pthread_mutex_destroy(this->m_mutex);
pthread_cond_destroy(this->m_not_empty);
pthread_cond_destroy(this->m_not_full);
pthread_cond_destroy(this->m_all_tasks_done);
delete this->m_mutex;
delete this->m_not_empty;
delete this->m_not_full;
delete this->m_all_tasks_done;
}
bool empty(){
bool result = false;
pthread_mutex_lock(this->m_mutex);
result = this->m_task_size == 0;
pthread_mutex_unlock(this->m_mutex);
return result;
}
bool full(){
bool result = false;
pthread_mutex_lock(this->m_mutex);
result = this->m_task_size == this->m_max_size;
pthread_mutex_unlock(this->m_mutex);
return result;
}
int clear(){
pthread_mutex_lock(this->m_mutex);
this->m_task_size = 0;
this->m_unfinished_tasks = 0;
this->m_deque->clear();
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
int size(){
int iResult = 0;
pthread_mutex_lock(this->m_mutex);
iResult = (int)this->m_deque->size();
pthread_mutex_unlock(this->m_mutex);
return iResult;
}
int put(const T& task, const bool block = true, const int timeout = -1){
struct timespec ts = {0, 0};
struct timeval now = {0, 0};
gettimeofday(&now, NULL);
ts.tv_sec = now.tv_sec + timeout;
ts.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(this->m_mutex);
if(block == false) {
if(this->m_task_size == this->m_max_size) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_FULL;
}
}
else if(timeout < 0){
while(this->m_task_size == this->m_max_size) {
pthread_cond_wait(this->m_not_full, this->m_mutex);
}
}
else{
while(this->m_task_size == this->m_max_size) {
int iResult = SUCCESS;
iResult = pthread_cond_timedwait(this->m_not_full, this->m_mutex, &ts);
if(iResult == ETIMEDOUT) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_TIMEOUT;
}
else if(iResult != SUCCESS) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_OTHER;
}
}
}
this->m_deque->push_back(task);
this->m_task_size += 1;
this->m_unfinished_tasks += 1;
pthread_cond_signal(this->m_not_empty);
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
int put_nowait(T& task){
return put(task, false);
}
int get(T& task, const bool block = true, const int timeout = -1){
struct timespec ts = {0, 0};
struct timeval now = {0, 0};
gettimeofday(&now, NULL);
ts.tv_sec = now.tv_sec + timeout;
ts.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(this->m_mutex);
if(block == false){
if(this->m_task_size == 0) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_EMPTY;
}
}
else if(timeout < 0){
while(this->m_task_size == 0) {
pthread_cond_wait(this->m_not_empty, this->m_mutex);
}
}
else {
while(this->m_task_size == 0) {
int iResult = SUCCESS;
iResult = pthread_cond_timedwait(this->m_not_empty, this->m_mutex, &ts);
if(iResult == ETIMEDOUT) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_TIMEOUT;
}
else if(iResult != SUCCESS) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_OTHER;
}
}
}
memcpy(&task, &this->m_deque->front(), sizeof(T));
this->m_deque->pop_front();
this->m_task_size -= 1;
pthread_cond_signal(this->m_not_full);
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
int get_nowait(T& task){
return get(task, false);
}
int task_done(){
int unfinished = 0;
pthread_mutex_lock(this->m_mutex);
unfinished = this->m_unfinished_tasks - 1;
if(unfinished < 0) {
pthread_mutex_unlock(this->m_mutex);
return ERROR_VALUE;
}
else if(unfinished == 0) {
pthread_cond_broadcast(this->m_all_tasks_done);
}
this->m_unfinished_tasks = unfinished;
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
int join(){
pthread_mutex_lock(this->m_mutex);
while(this->m_unfinished_tasks != 0) {
pthread_cond_wait(this->m_all_tasks_done, this->m_mutex);
}
pthread_mutex_unlock(this->m_mutex);
return SUCCESS;
}
private:
deque<T>* m_deque;
int m_max_size;
int m_task_size;
int m_unfinished_tasks;
pthread_mutex_t* m_mutex;
pthread_cond_t* m_not_empty;
pthread_cond_t* m_not_full;
pthread_cond_t* m_all_tasks_done;
};
#endif //_BLOCKING_QUEUE_H
#include <unistd.h>
#include "blocking_queue.h"
using namespace std;
void* customer(void* arg) {
int task = -1;
blocking_queue<int>* bq = (blocking_queue<int>*)arg;
for(int i = 0; i < 100; ++i) {
cout << "customer get : " << bq->get(task) << endl;
usleep(100);
}
}
void* producer(void* arg) {
blocking_queue<int>* bq = (blocking_queue<int>*)arg;
for(int i = 0; i < 1000; ++i) {
bq->put(i);
cout << "producer put :" << i << endl;
}
}
int main(int argc, char** argv) {
pthread_t tid[2];
blocking_queue<int>* bq = new blocking_queue<int>(500);
pthread_create(tid, NULL, producer, (void*)bq);
pthread_create(tid+1, NULL, customer, (void*)bq);
pthread_join(*tid, NULL);
pthread_join(*(tid+1), NULL);
cout << "the number of bq is : " << bq->size() << endl;
}