基本情報技術者試験など情報処理技術者試験を受験する方にとっては必須の,オブジェクト指向設計についてシンプルにまとめています。オブジェクト(データ(属性),メソッド(手続き)),クラス(抽象化,インスタンス,メッセージ),オブジェクト指向の概念(カプセル化(隠蔽),継承(インヘリタンス,上位クラス(基底クラス,スーパークラス),下位クラス(派生クラス,サブクラス),多重継承,汎化・特化関係(is-a関係),集約・分解関係(part-of関係)),ポリモーフィズム(多相性,多様性,オーバーライド)),オブジェクト指向のその他の概念(オーバーロード,委譲(デリゲート),伝搬(プロパゲーション)),オブジェクト指向プログラミングについて説明します。各内容をしっかり理解するとともに,プログラミングについても例を載せていますので,じっくり取り組んでみてください。
オブジェクト
オブジェクトとは,操作対象である属性(データ)と,そのデータに対するメソッド(手続き)を,1つにまとめたものをいいます。
オブジェクト指向プログラミングでは,操作対象のオブジェクトに対してメッセージを送ることでオブジェクトを操作し,状態を変化させます。
※ オブジェクトは,モジュールに相当する。モジュールについては,「ソフトウェア要件定義・ソフトウェア方式設計・ソフトウェア詳細設計-ソフトウェア詳細設計 -情報処理シンプルまとめ」を参照
※ オブジェクト指向設計は,POAやDOAを改善する設計手法である。POAやDOAの詳細は,「ソフトウェア設計手法 -情報処理シンプルまとめ」を参照
クラス
クラスとは,オブジェクトを定義したものをいいます。幾つかの類似オブジェクトから,共通の性質を抜き出して,属性やメソッドを抽象化して定義します。
インスタンス
インスタンスとは,クラスを使用するという宣言をして生成された実体(具体的なオブジェクト)をいいます。
メッセージ
メッセージは,あるオブジェクトが,別のオブジェクトに操作を依頼するときに使用します。
オブジェクト指向の概念
カプセル化
カプセル化とは,属性とメソッドを1つのオブジェクトとして一体化し,属性とメソッドの詳細をオブジェクトの外部から隠蔽することをいいます。オブジェクト内部の属性やメソッドを変更した場合でも,他のオブジェクトは影響を受けません(受けにくいです)。
継承(インヘリタンス)
継承とは,ある上位クラス(基底クラス,スーパークラス)の属性やメソッドを,下位クラス(派生クラス,サブクラス)に引き継ぐ性質をいいます。サブクラスを定義する場合,継承により基底クラスの属性やメソッドに対する差異(差分)だけを定義すればよいので開発効率が高まります。
※ クラスは,階層構造を持つことができる(あるクラスを基本として,別の新しいクラスを作ることができる)
※ 多重継承…複数の上位クラスから属性やメソッドを継承すること。C++では可能。C#やJavaなどではできない
汎化・特化(is-a)関係と集約・分解(part-of)関係
汎化・特化関係とは,下位クラスの共通する性質をまとめて上位クラスを定義する汎化と,上位クラスの共通する部分に個別の部分を加えて下位クラスを定義する特化の関係をいいます。
集約・分解関係とは,複合オブジェクトと,それを構成するオブジェクトの関係をいいます。
ポリモーフィズム(多相性,多様性)
ポリモーフィズムとは,複数の子クラスに対して同じメッセージを送った場合に,それぞれのクラスが異なる動作をするという特性をいいます。
オーバーライド
オーバーライドとは,上位クラスで定義されたメソッドを,サブクラスで再定義することをいいます。
※ ポリモーフィズムを実現するためには,上位クラスから引き継いだメソッドを,それぞれのサブクラスに合った内容で定義し直し,動作を上書きする
オブジェクト指向のその他の概念
オーバーロード
オーバーロードとは,1つのクラスにおいて,引数の個数やデータ型などが異なる同じ名前の関数を複数定義できることをいいます。
委譲(デリゲート)
委譲とは,あるオブジェクトが一部の処理を他のオブジェクトに依頼することをいいます。
※ イベントハンドラ…特定のイベントが発生した際に実行される処理。委譲の仕組みを利用してメソッドを登録しておくことで実行できる
伝搬(プロパゲーション)
伝播とは,あるオブジェクトを操作した際に,そのオブジェクトに関連する他のオブジェクトに対してもその操作が自動的に適用されることをいいます。
オブジェクト指向プログラミング
例)クラスGameCharacterは,あるゲームのキャラクターを表すクラスであり,その説明を表に示す。メインの処理でキャラクターを1体生成し敵キャラと戦闘を行う。敵キャラとの戦闘時,ユーザーが1を入力した場合は敵キャラを攻撃して自キャラクターは50のダメージを受ける。2を入力した場合はHPを30回復する。この処理を,敵キャラを倒すか自キャラクターのHPが0になるまで繰り返す。なお,ゲームの状態を管理する変数statusは,0の場合は戦闘中,1の場合は自キャラクターが攻撃中,2の場合は自キャラクターがHPを回復中,-1の場合は戦闘終了であることを示す
擬似言語
○main ()
GameCharacter:gChar ← GameCharacter( "Aくん", 100 )
数値型:status ← 0
gChar.nameとgChar.hpの値をコンマ区切りで表示
while( status ≠ -1 )
status を入力 // 1(戦闘)または2(回復)を入力
if( status = 1 )
// 攻撃の処理は省略
status ← gChar.damage( 50 )
elseif( status = 2)
gChar.recovery( 30 )
status ← 0
endif
gChar.nameとgChar.hpの値をコンマ区切りで表示
endwhile
2行目:GameCharacterオブジェクトの宣言と初期化
4,14行目:GameCharacterクラスのメンバ変数の利用
9,11行目:GameCharacterクラスのメンバ関数の呼び出し
Python
# GameCharacterクラス
class GameCharacter:
# メンバ変数
__name: str; __hp: int
# プロパティ
def get_Name(self) -> str:
return self.__name
def set_Name(self, name: str):
self.__name = name
pName = property(get_Name, set_Name)
def get_Hp(self) -> int:
return self.__hp
def set_Hp(self, hp: int):
self.__hp = hp
pHp = property(get_Hp, set_Hp)
# コンストラクター
def __init__(self, name: str, hp: int):
self.__name = name
self.__hp = hp
# メンバ関数
def damage(self, n: int) -> int:
self.__hp = self.__hp - n
if self.__hp > 0:
return 0
else:
self.__hp = 0
return -1
def recovery(self, n: int):
self.__hp = self.__hp + n
if self.__hp > 100:
self.__hp = 100
# 主プログラム
def main():
gChar: GameCharacter = GameCharacter('Aくん', 100)
status: int = 0
print(gChar.pName, ", ", gChar.pHp)
while status != -1:
status = int(input("1(戦闘)または,2(回復)を入力:"))
if status == 1:
# 攻撃の処理は省略
status = gChar.damage(50)
elif status == 2:
gChar.recovery(30)
status = 0
print(gChar.pName, ", ", gChar.pHp)
if __name__ == "__main__":
main()
40行目:GameCharacterオブジェクトの宣言と初期化
42,51行目:GameCharacterクラスのプロパティの利用(メンバ変数は隠蔽しているので使用できない)
47,49行目:GameCharacterクラスのメンバ関数の呼び出し
※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照
C#
// GameCharacterクラス
class GameCharacter {
// メンバ変数
private string name;
private int hp;
// プロパティ
public string Name {
get { return name; }
set { name = value; }
}
public int Hp {
get { return hp; }
set { hp = value; }
}
// コンストラクター
public GameCharacter(string name, int hp) {
this.name = name;
this.hp = hp;
}
// メンバ関数
public int damage(int n) {
hp = hp - n;
if(hp > 0) {
return 0;
} else {
hp = 0;
return -1;
}
}
public void recovery(int n) {
hp = hp + n;
if(hp > 100) {
hp = 100;
}
}
}
class b_012 {
// 主プログラム
static void Main(string[] args) {
GameCharacter gChar = new GameCharacter("Aくん", 100);
int status = 0;
Console.WriteLine("{0}, {1}", gChar.Name, gChar.Hp);
while(status != -1) {
Console.Write("1(戦闘)または,2(回復)を入力:");
status = Convert.ToInt32(Console.ReadLine());
if(status == 1) {
// 攻撃の処理は省略
status = gChar.damage(50);
} else if(status == 2) {
gChar.recovery(30);
status = 0;
}
Console.WriteLine("{0}, {1}", gChar.Name, gChar.Hp);
}
}
}
46行目:GameCharacterオブジェクトの宣言と初期化
48,59行目:GameCharacterクラスのプロパティの利用(メンバ変数は隠蔽しているので使用できない)
54,56行目:GameCharacterクラスのメンバ関数の呼び出し
※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照
実行結果
Aくん, 100
1(戦闘)または,2(回復)を入力:1
Aくん, 50
1(戦闘)または,2(回復)を入力:2
Aくん, 80
1(戦闘)または,2(回復)を入力:1
Aくん, 30
1(戦闘)または,2(回復)を入力:1
Aくん, 0
例)クラスMyListは,単方向リストを管理するクラスである。クラスMyNodeは,クラスMyListの内部クラスであり,単方向リストの要素である。MyNode型のメンバ変数listHeadはクラスMyNodeのインスタンスの参照(先頭の要素の参照)を格納するものとする。メソッドAdd()は,単方向リストに引数valueで指定された文字列を格納する要素を追加するメソッドである。メソッドIns()は,単方向リストの引数value2で指定された文字列を格納する要素の次の位置に引数value1で指定された文字列を格納する要素を追加するメソッドである。メソッドDel()は,単方向リストから引数valueで指定された文字列を格納する要素を削除するメソッドである。主プログラムでは,単方向リストを生成したあと,別の要素を挿入・削除し,その内容を表示する
C#
// MyListクラス
public class MyList {
// MyNode内部クラス
public class MyNode {
// メンバ変数
private string value;
private MyNode? next;
// プロパティ
public string Value {
get { return this.value; }
set { this.value = value; }
}
public MyNode Next {
get { return this.next!; }
set { this.next = value; }
}
// コンストラクター
public MyNode(string value) {
this.value = value;
this.next = null;
}
}
// リストの先頭要素の参照
public MyNode? listHead;
// コンストラクター
public MyList() {
listHead = null;
}
// メンバ関数
// 全ての要素を表示する
public void Write() {
if(listHead == null) {
return;
}
MyNode curr = listHead;
while(true) {
Console.WriteLine(curr.Value);
if(curr.Next == null) {
break;
} else {
curr = curr.Next;
}
}
}
// 要素を追加する
public void Add(string value) {
MyNode prev;
MyNode curr = new MyNode(value);
if(listHead == null) {
listHead = curr;
} else {
prev = listHead;
while(prev.Next != null) {
prev = prev.Next;
}
prev.Next = curr;
}
}
// 要素を挿入するメソッド
public void Ins(string value1, string value2) {
if(listHead == null) {
return;
}
MyNode curr = listHead;
while(true) {
if(curr.Value == value2) {
MyNode ins = new MyNode(value1);
ins.Next = curr.Next;
curr.Next = ins;
break;
}
curr = curr.Next;
if(curr == null) {
break;
}
}
}
// 要素を削除するメソッド
public void Del(string value) {
if(listHead == null) {
return;
}
if(listHead.Value == value) {
listHead = listHead.Next;
} else {
MyNode curr = listHead;
while(curr.Next != null) {
if(curr.Next.Value == value) {
curr.Next = curr.Next.Next;
break;
} else {
curr = curr.Next;
}
}
}
}
}
class b_013 {
// 主プログラム
static void Main(string[] args) {
MyList myList = new MyList();
myList.Add("広島");
myList.Add("横川");
myList.Add("山口");
myList.Add("西広島");
myList.Ins("新白島", "広島");
myList.Del("山口");
myList.Write();
}
}
111行目:MyListオブジェクトの宣言と初期化
112~118行目:MyListクラスメンバ関数の呼び出し
※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照
実行結果
広島
新白島
横川
西広島
まとめ
今回は,オブジェクト指向設計について,シンプルにまとめてみました。基本情報技術者試験や応用情報技術者試験で問われるところですので,しっかり頑張りましょう。