集約とコンポジション

# 1. 要約
2つのクラスA, Bについて考える。

集約（Aggregation）: クラスAがクラスBに集約された時、Aが削除されてもBは存在し続ける。

コンポジション（Composition）:クラスAがクラスBに集約された時、集約されるBがAに対して**依存性**を持つ。Aが削除されると、Bも削除される。

# 2. 集約（Aggregation）
## 2.1. 集約とは?
集約は`has-a`関係と呼ばれ、クラスAが状態の一部としてクラスBを含む関連の形を表している。（クラスAがBを所有している状態）クラスは**独立して存在する**ことができ、クラスAを削除しても、クラスBは存在し続けることができる。

## 2.2. 例（集約）
車の構成情報を保持するクラス設計を考える。この設計には、以下のクラスがある。

- `Car` 
- `Wheel`

Carクラスは、Wheelクラスを**所有**しており、二つのクラスは関連している。Carクラスが削除されても、Wheelクラスはそれ自体で存在することができる。（車からタイヤを外しても、タイヤは存在し続けることができる）

## 2.3. コード（集約）
C++で、CarとWheelの関係をコードに表すと以下のようになる。

```Aggregation.cpp
#include <iostream>
#include <vector>
#include <string>

class Wheel {
public:
    std::string brand;

    // コンストラクタ -> インスタンス生成時に呼ばれる
    Wheel(const std::string& b) : brand(b) {
        std::cout << "Wheel created: " << brand << std::endl;
    }

    // デストラクタ -> deleteやスコープ終了によりオブジェクトが破棄されると呼ばれる
    ~Wheel() {
        std::cout << "Wheel destroyed: " << brand << std::endl;
    }

    void rotate() const {
        std::cout << brand << " wheel is rotating!" << std::endl;
    }
};

class Car {
private:
    std::vector<Wheel*> wheels;  // 集約: ポインタで保持。Wheelとの関係は弱い。

public:
    // タイヤの追加
    void addWheel(Wheel* w) {
        wheels.push_back(w);
    }

    // 集約関係にあるタイヤを回転させる
    void testDrive() const {
        for (Wheel* w : wheels) {
            w->rotate();
        }
        std::cout << "\n" << std::endl;
    }

    // デストラクタ -> deleteやスコープ終了によりオブジェクトが破棄されると呼ばれる
    ~Car() {
        std::cout << "\n" << "---- Program End ----" << std::endl;
        std::cout << "Car destroyed." << std::endl;
    }
};

int main() {
    std::cout << "\n---- create wheels ----" << std::endl;
    // タイヤを生成する。ポインタで保持するので、deleteしないとメモリリークになる。
    Wheel* w1 = new Wheel("Michelin1");
    Wheel* w2 = new Wheel("Michelin2");

    std::cout << "\n---- add wheels to a car ----" << std::endl;
    // 車を生成する。ポインタで保持するので、deleteしないとメモリリークになる。
    Car* car = new Car();  
    car->addWheel(w1);
    car->addWheel(w2);

    std::cout << "\n---- test drive ----" << std::endl;
    car->testDrive();

    std::cout << "\n---- delete car (but not wheels) ----" << std::endl;
    delete car;  // Carだけを明示的に消す

    // 集約関係であるため、Carを削除してもWheelは生存しており、使用可能。
    std::cout << "\n---- wheels are still alive ----" << std::endl;
    w1->rotate();
    w2->rotate();

    // 明示的にdelete
    std::cout << "\n---- now delete wheels ----" << std::endl;
    delete w1;
    delete w2;

    return 0;
}
```

## 2.4. 実行結果（集約）
```text
---- create wheels ----
Wheel created: Michelin1
Wheel created: Michelin2

---- add wheels to a car ----

---- test drive ----
Michelin1 wheel is rotating!
Michelin2 wheel is rotating!



---- delete car (but not wheels) ----

---- Program End ----
Car destroyed.

---- wheels are still alive ----
Michelin1 wheel is rotating!
Michelin2 wheel is rotating!

---- now delete wheels ----
Wheel destroyed: Michelin1
Wheel destroyed: Michelin2
```

CarとWheelは関連性があったが、Carを削除してもWheelは生きていて、Wheelのメソッドを実行することができる。

# 3. コンポジション（Composition）
## 3.1. コンポジションとは？
コンポジションは、**集約の特殊な形**とみることができ、集約される側が、集約する側に対して、**依存性**をもつ。このため、クラスAとBの間にコンポジションが適用されている場合には、クラスAを削除したとき、クラスBも同様に削除される。

コンポジションは、単純なクラスに依存性を持たせて、より複雑なクラスを作る場合などに使われる。これにより、保守がしやすいクリーンなコードを書くことができる。

## 3.2. 例（コンポジション）
図書館のソフトウェアシステムについて考える。このシステムでは、

- `Library`
- `Book`
- `Author`

の3つから構成されている。Libraryクラスが削除された場合、Library内にあるBookも削除されるべきである。(=コンポジション）

ただし、Authorは複数のBookにまたがる可能性があるので、独立して存在する。（=集約）


## 3.3. コード（コンポジション）
コンポジションの関係をコードで表すと以下の要になる。

```Composition.cpp
#include <iostream>
#include <string>
#include <vector>

class Author {
public:
    std::string name;
    Author(const std::string& n) : name(n) {}
};

class Book {
public:
    std::string title;
    Author* author;

    Book(const std::string& t, Author* a) : title(t), author(a) {}
    void print() const {
        std::cout << "BookInfo: \"" << title << "\" by " << author->name << std::endl;
    }
};

class Library {
private:
    std::vector<Book> books;

public:
    void addBook(const Book& book) {
        books.push_back(book);
    }

    Book* getFirstBook() {
        return &books[0];
    }
};

int main() {
    Book* outsideBookPtr = nullptr;

    Author* a = new Author("George Orwell");

    Library* lib = new Library();
    lib->addBook(Book("1984", a));

    outsideBookPtr = lib->getFirstBook();  // Bookの内部アドレスを保存
    outsideBookPtr->print();  // Bookの内容を表示

    delete lib;  // Libraryと中のBookが破棄される（ここで Book の寿命終了）

    std::cout << "\n---- trying to access deleted Book ----" << std::endl;
    outsideBookPtr->print();  // ここで未定義動作！
    std::cout << "---- end of access ----" << std::endl;
    delete a;
    return 0;
}
```
## 3.4. 実行結果（コンポジション）
```text
BookInfo: "1984" by George Orwell

---- trying to access deleted Book ----
BookInfo: "秦}鍬・&鍬
```

Libraryを削除した後に、Book内のメソッドを呼び出すと、未定義動作となり（=BookはLibraryと同時に削除されている為）、期待した結果とならないことが分かる。

# 4. 終わりに
OOPの基本となるコンポジションと、集約について説明をしてみた。もし間違いがあれば、コメントで教えてください。