『関連 vs 集約』比較でわかるクラス図

ソフトウェア工学を学ぶなかで、クラス図描いたらプログラムの関係が整理できて素敵じゃん!やってみよう!と思いますよね。でも描き始めたら、

リトル
リトル

関連と集約の違いがわからなくてモヤモヤする。んー、やめようかな。別にクラス図描きたかったわけじゃないし。プログラム描きたかったんだし。

Yugo
Yugo

わかならい用語多くてモヤモヤするよね!
抽象的な説明だと、どちらも同じでは?と疑問が残ります。

そこでこの記事では、なるべく多くの方に関連と集約の違いを伝えられるように、日本語、PlantUML、Pythonを使って丁寧に説明します。あなたのクラス図への熱量が冷めないうちに。

概要

クラス同士が関連し合うとき、条件が増えるごとに呼び方が変わります。関連は柔軟な関係で、クラス間の入れ替えが比較的簡単。集約はもうすこし構造的な関係で、部分を入替えるときに全体を見直して整合をとる必要があり、クラスの入れ替えが比較的難しい。

関連集約合成集約
記号ーーー◇ー◆
生存期間管理しない管理する完全に管理する
クラスの結びつき弱い強いより強い

関連(Association)

関連は、単純に一方のクラスが他方のクラスのメソッドを使ったり、データを参照したりすることを表しています。使っているだけですので、使用するクラスを変更することが比較的簡単な関係にあります。


たとえば、魔法使いの学校で、ある魔法使いが杖を借りている場面を考えてみましょう。登場人物は、Wizardクラス(魔法使い)とStaffクラス(杖)です。魔法使いは、杖を借りただけですので、使い終わったら、返却します。また、次の日、複数ある杖の中から別の杖を選んで借りても問題ありません。このような、比較的ゆるい関係のことを、関連と表しています。

Wizardクラス(魔法使い)は、Staffクラス(杖)を使っているだけで、所有しているわけではないことに注意してください。つまり、Wizardクラス(魔法使い)にとって、特定のStaffクラス(杖)が必要なわけではありません。たまたま見つけた杖を使っているのです。

クラス図でみる関連

関連のクラス図の例を下図で示します。リンク記号で関連を表しています。

@startuml
class Wizard {
  name : String
  castSpell() : void
}

class Staff {
  material : String
  castMagic() : void
}

Wizard "1" o-- "1" Staff : owns
@enduml

Pythonでみる関連

関連のPythonスクリプトの例は、次のとおりです。このプログラムでは、WizardクラスとStaffクラスの生存期間は無関係になっています。もし、魔法使い(Wizardクラス)が敵に倒されても、杖(Staffクラス)は生き残ります。

class Staff:
    def __init__(self, spell_name):
        self.spell_name = spell_name

    def cast_spell(self):
        print(f"Casting {self.spell_name}!")

class Wizard:
    def __init__(self, name):
        self.name = name
        self.staffs = []

    def use_magic(self):
        if not self.staffs:
            print(f"{self.name} has no staff to cast spells with!")
        else:
            print(f"{self.name} starts casting spells with their staffs:")
            for staff in self.staffs:
                staff.cast_spell()

    def add_staff(self, staff):
        self.staffs.append(staff)
        print(f"{self.name} has acquired a new staff for casting {staff.spell_name}.")

# Create a wizard
wizard = Wizard("Gandalf")

# Create staffs
fire_staff = Staff("Fireball")
ice_staff = Staff("Ice Blast")

# Add staffs to the wizard
wizard.add_staff(fire_staff)
wizard.add_staff(ice_staff)

# Wizard uses magic
wizard.use_magic()

集約(Aggregation)

クラス図でみる集約

@startuml
class AdventureParty {
  addMember()
  removeMember()
}

class Wizard {
  useMagic()
}

AdventureParty o-- "0..*" Wizard
@enduml

Pythonでみる集約

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

    def use_magic(self):
        print(f"{self.name} uses magic!")

class AdventureParty:
    def __init__(self):
        self.members = []

    def add_member(self, wizard):
        if wizard not in self.members:
            self.members.append(wizard)
            print(f"{wizard.name} has been added to the party.")
        else:
            print(f"{wizard.name} is already a member of the party.")

    def remove_member(self, wizard):
        if wizard in self.members:
            self.members.remove(wizard)
            print(f"{wizard.name} has been removed from the party.")
        else:
            print(f"{wizard.name} is not a member of the party.")

# 魔法使いのインスタンスを作成
wizard1 = Wizard("Gandalf")
wizard2 = Wizard("Saruman")

# 冒険者パーティーのインスタンスを作成
party = AdventureParty()

# パーティーに魔法使いを追加
party.add_member(wizard1)
party.add_member(wizard2)

# 魔法使いが魔法を使う
wizard1.use_magic()
wizard2.use_magic()

# パーティーから魔法使いを削除
party.remove_member(wizard1)

コメント

タイトルとURLをコピーしました