それなりに需要があるかもしれないので、まとめてみました。
前提
- Casey Reas, Ben Fry著、船田巧訳、『Processingをはじめよう』、オライリージャパン、2011年の「9章オブジェクト」以前の内容は理解している。
- Fate/stay nightを知っていて、ネタバレがあっても構わない
1.オブジェクト指向プログラミング
オブジェクト指向のプログラムは、「コンピュータの中にオブジェクトというものがあって、これがお互いにメッセージを送り合うことで動いている」といった感じで作ります。
なお、メッセージを送ることを、メッセージ・パッシング(message passing: メッセージ送信)といいます。
オブジェクトというのは、Fate/stay night(以下Fate)を例にすると、サーヴァント(使い魔)のようなものです。オブジェクトにメッセージを送ることは、サーヴァントに指示を出して使役することに対応します。
Fateと違うのは、いくらでもサーヴァントと契約できることです。いろいろなサーヴァントを召喚して、使役して、プログラムを組み立てます。
2.クラス
Processingは、実際にはJavaだったりします。Javaはクラス・ベースのオブジェクト指向言語で、オブジェクトを使うために、クラスを用意する必要があります。
クラスは、オブジェクトの設計図のようなものです。
このクラスは、Fateのクラスのようなものだと思ってください。実際にはあんまりちゃんと対応しないけど、取りあえず。
クラスには、そのクラスのサーヴァントが持つべきパラメータと、そのクラスのサーヴァントを使役するのに使う指示を用意します。
このパラメータのことをフィールド(field データを記入する欄)、指示をメソッド(method 処理方法)と言います。
たとえば、セイバー(剣の騎士)のクラスのサーヴァントには、真名と魔力というフィールドを持たせ、攻撃しろというメソッドを用意するとします。他にもいろいろあると思いますが、簡単のためにこの3つにしておきます。
これをクラス図というもので書くと以下のようになります。
セイバー |
真名 魔力 |
攻撃しろ() |
プログラム的には、フィールドは変数の宣言、メソッドは関数の定義みたいに書きます。
というわけで、具体的には、以下のようになります。
/* セイバーのクラスの定義 */ class Saber { /* フィールドの宣言 */ String trueName; // 真名 int magicalEnergy; // 魔力 /* メソッドの定義 */ void attack() { println("剣で攻撃しました。"); } }
3.オブジェクトの生成と使用
ここまでは、まだ、クラス(設計図)を用意しただけで、実際にオブジェクトを使うところまで行っていません。
これは、セイバーというクラスはあるけれど、具体的にサーヴァントが召喚されていないし、使役もされていないということです。
それでは、召喚して使役する方法を説明します。まず、とりあえず、仮の名前を用意します。
普通の変数の宣言と同じように、型(ここではクラス名)と変数名(仮の名前)を以下のように書きます。
Saber ahoge;
次に、プログラムの中でサーヴァントを召喚し、契約します。
ahoge = new Saber();
ここで、new クラス名()で、召喚を行ないます。
仮の名前に=で代入することで、名前で縛ることで、契約がなされます。
これは、プログラム的には、クラスの定義から、オブジェクトを生成したことになります。
このオブジェクトは、設計図であるクラスではなく、具体的な実体があるので、インスタンス(instance 具体的な実体)とも呼ばれます。
Fateの現界している個々のサーヴァントがインスタンスということです。ちなみに、new(召喚)しても、=(契約)しないと、現界できなくなって、消滅し(ガーベッジ・コレクションされ)てしまいます。
召喚して契約したら、使役できます。具体的には、以下のように、「仮の名前.メソッド名()」で、サーヴァントに指示を出して使役します。
ahoge.attack();
4.オブジェクトの初期化
変数を初期化するときには、「int i = 0;」のように、宣言と同時に代入していました。
しかし、一般にオブジェクトは、複数のパラメータを持っているので、同じように初期化することはできません。
では、たとえば、Saberの場合、召喚時に真名と魔力を初期化したいとします。
次のように書くことで、真名を「アルトリア・ペンドラゴン」、魔力を「40」と、初期化するという文法にするのはどうでしょうか?
ahoge = new Saber("アルトリア・ペンドラゴン", 40);
で、実際、Javaの場合、そういう文法になっています。
これはよく見ると、Saberという関数があって、その引数が真名と魔力になっているようなものです。
また、この関数は、Saberクラスのサーヴァント(オブジェクト)を返すので、戻り値の型はSaberです。
というわけで、この関数の宣言を以下のように書いてはどうでしょう。
/* セイバーのクラスの定義 */
class Saber {
/* フィールドの宣言 */
String trueName; // 真名
int magicalEnergy; // 魔力
/* コンストラクタの定義 */
Saber(String name, int energy) {
trueName = name;
println("真名を" + name + "にしました。");
magicalEnergy = energy;
println("魔力を" + energy + "にしました。");
}
/* メソッドの定義 */
void attack() {
println("剣で攻撃しました。");
}
}
戻り値の型はSaberなので、わざわざ書くと、2回Saberと書くことになるので、書かないという文法にしておきます。
このように、オブジェクトを初期化するのに使う関数のようなものを、オブジェクト指向言語ではコンストラクタ(constructor 生成するのに使用されるもの)と呼びます。
文法的には、クラス名と同じ名前の関数の定義です。ただし、戻り値の型は書きません。
5.まとめ
- オブジェクト指向のプログラムでは、サーヴァント(オブジェクト)を召喚して、使役することで、処理を実行する
- クラスは、Fateのクラスみたいなもの
- クラスの定義には、そのクラスのサーヴァントが持っているパラメータを変数の宣言みたいに、使役するのに使う指示を関数の定義みたいに書く
- サーヴァントは、召喚して(new クラス名())、契約して(仮の名前に=で代入)、使役できるようになる
- 使役するには、「仮の名前.メソッド名()」
- サーヴァントを召喚と同時に初期化するのに、コンストラクタというものを使う
- コンストラクタの定義は、クラス名と同じ名前の関数。ただし、戻り値の型を書かない
では、最後にProcessingのプログラムの全体を書いておきます。
Saber ahoge; // 仮の名前を用意 void setup() { /* セイバーのサーヴァントを召喚して、契約します */ ahoge = new Saber("アルトリア・ペンドラゴン", 40); /* セイバーを使役します(攻撃するように命じます) */ ahoge.attack(); } /* セイバーのクラスの定義 */ class Saber { /* フィールドの宣言 */ String trueName; // 真名 int magicalEnergy; // 魔力 /* コンストラクタの定義 */ Saber(String name, int energy) { trueName = name; println("真名を" + name + "にしました。"); magicalEnergy = energy; println("魔力を" + energy + "にしました。"); } /* メソッドの定義 */ void attack() { println("剣で攻撃しました。"); } }
Todo?
カプセル化とアクセス制御、継承、多態性、抽象クラスとインターフェイス。
同じクラスのサーヴァントでも、インスタンスによって、いろいろな宝具とか、攻撃が使えるようにする設計。
0 件のコメント:
コメントを投稿