Help us understand the problem. What is going on with this article?

【コードで分かるUMLシリーズ】クラス図の書きかた(集約とコンポジションの意味の違い)

追記(2017/11/26)

この記事で紹介している内容を使って、集約やコンポジションの実装を補助するクラスを作りました。
UMLの関連を分かりやすく実装するクラス(C#)をどうぞ。

追記(2020/03/03)

集約のコードの中に、コレクション要素に追加/削除するときのチェックについてコメントを入れました。

集約とコンポジションの使い分け

クラス図で、「白抜きのひし形(集約)」と「黒塗りのひし形(コンポジション)」をどのように使い分けたらよいのか? 悩んだ経験はありませんか?

集約とコンポジションの違いを誤解している人がいるようですので、同じ誤解をしている人のために記事にします。

モデリングだけだと、ピンとこない人のために、サンプルコードも付けました。

UMLをぼんやり知っているOさんの答え

白抜きのひし形は「has-a」=「所有している」という意味。
黒塗りのひし形は「part-of」=「~の一部」という意味。
だから、「人は車を所有する」は、白いひし形で表現し、「タイヤは車の一部」は、黒いひし形で表現する。
wrong_agg.png

これは、「part-of」が「has-a」と呼ばれることがあるために起きている誤解なのかも知れません。

正しい意味

まず、「ひし形」自体が「全体と部分」を表現しています。
つまり、白抜きも黒塗りも、ともに「全体と部分」=「part-of」です。
same_agg.png

白抜きは、全体と部分としての結びつきが緩やかで、
黒塗りは、全体と部分としての結びつきが強い、
という違いです。

白抜きのひし形(集約)の正しい意味

白抜きのひし形(集約)は、UML1.3のときは「共有集約」と呼ばれていました。
これは、複数の「全体」インスタンスが、1つの「部分」インスタンスを共有する
という意味です。
これが、全体と部分としての結びつきが緩やか、ということです。
「集約」と呼ばれるようになってからも、この意味自体は変わっていません。

例えば、「図形」には「スタイル(※線の色や図形の塗りつぶし)」があり、これは全体と部分とみることができます。
1つの「スタイル」インスタンスを、複数の「図形」インスタンスが共有しています。

white_agg.png

黒塗りのひし形(コンポジション)の正しい意味

黒塗りのひし形(コンポジション)は、共有集約と違い、「部分」が複数の「全体」に共有されません。
つまり、唯一の「全体」インスタンスが、1つの「部分」インスタンスを所属させます。
また、「部分」は「全体」と生存期間を共にすると考えられています。
そのため、「全体」側を削除すると「部分」側も連鎖的に削除されます。
ただし、全体を削除する前に「部分」を取り外すことで、連鎖的な削除を免れることは可能です。

例えば、「車」と「タイヤ」は「全体と部分」の関係で、1つの「タイヤ」インスタンスは、1つの「車」インスタンスにしか属すことができません。
車を廃車にすると、タイヤも壊されてしまいます。
ただし、車からタイヤを外してから廃車にしてしまえば、タイヤは別の車に付け替えることもできます。

black_agg.png

誤解していた「所有」はどう描くのか?

いわゆる「所有」、
人(飼い主)は、犬を飼っている
社員は、社員証を持っている
は、ただの関連です。
「所有」だと ひし形のような何か特別な記号で描く、ということ自体が誤解です。

relation.png
relation_has.png

C#サンプルコード

最後に、集約とコンポジションのサンプルコードを書いてみます。
image.png

集約

Hoge(全体)がPiyo(部分)を複数含んでいます。
部分をprivate List<Piyo> m_piyosで保持して、AddとRomoveだけをHoge経由で公開しています。
わざわざprivateなのは、
・List自体を外部から変更されないようにする
・Hogeが知らないところでm_piyosの中身を変えられないようにする
ためです。

車とタイヤの例で考えてみると、m_piyosを外部から= new List<Piyo>();されたら、
タイヤのシャフトのあたりを、他の空間と交換されてしまうような感じですよね。

また、Hogeが知らないところでm_piyosの中身を変えるということは、
車が街中を走っている途中で、突然、工場からタイヤを交換することができてしまったりする感じですよね。

もし、こんな世界観でプログラミングしていたら、不具合だらけですよね。
だから、モデリングで概念上の世界観を可視化したら、その世界観をコーディングで崩さないように気を付けて(街中を走行中に工場からタイヤを交換出来ないような)コードで実装するのです。

なお、Hogeのpublicメソッド名がAddではなくAddpiyoなのは、複数の「部分」を扱うことがあるためです。

Hoge.cs
class Hoge
{
    // 集約
    private List<Piyo> m_piyos = new List<Piyo>();

    public Hoge()
    {
    }

    public void Addpiyo(Piyo piyo)
    {
        // チェックが必要ならば、この辺でする。
        // 車とタイヤの例ならば、「車」クラスが「走行中」状態のときにAddされないようにガードするとか。
        m_piyos.Add(piyo);
    }

    public bool Removepiyo(Piyo piyo)
    {
        // Addと同じく、チェックが必要ならば、この辺でする。
        return m_piyos.Remove(piyo);
    }
}
Piyo.cs
class Piyo
{
    // 特になし
}

コンポジション

基本的には集約と変わりませんが、全体側を削除したときに部分側を削除する点が異なります。
ただ、C#はガベージ コレクションを使うので、それに任せるならば、集約と同じ実装で良いと考えます。
一応、Disposeで削除できるだけ削除するサンプルを書いてみました。
Disposeの実装方法については、下記の記事を参考にさせて頂きました。

Hoge.cs
class Hoge : IDisposable
{
    // コンポジション
    private List<Piyo> m_piyos = new List<Piyo>();

    public Hoge()
    {
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            foreach(Piyo piyo in m_piyos)
            {
                piyo.Dispose();
            }
            m_piyos.Clear();
            m_piyos = null;
        }
    }

    ~Hoge()
    {
        Dispose(false);
    }

    public void Addpiyo(Piyo piyo)
    {
        m_piyos.Add(piyo);
    }

    public bool Removepiyo(Piyo piyo)
    {
        return m_piyos.Remove(piyo);
    }
}
Piyo.cs
class Piyo : IDisposable
{
    // Disposeなど、削除に必要なものを実装
}

以上のサンプルコードでは、「部分の削除」を入れましたが、
あと、もう一つのコンポジションの特徴である「複数の全体に共有されないこと」の実現を追記したサンプルコードは、
別の記事コンポジションのサンプルコード(「クラス図の書きかた(集約とコンポジションの意味の違い)」の補足) に記載しました。
そちらもご覧ください。

偉そうなクラスがコンポジションではないです

(2020/4/9 追記)
なんとなく、主導権を持っているほうや、管理している側が、コンポジションだと思って
モデリングしてしまう生徒さんを見ます。
いくら注意しても、なかなか直らないです。
そんな例を。

image.png
こういうのは、一見、問題なさそうに見えるけれど、
概念的に捉えると、結構おかしいです。
この図では、
「登録ユーザー」という概念は、「システム」という全体概念の一部を構成する概念だ、
と描かれています。
ゆえに、「システム」が消失すると「ユーザー」もその一部だから消失する、
という意味です。
ソフトウェアシステムを作っている人の頭で考えたら、おかしくなさそうだけれども、
人間が普通に持っている世界観からしたら、気が狂っていると思いませんか?

image.png
こちらの図では、
「ログオン先のシステム」というのは「ユーザー」という全体概念の一部を構成する概念だ、
となっています。
「ユーザー」が消えると、「システム」の実体も消えてくれる。
ユーザー登録を変えたら、「システム」が消えちゃうのかなぁ?
と思います。

普通の人が持っている世界観からしたら、変だと思いませんか?
これで、普通の人とシステム開発について話しようだなんて、、、。

、、、でも、こんなモデルでおかしいと感じない人だって、
次のモデルを見たら、なんか「やばい」って、感じるかも知れません。

image.png
「こども」は「お父さん」の一部分であって、
「お父さん」が死んだら、「こども」はその一生を共にする(つまり、一緒に死ぬ)ものだ、
というモデルですね。

絵的に言うと、↓な感じ
寄生獣 amazon.co.jp より

先ほどの「システムとユーザー」をコンポジションにしている人は、これと同じことをしているのですよ...。

こんなモデル描いてたら、
一般的な概念を持ったユーザーとお話ししていても、ユーザーに寄り添った考え方ができないですね...。

「だって、システムはこうなっているんですから!」
「だって、仕様ですから!」
って、自分の考えかたのほうが不自然だってことに
気づかなくなってしまうのかも知れませんね。

参考

『オブジェクト指向システム開発 分析・設計・プログラミングへの実践的アプローチ』
              本位田 真一・山城 明宏 著 日経BP出版センター
『UMLモデリングのエッセンス 第2版 標準オブジェクトモデリング言語入門』
   マーチン・ファウラー/ケンドール・スコット 著 羽生田 栄一 監訳 翔泳社

『C# によるプログラミング入門 [メモリとリソース管理] [雑記] Dispose にまつわる余談』

azuki8
UMLモデリングでスムーズなコーディングを。 ★アメブロのほうが更新頻繁です★ 豆蔵HPの連載 https://www.mamezou.com/techinfo/modeling_ddd/uml_miss_01
https://ameblo.jp/azukizenzai8
mamezou
先進の工学的手法と独自のスタンス。お客様とともにビジネスを活かすITを追求します。
http://www.mamezou.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした