クラス図
概要
クラス図とはクラスの属性やクラス間の関係から
システムの構造を表した図のことです。
クラス
クラスはクラス内に定義しているメンバのアクセス指定子や
メンバの型、引数や戻り値を記述します。
クラスの構成
クラスの基本は以下の構成になっています。
①.クラス名
クラスで定義しているクラス名を記述します。
②.メンバ変数
クラスで定義しているメンバ変数を記述します。
書式例:
[可視性] 変数名[:データ型][多重度][=初期値][{プロパティ}]
具体例:
- m_Hp:int
メンバ変数の設定は変数名が必須で、その他のパラメータは
設定が必要ならば記述します。
③.メンバ関数
クラスで定義しているメンバ関数を記述します。
書式例:
[可視性] 関数名[(引数)][:戻り値の型][{プロパティ}]
具体例:
- PrintParam : () : void
メンバ関数の設定も関数名が必須で、その他のパラメータは
設定が必要ならば記述します。
可視性
クラスで設定するアクセス指定子などは可視性と呼ばれており、
クラス図では記号で以下の4種類の記号で表現しています。
多重度
多重度はクラス間の個数情報を設定するために使用する値です。
クラスのインスタンスに対して他のクラスのインスタンスが
いくつ存在するかを表しています。
多重度の表記内容は以下の通りです。
以下の図が多重度の例になります。
上の図では車を例に出していますが、車1台につき
ハンドルが1つ、タイヤが4つ、ドアが2~4つあり、
更にドアは1つにつきドアノブが1つあるということを示しています。
プロパティ文字列
プロパティ文字列は「readonly」や「abstract」といったパラメータです。
先ほど紹介した多重度のパラメータにも「ordered」や
「unordere」「unigque」などがあります。
プロパティ文字列例
ordered:
多重度の指定がされているインスタンスに対して
何らかの順序性が存在する
unordered:
多重度の指定がされているインスタンスに
順序性は存在しない
unique:
インスタンスの重複がない
コードとクラス図の例
以下はコードとクラス図の例です。
コード
class Character
{
public:
Character() : m_Hp(0), m_Mp(0)
{
}
private:
int m_Hp;
int m_Mp;
};
クラス図
関連
関連とはクラスとクラスの関係を表しており二つのクラスを線で結びます。
クラス間の関係の一例は以下の通りです。
クラス間の関係の例
・クラスAでクラスBのメンバ関数を呼び出す
・クラスAのメンバとしてクラスBがある
・クラスAにはクラスBをクラスBにはクラスAのメンバを持ち
お互いがやり取りできるようにしている
「クラス」の項目で説明をしている多重度も関連で表現を行うことも可能です。
2項関連
2つのクラス間の関連を2項関連と呼びます。
「関連」という言葉は基本的に2項関連を指します。
クラス図では関連は直線で表現します。
3つ以上のクラスに関連がある関連をN項関連と呼び、
クラス図では関連させたいクラスをそれぞれひし形につなげます。
コード例
GameDataManager
{
public:
GameDataManager *GetInstance()
{
static GameDataManager instatnce;
return &instance;
}
int GetPlayerHp() { return m_PlayerHp; }
void SetPlayerHp(int hp) { m_PlayerHp = hp; }
private:
int m_PlayerHp;
};
class Player
{
public:
void Control()
{
// プレイヤーのHPを更新
GameDataManager::GetInstance()->SetPlayerHp(m_Hp);
}
private:
int m_Hp;
};
上のコードではGameDataManagerのPlayerHpを更新するために
PlayerクラスのHPをGameDataManagerクラスの
SetPlayerHpで設定しています。
2つのクラスがやり取りを行っているので、「関連」していると考えられます。
この関連をクラス図にした内容が以下の図となります。
関連名
関連名はクラス間の関連の内容を表現するために使用します。
クラス図では関連の線の中央に関連の内容を記述します。
また、関連の方向性の関係を明確にするために▲を使用することができます。
上の画像ではPlayerクラスとHpGaugeクラスの関連を図示しており、
関連名を「データ保存」としています。
これに▲をつけることで「PlayerクラスはHpGaugeを利用して
HPの状態を描画する」関係であることを示しています。
関連端名
関連の線の終端を関連端と呼び、関連先クラスに対して持つ役割を
記述することができます。
この内容を関連端名と呼ばれています。
上の画像ではPlayerクラスは「データ担当」、
HpGaugeは「描画担当」として役割を割り当てています。
誘導可能性
誘導可能性とは片方のクラスが
もう片方のクラスを参照できるかどうかを表します。
クラス図では参照されるクラスに対して線の先端に矢印を記述し、
参照するクラスに対しては×を記述します。
通常の関連端 => 矢印
関連端のクラスから矢印のクラスの参照が可能
矢印 => 通常の関連端
矢印のクラスから関連端の参照は不可能
上の図ではPlayerクラスはHpGaugeクラスを参照できないので、
Playerクラスに矢印を設定して誘導可能性を表現しています。
集約
集約とは1つのクラスが複数のクラスで構成されていたり、
別のクラスを保持しているような「全体と部分」の
関係になっている場合に使用します。
図ではクラス間を線でつなぎ、全体のクラス側の関連端でひし形を記述します。
上の図ではPlayerクラスの構成部分の1つとしてWeaponクラスがあることや、
多重度の設定でWeaponが0(NULL)の場合もあることも一緒に表しています。
コンポジション
コンポジションも集約の1種で複数のクラスで1つクラスが構成されていて
集約よりも結びつきの関係が強い場合に使用します。
以下の特徴が含まれている場合は集約ではなく、
コンポジションの意味合いが強い結びつきと考えられます。
クラス図では集約のひし形を黒く塗りつぶします。
コンポジションの特性
全体と生存期間が一緒:
全体のクラスのインスタンスを削除する際に
部品クラスのインスタンスも削除する
部分のみでは機能が活きない:
全体クラスの機能ありきで存在しており、
部分クラス単体の機能では存在の意味が少ない
部分クラスがなければ動作しない:
全体のクラスを動作させる場合に
部分クラスがなければまともに動作しない機能が多数ある
例
class Graphics
{
public:
Graphics() {}
void DrawTexture(float pos_x, float pos_y, Texture *texture)
{
// テクスチャを指定位置に描画
}
private:
Device *m_pDevice; // グラフィックデバイス
};
class Library
{
public:
Library()
{
m_pGraphics = new Graphics();
}
~Library()
{
delete(m_pGraphics);
}
void DrawTexture(float pos_x, float pos_y, int texture_id)
{
Texture *texture = m_pFileManager->GetTexture(texture_id);
if (tex != NULL)
{
DrawTexture(pos_x, pos_y, texture);
}
}
private:
FileManager *m_pFileManager;
Graphics *m_pGraphics;
};
上の図とコードからLibraryクラスはGraphicsクラスを持っています。
今回のコードでGraphicsクラスは単体でも動作する機能を有しているので
「部分のみでは機能が活きない」という特性にはあてはまっていませんが、
LibraryクラスはGraphicsクラスがなければ機能しないという
設計意図からコンポジションの関係としています。