オブジェクト指向プログラミング(OOP)はプログラムについて考える、また別の方法です。オブジェクトはまた、変数をそれと関係する関数とグループ化する方法でもあります。変数や関数の扱いに慣れてくると、オブジェクトは単に、これまでに学んだことをより理解しやすいパッケージにまとめたものだと分かってきます。
オブジェクトは重要です。なぜならアイデアをより小さな構成要素に分けたものであるからです。これは自然界にある事物に似ています。たとえば、臓器は組織でできており、組織は細胞でできています。これと同様に、コードがどんどん複雑になってくると、複雑化したものを形成するより小さな構造の視点から考える必要が出てきます。すべてのことを1度にやってしまおうとする大きなコードのかたまりを記述するよりも、互いが連携する、もっと小さくて理解しやすいコードのかたまりを記述して管理する方が、よほど容易です。
目次
プロパティとメソッド
ソフトウェアオブジェクトは関係する変数と関数を集めたものです。オブジェクトの文脈では、変数はプロパティ(またはインスタンス変数)、関数はメソッドと呼ばれます。プロパティとメソッドの働きは、これまで見てきた変数と関数と変わりませんが、プロパティとメソッドがオブジェクトの一部だということを重視するために新しい用語を使うことにします。別の言い方をすると、オブジェクトは関係するデータ(プロパティ)と関係するアクションや振る舞い(メソッド)とを一体化します。
たとえば、ラジオのモデル化なら、調整できるパラメータには何があり、そのパラメータに影響するアクションにはどんなものがあるかは想像しやすいでしょう。
プロパティ:volume, frequency, band(FM, AM), power(ON, OFF)
メソッド:setVolume, setFrequency, setBand
単純な機器のモデル化は、蟻や人間などの有機体のモデル化よりも容易です。複雑な有機体を少数のプロパティとメソッドに凝縮することは不可能ですが、しかし興味を引くシミュレーションモデルなら十分に可能です。「シムピープル」はその分かりやすい例です。このコンピュータゲームは、シミュレーションする人々の日常を管理することでプレーします。登場人物はプレーヤーを飽きさせず熱中させるだけの個性を持っていますが、それ以上のものは持っていません。実際、彼らが持っている性格の属性は、きれい好き、社交的、活発、遊び好き、快活のたった5つです。複雑な有機体でも高度に単純化したモデルが作成できるということを知っていれば、少数のプロパティとメソッドで蟻のプログラミングをスタートできるです。
プロパティ:type(worker, soldier), weight, length
メソッド:walk, pinch, releasePheromones(フェロモンを出す), eat
蟻のプロパティとメソッドのリストができたら、モデル化のための蟻のさまざまな側面の集中に移れるかもしれません。しかし、モデルをプログラムの目的に適うものにしようとするのであれば、これが正しいというモデルの作成方法はありません。
クラスの定義
オブジェクトを作成するには、クラスの定義から始めます。クラスはオブジェクトの仕様書です。建築にたとえるなら、クラスは家の設計図で、オブジェクトは家そのものです。家は設計図から作られますが、バリエーションを持つことは可能です。設計図はあくまでも仕様であり、家の構造そのものではありません。たとえばある家は青く、別の家は赤くできます。またある家は暖炉つきで、別の家にはないかもしれません。オブジェクトも同様で、1つのクラス(設計図)から作られるオブジェクト(家)は、異なる値に設定できる変数(色、暖炉)を持ちます。専門用語を使うなら、それぞれのオブジェクトはインスタンスであり、それぞれのインスタンスは自分自身のプロパティとメソッドを持ちます。
クラスを記述するにはその前に、少し計画を立てるようにします。オブジェクトにはどんなプロパティとメソッドを持たせるべきでしょう? ブレインストーミングをしてあらゆる選択肢を想像し、優先順位をつけて、どれがうまくいくか推測してみましょう。プログラミング中には変更することになるでしょうが、良いスタートを切るのは重要です。
プロパティには明確な名前をつけます。オブジェクトのプロパティはどんな種類のデータでも保持できます。オブジェクトは同時に、多くのブール値や数値、イメージ、文字列などが保持できます。オブジェクトを作成する1つの理由は、関係するデータ要素をグループ化するためです。これは覚えておいてください。メソッドにも明確な名前をつけ、戻り値を決めます(ある場合)。メソッドはプロパティの値を変更し、プロパティの値に基づいてアクションを実行するために使用します。
初めてのクラスとして、「7_4:ランダム p5.js JavaScript」の「シェイプのランダムな移動」を利用します。
let x;
let y;
const diameter = 20;
const speed = 10;
function setup() {
createCanvas(240, 120);
background(204);
x = width / 2;
y = height / 2;
}
function draw() {
x += random(-speed, speed);
y += random(-speed, speed);
ellipse(x, y, diameter, diameter);
}
まずはプロパティは次のものにします。
x, y, diameter, speed
次はどんなメソッドがこのオブジェクトに役立つかを考えます。draw()関数を見ると、シェイプの位置の更新と画面への描画という2つの構成要素があることが分かります。そこでオブジェクトには、それぞれが専用の作業を行うメソッドを2つ作成することにします。
move() // => 移動専用
display() // => 表示専用
これらはどれも値を返しません。オブジェクトが持つべきプロパティとメソッドが決まったので、クラスを記述しいきましょう。
クラスはキーワードclassで始め、クラス名を続けます。その後を{と}で囲みます。どんなオブジェクトにするか、つまり以降に記述するすべてはこの{と}の間に記述します。クラス名は慣習的に大文字で始めます。
class JitterBug {
// 中身をここに記述
}
コンストラクタの定義
クラスは、そのクラスからオブジェクトを作成するときにオブジェクトの状態を初期化するコンストラクタと呼ばれるメソッドを持ちます。これはconstructorという名前で作成します。
class JitterBug {
constructor() {
// コンストラクタの中身をここに記述
}
}
プロパティの定義
プロパティは、コンストラクタの{と}の間に定義します。JavaScriptには特別なキーワードthisがあり、コンストラクタ内で現在のオブジェクトの参照に使用できます。プロパティを作成するときには、letやconstではなく、thisの後にドット、その後にプロパティ名をつなげる方法で作成します。speedプロパティの場合には、次のように記述できます。
class JitterBug {
constructor() {
this.speed = 2.5;
}
}
コンストラクタ内ではプロパティを宣言し(this.speed)、適切な値を割り当てます(this.speed = 2.5)。値はspeedプロパティのように直接代入することもできますし、コンストラクタを通して代入することもできます。コンストラクタを通す場合には、クラスから作成されるオブジェクトによって値を変えることができます(たとえば色やサイズなど)。JitterBugクラスの場合には、xとy、diameterプロパティをオブジェクトごとに変えたいので、コンストラクタを通すことにします。
class JitterBug {
constructor(tempX, tempY, tempDiameter) {
this.x = tempX;
this.y = tempY;
this.diameter = tempDiameter;
this.speed = 2.5;
}
}
constructor()メソッドに指定しているtempXとtempY、tempDiameterは、これまでの関数で使ってきたパラメータと同じ変数です。JitterBugクラスからオブジェクトを作成するときにこの3つを渡すと、コンストラクタによって、そのオブジェクトのxプロパティが宣言されtempXが代入され、yプロパティが宣言されtempYが代入され、diameter プロパティが宣言されtempDiameterが代入されます。speedプロパティの値はどのオブジェクトでも同じ2.5です。
メソッドの定義
メソッドはconstructor(){…}の後に記述します。書き方はメソッド名のあとに(){…}をつづけるだけですが、プロパティを参照するときには前にthis.をつける必要があります。move()メソッドの場合には、次のようになります。
move() {
this.x += random(-this.speed, this.speed);
this.y += random(-this.speed, this.speed);
}
JitterBugクラス
JitterBugクラス全体は次のようになります。
// JitterBugクラス
class JitterBug {
// コンストラクタメソッド
constructor(tempX, tempY, tempDiameter) {
// 自分のx位置
this.x = tempX;
// 自分のy位置
this.y = tempY;
// 自分の直径
this.diameter = tempDiameter;
// 全インスタンス共通のスピード
this.speed = 2.5;
}
// ランダムに移動するmove()メソッド
move() {
this.x += random(-this.speed, this.speed);
this.y += random(-this.speed, this.speed);
}
// 各プロパティを使って円を描画するdisplay()メソッド
display() {
ellipse(this.x, this.y, this.diameter, this.diameter);
}
}
オブジェクトの作成
クラスが定義できたら、プログラムでそのクラスからオブジェクトのインスタンスが作成できます。
- オブジェクトの変数を宣言する
- newキーワードを使って、オブジェクトを作成(インスタンス化)する
sketch.jsで動作するJitterBugオブジェクトを作成し、そのmove()とdisplay()メソッドを実行するには、次のようにします。
let bug;
function setup() {
createCanvas(480, 120);
background(204);
bug = new JitterBug(width / 2, height / 2, 20);
}
function draw() {
bug.move();
bug.display();
}
// JitterBugクラスの定義コードを以下に記述
クラスから作成するオブジェクト用の変数もこれまでと同じ方法で宣言できます。次いで、キーワードnewを使ってオブジェクトをインスタンス化します。newの後には半角開けてクラス名と()をつづけ、()の中にはパラメータの具体的な値を指定します。値の順番は、クラスのコンストラクタで定義した順番である必要があります。このnew JitterBug(…)によって、すべてのプロパティ全メソッドを持つオブジェクトがメモリ内に作成され、それが返されて変数bugに割り当てられます。
draw()内で呼び出しているbug.move()はsetup()で作成したJitterBugクラスのインスタンスです。JitterBugクラスのインスタンスはどれも、JitterBugクラスで定義したメソッドを持っているので、そのインスタンス(bug)にドットとメソッド名と()をつなげることで、そのメソッドを呼び出すことができます。
複数のオブジェクトの作成
前のサンプルでは、draw()内でドット(.)を使ってオブジェクトのメソッドにアクセスするという新しい書き方をしました。ドット演算子はオブジェクトの名前(変数名)とそのプロパティやメソッドをつなぐために使用されます。
次の例では、同じクラスから2つのオブジェクトを作成し、変数jitとbugに割り当てています。jitとbugは異なる値を与えて作成しているので、その属性が異なります。またjit.move()はjitオブジェクトが持つmove()メソッドであり、bug.move()はbugオブジェクトが持つmove()メソッドです。
let jit;
let bug;
function setup() {
createCanvas(480, 120);
background(204);
// 同じクラスに対して、異なる値を与えると、インスタンスの属性が変わる
jit = new JitterBug(width * 0.33, height / 2, 50);
bug = new JitterBug(width * 0.66, height / 2, 10);
}
function draw() {
jit.move();
jit.display();
bug.move();
bug.display();
}
クラスを定義したコードはそれ自体がモジュールなので、クラスへの変更は、そこから作成されるすべてのオブジェクトに反映されます。たとえばJitterBugクラスにカラーを制御するプロパティやサイズを決めるプロパティが追加できます。これらの値はコンストラクタメソッドを通して渡すこともできますし、setColor()やsetSize()などのメソッドを追加して設定することもできます。クラスは自己完結型なので、JitterBugクラスを別のスケッチで使用することもできます。
クラスを定義したコードは、上記のようにsketch.js内に記述することもできますし、長い場合には専用のJavaScriptファイル(.js)にクラスのコード全体を移して、それをHTMLの<script>で読み込むこともできます。
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script> <script src="JitterBug.js"></script>