主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

JavaScript: 簡単なスクリプトで理解する「オブジェクト指向」 その2

                
tags: JavaScript
その1 - そもそもオブジェクトとはなんぞ
その2 - "コンストラクタ"を使うと類似オブジェクトを作るのが便利
その3 - "プロトタイプ"を使うとオブジェクト作成が合理的になる
番外編 - クラスの概念に縛られた巷のオブジェクト指向の説明


 前回に「オブジェクト指向ってどんな指向?」ということに答えられるように、オブジェクト指向の説明を展開した(参考)。変数も関数も格納できるオブジェクトなるものがあって、実装する一つの機能に関する変数と関数を一つのオブジェクトにまとめてつっこんで、スクリプト(コード)を読みやすくして保守性を高めようというのが主なところ。オブジェクトに格納された変数をプロパティ、関数をメソッドと呼ぶ。以上がオブジェクト指向のはじめの一歩として理解しておくべきこと。
 巷にあるオブジェクト指向の説明で”インスタンス”とかいきなり使ってるのが、少し難易度上げてないかと思って、前回はオブジェクト指向の説明をそれら抜きで展開してみた。次はインスタンスについて説明を展開してみる。今回も抽象的すぎる例えを使わず、スクリプトをメインに説明を展開する。


 長方形が枠の中を跳ね返るアニメーションを作ってみる。最初はオブジェクトを使わず、変数と関数だけで作る。
デモ
function drawRectangle(context, color, x, y, w, h)
{
context.fillStyle = color;
context.fillRect(x, y, w, h);
}

function prepareDraw()
{
// canvasへの描画準備
var screen = document.getElementById("screen");
var context = screen.getContext("2d");

// 長方形のデータ(色、座標、サイズ、移動速度)
var color = "blue";
var x = 0;
var y = 0;
var w = 50;
var h = 50;
var speedX = 3;
var speedY = 3;

// 長方形の座標更新とキャンバスへの描画をする関数を定義
function refreshScreen()
{
// canvasを白で塗りつぶす
context.clearRect(0, 0, screen.width, screen.height);

// 長方形の水平方向の位置を更新
x += speedX;
// 長方形がキャンバスからはみ出てるなら、進行方向を逆に向ける
if (x < 0 || screen.width < x + w)
{
speedX = -speedX;
}

// 水平方向と同様の処理を垂直方向にも適用
y += speedY;
if (y < 0 || screen.height < y + h)
{
speedY = -speedY;
}

// 長方形を指定の位置に、指定の色、指定のサイズで描画
drawRectangle(context, color, x, y, w, h);
}

// 33msごとに関数refreshScreenを実行するように命令
window.setInterval(refreshScreen, 33)
}

window.onload = prepareDraw;


 関数prepareDrawの中にある関数refreshScreenのなかが、長方形の位置更新の手続き表記によって少しばかりごちゃごちゃしている。これは枠の中に入れる長方形を増やしたいとき、やっかいな障壁となる。たとえば枠の中にもう一つ動く長方形を加えたいとき、どうするだろうか。変数xをx1などと書いて、それぞれに対応した変数を必要な数だけ用意していくだろうか。とりあえず長方形を増やすことは置いておいて、まずオブジェクトを導入することで、関数refreshScreenの中のごちゃごちゃを緩和してみる。
 長方形に関する変数と関数がオブジェクトにまとまっていると便利そう。長方形の色、位置、サイズをプロパティにする。長方形の位置更新をメソッドにする。
デモ
function drawRectangle(context, rect)
{
context.fillStyle = rect.color;
context.fillRect(rect.x, rect.y, rect.w, rect.h);
}

function prepareDraw()
{
var screen = document.getElementById("screen");
var context = screen.getContext("2d");

// 長方形オブジェクトrectの定義とプロパティの用意
var rect = {color:"blue", x:0, y:0, w:50, h:50, speedX:3, speedY:3};
// 長方形オブジェクトrectの位置更新関数をメソッドupdateとして入れる
rect.update = function()
{
this.x += this.speedX; // このメソッド中での"this"は"rect"を指すようになっている
if (this.x < 0 || screen.width < this.x + this.w)
{
this.speedX = -this.speedX;
}

this.y += this.speedY;
if (this.y < 0 || screen.height < this.y + this.h)
{
this.speedY = -this.speedY;
}
};

function refreshScreen()
{
context.clearRect(0, 0, screen.width, screen.height);
rect.update();
drawRectangle(context, rect);
}

window.setInterval(refreshScreen, 33)
}

window.onload = prepareDraw;


 関数refreshScreenの中がだいぶスッキリ。だけどアニメーションは変わらずに動いているはず。

 ここでオブジェクトrectの定義の仕方をちょっと変える。下にあるスクリプトを、上のスクリプトでオブジェクトrectを定義している部分とごっそり置換しても動く。

function Rect()
{
this.color = "blue";
this.x = 0;
this.y = 0;
this.w = 50;
this.h = 50;
this.speedX = 3;
this.speedY = 3;
this.update = function()
{
this.x += this.speedX;
if (this.x < 0 || screen.width < this.x + this.w)
{
this.speedX = -this.speedX;
}

this.y += this.speedY;
if (this.y < 0 || screen.height < this.y + this.h)
{
this.speedY = -this.speedY;
}
};
}
var rect = new Rect();

 新しいオブジェクトの定義の仕方では、関数Rectを使うようになっている。関数Rectの中ではthisというオブジェクトにプロパティがポンポン追加され、updateというメソッドも追加されている。なんかさっきまでのオブジェクト宣言ととても似たようなことをしている。関数Rectの宣言が終わると、"var rect = new Rect();"という文でオブジェクトrectが定義されるようになっている。このオブジェクトrectもやっぱりプロパティとして自分の色や位置を持っているし、自分の位置を更新するメソッドも持っている。
 なぜわざわざオブジェクトの定義の仕方を書き換えたか。その意味は"var rect = new Rect();"という文に潜んでいる。長方形オブジェクトを一行の命令で変数rectに入れている。わざわざプロパティやメソッドを一つ一つ付け足していない。新しい長方形オブジェクトがまた欲しくなったら、適当な変数に"new Rect();"を渡すだけでいいのだ。二つ目の長方形が欲しくなったら"var rect2 = new Rect();"、三つ目は同様に"var rect3 = new Rect();"とでも書けばいい。これは「コンストラクタRectを使って、変数rectにインスタンスを入れている」ということになる。キーワードnewを受けて、新たなオブジェクトを作成してくれるのがコンストラクタ。新たに作成されたオブジェクトがインスタンスと呼ばれる。
 プログラムを書いていると、似たような部品を取りまわさなければならなくなることが多々ある。それを便利にするのがコンストラクタとインスタンスである。

 上の方で、アニメーションの枠内にある長方形を増やしたらどうなるかと言っていた。それをやってみる。とりあえず3つぐらいにしてみよう。スクリプトはどれだけ複雑になるだろうか。ここでそれぞれの長方形の初期位置をコンストラクタRectに引数で渡す形に書き換えておく。長方形の初期位置がすべて同じだと、増えたかどうかわからなくなってしまうので。
デモ
function drawRectangle(context, rect)
{
context.fillStyle = rect.color;
context.fillRect(rect.x, rect.y, rect.w, rect.h);
}

function prepareDraw()
{
var screen = document.getElementById("screen");
var context = screen.getContext("2d");

function Rect(x, y)
{
this.color = "blue";
this.x = x;
this.y = y;
this.w = 50;
this.h = 50;
this.speedX = 3;
this.speedY = 3;
this.update = function()
{
this.x += this.speedX;
if (this.x < 0 || screen.width < this.x + this.w)
{
this.speedX = -this.speedX;
}

this.y += this.speedY;
if (this.y < 0 || screen.height < this.y + this.h)
{
this.speedY = -this.speedY;
}
};
}
var rect1 = new Rect(0, 0);
var rect2 = new Rect(50, 50);
var rect3 = new Rect(100, 100);

function refreshScreen()
{
context.clearRect(0, 0, screen.width, screen.height);
rect1.update();
drawRectangle(context, rect1);
rect2.update();
drawRectangle(context, rect2);
rect3.update();
drawRectangle(context, rect3);
}

window.setInterval(refreshScreen, 33);
}

window.onload = prepareDraw;


 スクリプトの複雑度はぜんぜん増えなかった。シンプルな命令が6行増えたぐらい。なんでそんな素晴らしいことがというと、これがオブジェクト指向の恩恵。コンストラクタによって楽にオブジェクトのコピー(インスタンス)が作れるようになったのも大きな要因。で、それが今回の要点。
 前回はオブジェクトがどんなものか説明を展開した。そのオブジェクトが、コンストラクタを使うことで簡単にインスタンス(コピー)を作れるということが今回の要旨。この次は"プロトタイプ"あたりがキーワードだけど、コンストラクタとインスタンスを理解しているとすごい楽。


おまけ

応用
            

コメントの投稿

非公開コメント

プロフィール

hMatoba

Author:hMatoba
Github

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。