作成日 2007/8/12 最終更新日 2014/6/8
インスタンススコープの属性とクラススコープの属性
UML(※1)のクラス図では、クラスの属性はいろいろな点から分類することが出来るのですが、データの持ち方と言う点から分類する(※2)と、2つに分けることが出来ます。
1つがインスタンススコープの属性で、もう一つがクラススコープの属性です。
このページではインスタンススコープの属性とクラススコープの属性の特徴、違いを説明し、どのような箇所で使用されているか(※3)を説明します。
※1:UMLのバージョンは2.0とします。
※2:他にも、可視性(public,private,protected,package)、読み取り専用かどうか、派生属性(他の属性や関連から導き出せるものか?)かどうか、などから分類可能です。
UML2.0の仕様ではクラスの属性はClassクラス(※メタクラスです)からPropertyクラス(※メタクラスです)への関連として定義してあるので(UML2.0の仕様書 p29
Figure 7.12)、結局
、クラスの属性を分類するというのは、PropertyクラスやPropertyクラスの親クラス(StructualFeatureクラス(※抽象メタクラスです)やFeatureクラス(※抽象メタクラスです))の属性や関連を調べるのと同じ気がします。
※3:クラススコープの属性のみとします。というのは、インスタンススコープの属性は使用頻度が高いので、改めて説明する必要は無いと思ったためです。
とりあえず、図1のクラス図を見てください。
図1 クラス図
クラス図で下線が引かれていない属性が、インスタンススコープの属性となります。
インスタンススコープの属性はインスタンスごとに違った値(同じ値となることもある)を持ちます。…と、このような説明だと難しくてわからないので、「クラスとオブジェクト 3.具体例1(電話帳の例) 」の図1と図2を見てください。
図1を見ると、「名前」属性や「電話番号」属性は1行1行、違った値を持っています(1行ごとに別の値を持つことが出来る)。このような属性はインスタンススコープの属性です(※1)。
※1:インスタンススコープの属性と、クラススコープの属性を対比して書かないとわかりずらいか…。
とりえあえず、クラススコープの属性についての説明も読んで欲しいです。
これもまた、とりあえず、図2のクラス図(図1と同じ図です)を見てください。
図2 クラス図(※1)
クラス図で下線が引かれている属性が、クラススコープの属性となります(※2)。
とりあえず、図3を見てください。
図3 電話番号のデータとデータ数(※1)
クラススコープの属性はクラスのインスタンスごとに違った値を持つのではなく、クラスで1つの値を持ちます。
図2の例ではデータ数(電話帳クラスのインスタンスの数)は個々のインスタンスが持つデータではありません。
※1:ここでは、インスタンスのデータ数をクラススコープの属性として定義しました。しかし、これは例としてあげたのであって、実際の設計において、インスタンスのデータ数を常にクラススコープの属性として持たせるとは限りません。クラススコープの属性としてしまうと、確かにクラスのインスタンスの数を管理できますが、実際には1年1組の児童の電話帳データ数、1年2組の児童の電話帳データ数のように、何かでグループ分けした中でのデータ数は管理することが出来ません。
エクセルVBAでもインスタンスの数をクラススコープの属性として持っている例は恐らく無く、多くの場合はそのクラスのコレクションクラスが持っています(Countプロパティでインスタンスの数を取得する)。例えば、ExcelライブラリのWorksheetクラスのオブジェクトはWorksheetsクラス(※コレクションクラスです)が持ちます。(ちなみにWorkSheetsオブジェクトはWorkbookクラスが管理しています。結局、Worksheetクラスのオブジェクト(エクセルのワークシート)はWorkbookクラスのオブジェクト(エクセルファイルと考えてよい)ごとで管理する必要があるので、Worksheetクラスのオブジェクトの数も(直接的にではないにせよ)Workbookクラスのオブジェクトが管理する必要があるため、このようなデータ数をクラススコープの属性で保持するのではなく、コレクションクラスのインスタンススコープの属性で保持するようにしていると思います。)
※2:クラススコープの属性や操作に対して、下線を引くというのは、UML2.0の仕様書では、7.3.19
Feature(from Kernel)のNotationのところ(p67)に書いてある気がします。英語なのでよくわかりませんが…。
Featureクラス(※抽象メタクラスです)はOperationクラス(※メタクラスです)やPropertyクラス(※メタクラスです)の親クラスの親クラスです。
クラススコープの属性が使用される例としては、まずは、GoFのデザインパターン の一つであるシングルトンパターン でしょうか。
シングルトンパターンとは、クラスのインスタンスが1つとなることを保障したいときに使用するデザインパターンです。
それ以外のデザインパターンで、クラススコープの属性を使用するものは、だるまは知らないです。ただ、デザインパターンの中には、他のデザインパターンと組み合わせて使用されるものがあるので、シングルトンパターンと組み合わせて使用されるデザインパターンも見ておくと良いかもしれません(※1)。
他に、良くある例としては、システムで共通に使用する定数をファイルに書き込んでおき、システムの起動直後にクラススコープの属性値にセットする(※2)ことがあります。
他には…、思い浮かばないです。
※1:例えば、
フライウェイトパターン 。他は…、知らない。
※2:ファイルから読み込むのではなく、RDB(リレーショナルデータベース)から読み込んだり、どこかのリソースから読まずにプログラムに直接、値を書いてしまう方法もあります。
クラス図だけじゃ、理解できん!というお叱りを受けたので、ソース(実装例)も出します。言語はJava 、C++ 、VBA です。
■Javaの場合(動作確認環境:JRE1.6)
電話帳クラス(ファイル名:TelephoneBook.java)
/** 電話帳クラス。1件分のデータを保持します。 */
public class TelephoneBook {
/** 名前 */
private String name ;
/** 電話番号 */
private String number ;
/** データ数 */
private static int dataCount = 0;
/***
* コンストラクタ
*/
public TelephoneBook(){
dataCount ++;
}
/**
* 名前を返します。
* @return 名前
*/
public String getName() {
return name ;
}
/**
* 名前を設定します。
* @param name 設定する名前
*/
public void setName(String name) {
this .name = name;
}
/**
* 電話番号を返します。
* @return 電話番号
*/
public String getNumber() {
return number ;
}
/**
* 電話番号を設定します。
* @param number 設定する電話番号
*/
public void setNumber(String number) {
this .number = number;
}
/**
* データ数を返します。
* @return データ数
*/
public static int getDataCount() {
return dataCount ;
}
}
※このソースだと、データ数が増えるだけで、減ることがありません。本来は不具合なのですが、これはサンプルなので許してください。
電話帳オブジェクトを生成して表示してみるサンプル(ファイル名:Main.java)
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// オブジェクト作成
ArrayList<TelephoneBook> teleBook = new ArrayList<TelephoneBook>();
TelephoneBook obj = null ;
obj = new TelephoneBook();
obj.setName("山田 太郎" );
obj.setNumber("01-2345-6789" );
teleBook.add(obj);
System.out .println("データ数:" + TelephoneBook.getDataCount());
obj = new TelephoneBook();
obj.setName("上野 花子" );
obj.setNumber("03-0303-9999" );
teleBook.add(obj);
System.out .println("データ数:" + TelephoneBook.getDataCount());
obj = new TelephoneBook();
obj.setName("だるま 太郎" );
obj.setNumber("04-000-1234" );
teleBook.add(obj);
System.out .println("データ数:" + TelephoneBook.getDataCount());
}
}
実行結果(標準出力)
■C++の場合(動作確認環境:OS:Windows 7 , コンパイラ:VC++ 2010のcl.exe )
電話帳クラスの定義(ファイル名:TelephoneBook.h)
/**
* @file
*/
#ifndef TELEPHONEBOOK_H_
#define TELEPHONEBOOK_H_
#include <string>
/// @brief 電話帳クラス。1件分のデータを保持します。
class TelephoneBook {
private :
std::string name ; ///< @brief 名前
std::string number ; ///< @brief 電話番号
static int dataCount ;///< @brief データ数
public :
/// @brief コンストラクタ
TelephoneBook ();
/// @brief コピーコンストラクタ
TelephoneBook (const TelephoneBook & other);
/// @brief デストラクタ
virtual ~TelephoneBook ();
/// @brief 名前を返します。 @return 名前
std::string getName () const ;
/// @brief 名前を設定します。 @param [in] name 設定する名前
void setName (const std::string & name);
/// @brief 電話番号を返します。 @return 電話番号
std::string getNumber () const ;
/// @brief 名前を電話番号します。 @param [in] name 設定する電話番号
void setNumber (const std::string & number);
/// @brief データ数を返します。 @return データ数
static int getDataCount ();
};
#endif /* TELEPHONEBOOK_H_ */
電話帳クラスの関数の定義(ファイル名:TelephoneBook.cpp)
#include "TelephoneBook.h"
using namespace std;
int TelephoneBook::dataCount = 0;
TelephoneBook::TelephoneBook () {
dataCount ++;
}
TelephoneBook::TelephoneBook (const TelephoneBook & other) {
dataCount ++;
*this = other;
}
TelephoneBook::~TelephoneBook () {
dataCount --;
}
string TelephoneBook::getName () const {
return name ;
}
void TelephoneBook::setName (const string & name) {
this ->name = name;
}
string TelephoneBook::getNumber () const {
return number ;
}
void TelephoneBook::setNumber (const string & number) {
this ->number = number;
}
int TelephoneBook::getDataCount () {
return dataCount ;
}
※上記の場合、関数定義をヘッダーファイルで行い、インライン関数にした方が効率が良さそうですが、今回はそういうことはしていません。
電話帳オブジェクトを生成して表示してみるサンプル(ファイル名:Main.cpp)
#include <iostream>
#include <string>
#include <vector>
#include "TelephoneBook.h"
using namespace std;
int main () {
vector <TelephoneBook > teleBook;
TelephoneBook obj;
cout << "データ数:" << TelephoneBook ::getDataCount () << endl;
obj.setName("山田 太郎" );
obj.setNumber("01-2345-6789" );
teleBook.push_back(obj);
cout << "データ数:" << TelephoneBook ::getDataCount () << endl;
obj.setName("上野 花子" );
obj.setNumber("03-0303-9999" );
teleBook.push_back(obj);
cout << "データ数:" << TelephoneBook ::getDataCount () << endl;
obj.setName("だるま 太郎" );
obj.setNumber("04-000-1234" );
teleBook.push_back(obj);
cout << "データ数:" << TelephoneBook ::getDataCount () << endl;
return 0;
}
※注意:C++のstd::vectorのpush_back関数は、オブジェクトへの参照を配列に追加するのではなく、実体をコピーします。
なので、push_backするたびに、データ数が増えます。
実行結果(標準出力)
データ数:1
データ数:2
データ数:3
データ数:4
■VBAの場合(動作確認環境:VBA 6.0 )
電話帳クラス(モジュール名:TelephoneBook(クラスモジュール))
''
' 電話帳クラス。クラススコープの属性と操作を定義します。
Option Explicit
'' データ数
Private m_dataCount As Integer
''
' データ数を返します。
' @return データ数
Public Property Get dataCount() As Integer
dataCount = m_dataCount
End Property
''
' データ数を1増やします
Public Sub addDataCount()
m_dataCount = m_dataCount + 1
End Sub
''
' データ数を1減らします
Public Sub removeDataCount()
m_dataCount = m_dataCount - 1
End Sub
※VBAではクラスモジュール内に定義した属性や関数はすべてインスタンススコープの属性・関数になります。
クラススコープの属性や関数は定義できません。なので、標準モジュールに定義することになります。
電話帳クラス(モジュール名:TelephoneBook(クラスモジュール))
''
' 電話帳クラス。1件分のデータを保持します。
Option Explicit
'' 名前
Private m_name As String
'' 電話番号
Private m_number As String
''
' オブジェクト生成時に呼び出されます。
Private Sub Class_Initialize()
Call stTelephoneBook.addDataCount
End Sub
''
' オブジェクト解放時に呼び出されます。
Private Sub Class_Terminate()
Call stTelephoneBook.removeDataCount
End Sub
''
' 名前を返します。
' @return 名前
Public Property Get name() As String
name = m_name
End Property
''
' 名前を設定します。
' @param newName 設定する名前
Public Property Let name(ByVal newName As String )
m_name = newName
End Property
''
' 電話番号を返します。
' @return 電話番号
Public Property Get number() As String
number = m_number
End Property
''
' 電話番号を設定します。
' @param newNumber 設定する電話番号
Public Property Let number(ByVal newNumber As String )
m_number = newNumber
End Property
電話帳オブジェクトを生成して表示してみるサンプル(ファイル名:Main(標準モジュール))
Option Explicit
Sub main()
Dim teleBook As New Collection
Dim i As Integer
Dim obj As TelephoneBook
Set obj = New TelephoneBook
obj.name = "山田 太郎"
obj.number = "01-2345-6789"
Call teleBook.Add(obj)
Debug.Print "データ数:" & stTelephoneBook.dataCount
Set obj = New TelephoneBook
obj.name = "上野 花子"
obj.number = "03-0303-9999"
Call teleBook.Add(obj)
Debug.Print "データ数:" & stTelephoneBook.dataCount
Set obj = New TelephoneBook
obj.name = "だるま 太郎"
obj.number = "04-000-1234"
Call teleBook.Add(obj)
Debug.Print "データ数:" & stTelephoneBook.dataCount
End Sub
実行結果(イミディエイトウィンドウ)
このページを作成するのに参考にしたページです。
ただし、だるまは、このページを作成するにあたり、これらのページを100%理解してから作成したわけではない(おいおい。)です。
間違い(リンク先のページではなく、このページに)があったら、ごめんなさい。
このページの利用によって発生した、いかなる損害について、このホームページの作成者は責任を負いません。
このページの間違いや嘘を見つけた方、このページに書いて欲しい情報がある方は
メール をお願いします。
Microsoft 、Windows 、Visual Basic および Excel は米国Microsoft
Corporationの米国およびその他の国における登録商標または商標です。
ここではExcel® をエクセル、Visual Basic® for Applications をVBAと表記する場合があります。
Mac 、Mac OS 、Mac OS X は米国Apple
Computer,Inc.の登録商標または商標です。
OMG、UML、Unified Modeling
Languageは、Object Management Groupの商標または登録商標です。
その他、社名および商品名、システム名称などは、一般に各社の商標または登録商標です。
このホームページの作成者はこれらの会社とはいっさい関係がありません。