Ssaw08 0930

Preview:

Citation preview

SSAW08 後期第3回Processingでオブジェクト指向プログラミング2008年9月30日

今日の課題

• Processingでオブジェクト指向プログラミング(OOP)について理解する• オブジェクトの概念を理解する• Processingでオブジェクト指向プログラミングをやってみる

OOP理解のポイント

• OOP = 難しい?• OOPの難しげな用語たち

• オブジェクト• メソッド• メッセージ• 継承• 抽象化• カプセル化• ポリモーフィズム (多態性)• クラス• インスタンス化

• 言葉だけだとかなり難しそう…• 本質を理解すると、実はやっていることは単純

プログラミング・パラダイムの変遷

• OOP以前:その1• 非構造化ログラミング

• 全てのコードが一つの連続したブロックに含まれている• アセンブリ言語・COBOL・BASICなど• goto文による制御• デバッグが難しい• 「スパゲティプログラム」

プログラミング・パラダイムの変遷 実例:COBOLによるHello World

000100 IDENTIFICATION DIVISION.000200 PROGRAM-ID. HELLOWORLD.000300 DATE-WRITTEN. 02/05/96 21:04.000400* AUTHOR BRIAN COLLINS000500 ENVIRONMENT DIVISION.000600 CONFIGURATION SECTION.000700 SOURCE-COMPUTER. RM-COBOL.000800 OBJECT-COMPUTER. RM-COBOL.000900001000 DATA DIVISION.001100 FILE SECTION.001200100000 PROCEDURE DIVISION.100100100200 MAIN-LOGIC SECTION.100300 BEGIN.100400 DISPLAY " " LINE 1 POSITION 1 ERASE EOS.100500 DISPLAY "HELLO, WORLD." LINE 15 POSITION 10.100600 STOP RUN.100700 MAIN-LOGIC-EXIT.100800 EXIT.

プログラミング・パラダイムの変遷

• OOP以前:その2• 手続型プログラミング

• 手続を呼び出す• サブルーチン、関数• C言語・FORTRAN・Pascal など• 3つの基本制御パターン

• 逐次処理• 分岐• 繰り返し

プログラミング・パラダイムの変遷

• 手続型プログラミング、3つの基本制御パターン

判断

処理1 処理2

YesNo

処理1

処理2

判断

処理

Yes

No判断

処理

Yes

No

逐次処理 分岐 反復

機能中心のソフトウェア開発の問題点

• 機能、手続きを中心に構成されたプログラムの問題点• プログラムの規模が大きくなるにつれて全体像の把握が難しい• 機能の追加・拡張・変更が難しい• コードの再利用が難しい• 規模が大きくなるにつれ、プログラミングにかかるコストが膨大に

• これらの欠点を改良するため、新たなパラダイムが考案される• オブジェクト指向プログラミング (OOP)

• Simula, Smalltalk, C++, Objective-C, Java, Python, Ruby, JavaScript, C# ...

• 「もの ( = オブジェクト)」の集まりとしてプログラムを構成する

オブジェクト指向プログラミングの概念

• オブジェクト指向プログラムのポイント:その1• 「もの (= オブジェクト)」という単位でプログラムを構成する

• オブジェクトは、属性(性質)とメソッド(動作・ふるまい)から構成

状態1状態2状態3

オブジェクト

メソッド1 メソッド2

メソッド3メソッド4

• 例:「りんご」をオブジェクトとして考える

オブジェクト指向プログラミングの概念

赤5.0甘い

ふじ

実がなる

成長する

落ちる腐る

青4.0

すっぱい

青リンゴ実がなる

成長する

落ちる腐る

オブジェクト指向プログラミングの概念

• オブジェクト指向プログラムのポイント:その2• オブジェクト同士がメッセージを送りあう

オブジェクト指向プログラミングの概念

• オブジェクト指向プログラムのポイント:その3• オブジェクトはメッセージを受け取り、それに応じた処理を行う

• メッセージの処理方法は、オブジェクト自身が知っていて、その処理はオブジェクトによって異なる (ポリモーフィズム)

getName()

オブジェクトA:人間

「田所 淳」

getName()

オブジェクトB:車

「トヨタカローラ」

• オブジェクト指向プログラムのポイント:その4• 必要のない情報は隠す (カプセル化)

• プログラムの実装全てを知る必要はない• 必要なインターフェイス(接点)だけ見せて、あとは隠す

オブジェクト指向プログラミングの概念

It’s this resemblance to real things that gives objects much of their power and appeal. They can notonly model components of real systems, but equally as well fulfill assigned roles as components insoftware systems.

Interface and Implementation

To invent programs, you need to be able to capture abstractions and express them in the programdesign. It’s the job of a programming language to help you do this. The language should facilitate theprocess of invention and design by letting you encode abstractions that reveal the way things work.It should let you make your ideas concrete in the code you write. Surface details shouldn’t obscurethe architecture of your program.

All programming languages provide devices that help express abstractions. In essence, these devicesare ways of grouping implementation details, hiding them, and giving them, at least to some extent,a common interface—much as a mechanical object separates its interface from its implementation, asillustrated in “Interface and Implementation” .

Figure 2-1 Inte rface and Im plem enta tion

910

11

87 6

implementationinterface

Looking at such a unit from the inside, as the implementor, you’d be concerned with what it’scomposed of and how it works. Looking at it from the outside, as the user, you’re concerned onlywith what it is and what it does. You can look past the details and think solely in terms of the rolethat the unit plays at a higher level.

The principal units of abstraction in the C language are structures and functions . Both, in differentways, hide elements of the implementation:

! On the data side of the world, C structures group data elements into larger units which can thenbe handled as single entities. While some code must delve inside the structure and manipulatethe fields separately, much of the program can regard it as a single thing—not as a collection ofelements, but as what those elements taken together represent. One structure can include others,so a complex arrangement of information can be built from simpler layers.

14 Inte rface and Im plem enta tion2007-12-11 | © 2007 Apple Inc. All Rights Reserved.

C H A P T E R 2

O bject-O riented P rogram m ing

インターフェイス 実装

オブジェクト指向プログラミングの概念

• オブジェクト指向プログラムのポイント:その5• オブジェクトから新たなオブジェクトを派生させることができる (継承)

植物

生物

動物

果物 穀物

りんご

ふじ 紅玉 デリシャス

バナナ マンゴー

オブジェクト指向プログラミングの概念

• クラス• クラスとは:オブジェクトの「型紙」• クラスをインスタンス化 (実体化) することでインスタンスオブジェクト

となる

色重さ(g)味

リンゴ(クラス)

実がなる

成長する

落ちる腐る

赤5.0甘い

ふじ(インスタンスオブジェクト)

実がなる

成長する

落ちる腐る

青4.0

すっぱい

青リンゴ(インスタンスオブジェクト)

実がなる

成長する

落ちる腐る

インスタンス化

ProcessingでOOPを実践してみる!

• 簡単なクラスの作成から徐々に高度なアニメーションへ発展させてみる

• 丸い点を描くクラスを作ってみる• クラスに必要となる属性

属性 (型 変数名) 内容

float x 丸のx座標の位置

float y 丸のy座標の位置

float diameter 丸の直径

丸を描く (クラスを使わない例)

• まずはクラスを使わないで、丸を描いてみる• setup() - 初期化• draw() - 画面の描画• グローバル変数として丸のパラメータを定義

• float x; → x座標• float y; → y座標• float diameter; → 直径

丸を描く (クラスを使わない例)

int x = 150;int y = 200;int diameter = 30;

void setup() { size(400, 400); smooth(); noStroke();}

void draw() { background(0); ellipse(x, y, diameter, diameter);}

丸を描く (クラスを使わない例)

丸を描くクラスを宣言、属性を定義

• 丸を生成するクラスを宣言する• クラス名:Spot• インスタンスオブジェクト:sp

• Spotクラスをインスタンス化して、spというインスタンスオブジェクトを生成

• クラスの属性• float x; → x座標• float y; → y座標• float diameter; → 直径

丸を描くクラスを宣言、属性を定義

• 現在のクラスをクラス図で表現

x: floaty: floatdiameter :float

Spot クラス名

属性

丸を描くクラスを宣言、属性を定義

Spot sp; // クラスを宣言

void setup() { size(400, 400); smooth(); noStroke(); sp = new Spot(); // クラスの生成 (インスタンス化) sp.x = 150; // クラスの属性「x」に150を代入 sp.y = 200; // クラスの属性「y」に200を代入 sp.diameter = 30; // クラスの属性「diameter」に30を代入}

void draw() { background(0); ellipse(sp.x, sp.y, sp.diameter, sp.diameter);}

丸を描くクラスを宣言、属性を定義

class Spot { float x, y; // x座標とy座標の位置 float diameter; // 円の直径}

丸を描くメソッドを定義

• Spotクラスの「ふるまい (= メソッド)」を定義する• メソッドはクラス内で関数として表現される• 必要となるメソッド

• 円を描く• メソッド名

• void display()

丸を描くメソッドを定義

• 現在のクラスをクラス図で表現

display(): void

x: floaty: floatdiameter :float

Spot クラス名

属性

メソッド

丸を描くメソッドを定義

Spot sp;

void setup() { size(100, 100); smooth(); noStroke(); sp = new Spot(); sp.x = 33; sp.y = 50; sp.diameter = 30;}

void draw() { background(0); sp.display();}

丸を描くメソッドを定義

class Spot { float x, y, diameter;

// display() メソッドを定義 void display() { ellipse(x, y, diameter, diameter); }}

丸を描くコンストラクタを定義

• コンストラクタ (構築子、Constructor) とは• オブジェクトをインスタンス化する際に呼び出されて、内容の初期化な

どを行う関数• クラス名と同一の名前を持つメソッドとして定義される• 戻り値は持たない• コンストラクタに渡す引数により初期化のバリエーションが定義される

• 例:Spotクラスのコンストラクタ• Spot( 引数1, 引数2, 引数3 ...) { }

• Spotクラスの引数に以下の3つのパラメータを渡せるようにする• float x;• float y;• float diameter;

丸を描くコンストラクタを定義

• 現在のクラスをクラス図で表現

Spot(xpos: float, ypos: float, diameter: float)display(): void

x: floaty: floatdiameter: float

Spot

丸を描くコンストラクタを定義

Spot sp; //オブジェクトの宣言

void setup() { size(400, 400); smooth(); noStroke(); sp = new Spot(150, 200, 30); //コンストラクタ }

void draw() { background(0); sp.display();}

丸を描くコンストラクタを定義

class Spot { float x, y, diameter; Spot(float x, float y, float diameter) { //クラスのインスタンス x(this.x) に、引数の値xを代入 this.x = x; //クラスのインスタンス y(this.y) に、引数の値yを代入 this.y = y; //diameter(this.diameter) に、引数の値diameterを代入 this.diameter = diameter; } void display() { ellipse(x, y, diameter, diameter); }}

丸を動かしてみる

• さらにクラスを拡張して、丸をアニメーションさせてみる• 2つの属性と、1つのメソッドを追加する

• 追加する属性• 移動スピード

• float speed• 移動方向 (1を上向き、-1を下向きと解釈する)

• int speed

• 追加するメソッド• 座標移動

• void move()

丸を動かしてみる

• クラス図を更新

Spot(xpos: float, ypos: float, diameter: float, speed:float)move(): voiddisplay(): void

x: floaty: floatdiameter: floatspeed: floatdirection: int

Spot

丸を動かしてみる

Spot sp; //オブジェクトの宣言void setup() { size(400, 400); smooth(); noStroke(); sp = new Spot(150, 200, 30, 5); //インスタンス化}

void draw() { fill(0, 15); rect(0, 0, width, height); fill(255); sp.move(); //丸の座標を更新 sp.display(); //丸を表示}

丸を動かしてみる

class Spot { float x, y; float diameter; float speed; //移動スピード int direction = 1; //移動の向き (1 下向き, -1 上向き) Spot(float xpos, float ypos, float dia, float sp) { x = xpos; y = ypos; diameter = dia; speed = sp; } void move() { //上下の端に到達した際に向きが反転する y += (speed * direction); if ((y > (height - diameter / 2)) || (y < diameter / 2)) { direction *= -1; } } void display() { ellipse(x, y, diameter, diameter); }}

丸を動かしてみる

複数のオブジェクトを配置する

• オブジェクトは、変数と同様に名前を変えて宣言することで複数宣言することが可能

• インスタンス化の際の引数を変化させることで、様々なバリエーションのSpotオブジェクトを同時に表示することができる

• Spotクラスから3つのオブジェクトを生成• sp1• sp2• sp3

複数のオブジェクトを配置する

Spot sp1, sp2, sp3; //3つのオブジェクトを宣言void setup() { size(400, 400); smooth(); noStroke(); sp1 = new Spot(100, 200, 80, 2); //sp1を生成 sp2 = new Spot(200, 200, 20, 5); //sp2を生成 sp3 = new Spot(300, 50, 60, 3); //sp3を生成}void draw() { fill(0, 15); rect(0, 0, width, height); fill(255); sp1.move(); sp2.move(); sp3.move(); sp1.display(); sp2.display(); sp3.display();}

複数のオブジェクトを配置する

class Spot { float x, y; float diameter; float speed; int direction = 1; Spot(float xpos, float ypos, float dia, float sp) { x = xpos; y = ypos; diameter = dia; speed = sp; } void move() { y += (speed * direction); if ((y > (height - diameter / 2)) || (y < diameter / 2)) { direction *= -1; } } void display() { ellipse(x, y, diameter, diameter); }}

複数のオブジェクトを配置する

より複雑な動きを作成する物理法則をシミュレート

• もっと複雑な動きに挑戦してみる• よりリアルな動きを実現できないか?• 簡単な物理法則をシミュレーションしてみる

• 重力• 摩擦• 反射

Particleクラス

• Particleクラス• 重力の影響を受けながら運動する物体をシミュレート

• 必要となる属性• x座標、y座標 → float x, y;• x方向の速度、y方向の速度 → float vx, vy;• 大きさ → float radius;• 重力の強さ → float grabity;

• 必要となるメソッド• コンストラクタ → Particle• 座標の更新 → update• 画面に表示 → display

Particleクラス

• Particleクラス、クラス図を作成

Particle(x:float, y:float, vx:float, vy:float, radius: float, gravity:float)update(): voiddisplay(): void

x: floaty: floatvx: floatvy: floatradius: floatgravity: float

Particle

Particleクラス

class Particle { float x, y; float vx, vy; float radius; float gravity = 0.1; Particle(int x, int y, float vx, float vy, float radius) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.radius = radius; } void update() { vy = vy + gravity; y += vy; x += vx; } void display() { ellipse(x, y, radius*2, radius*2); }}

Particleを使用する

Particle p;

void setup() { size(400, 400); noStroke(); smooth(); p = new Particle(0, height, 2.5, -8, 10.0);}

void draw() { fill(0, 10); rect(0, 0, width, height); fill(255); p.update(); p.display();}

Particleを表示する

クラスの継承

• Particleクラスを継承して、別の性質を付加する

• Particleクラス• 画面からはみ出てもそのまま落下していく• 画面の範囲でバウンドするように変更したい

• LimitedParticleクラス• Particleクラスを継承• 画面の範囲内でバウンドする• 常に一定の割合で摩擦の影響を受ける

LimitedParticleクラスの実装

• クラス図• Particleクラスと、それを継承するLimitedParticleクラス

Particle(x:float, y:float, vx:float, vy:float, radius: float, gravity:float)update(): voiddisplay(): void

x: floaty: floatvx: floatvy: floatradius: floatgravity: float

Particle

LimitedParticle(x:float, y:float, vx:float, vy:float, radius: float, gravity:float, friction:float)update(): voidlimit(): void

friction: float

LimitedParticle

LimitedParticleクラス

class LimitedParticle extends Particle { float friction = 0.99; LimitedParticle(int x, int y, float vx, float vy, float radius, float friction) { super(x, y, vx, vy, radius); //親クラスの引数を参照 this.friction = friction; //摩擦係数 } void update() { vy *= friction; vx *= friction; super.update(); // limit(); } void limit() { //表示範囲内でバウンドする if (y > height - radius) { vy = -vy; y = constrain(y, -height * height, height - radius); } if ((x < radius) || (x > width - radius)) { vx = -vx; x = constrain(x, radius, width - radius); } }}

LimitedParticleクラスを使用する

int num = 80;LimitedParticle[] p = new LimitedParticle[num];float radius = 1.2;void setup() { size(640, 480); noStroke(); smooth(); for (int i = 0; i < p.length; i++) { float velX = random(-4, 4); float velY = random(-10, 0); p[i] = new LimitedParticle(width/2, height/2, velX, velY, 2.2, 0.999); }}void draw() { fill(0, 24); rect(0, 0, width, height); fill(255); for (int i = 0; i < p.length; i++) { p[i].update(); p[i].display(); }}

LimitedParticleクラスを使用する

色の付加

• LimitedParticleクラスに色の属性を追加する• 色の属性

• color col;• 設定した色で塗るように、display() メソッドを拡張

• super.display() を実行• スーパークラス (Particle) の display() メソッドの呼びだし

• 色の指定の付加

色の付加

• クラス図追加

Particle(x:float, y:float, vx:float, vy:float, radius: float, gravity:float)update(): voiddisplay(): void

x: floaty: floatvx: floatvy: floatradius: floatgravity: float

Particle

LimitedParticle(x:float, y:float, vx:float, vy:float, radius: float, gravity:float, friction:float, col:color)update(): voidlimit(): voiddisplay(): void

friction: floatcol: color

LimitedParticle

色の付加

class LimitedParticle extends Particle { float friction = 0.99; color col; //色の属性 LimitedParticle(int x, int y, float vx, float vy, float radius, float friction, color col) { super(x, y, vx, vy, radius); this.friction = friction; this.col = col; } void update() { vy *= friction; vx *= friction; super.update(); limit(); } void limit() { if (y > height - radius) { vy = -vy; y = constrain(y, -height * height, height - radius); } if ((x < radius) || (x > width - radius)) { vx = -vx; x = constrain(x, radius, width - radius); } } void display() { fill(col); super.display(); //スーパークラス (Particle) の display() を呼び出し }}

色の付加

int num = 200;LimitedParticle[] p = new LimitedParticle[num];float radius = 1.2;void setup() { size(640, 480); colorMode(HSB,360,100,100,100); //HSBで色を指定する noStroke(); smooth(); for (int i = 0; i < p.length; i++) { float velX = random(-4, 4); float velY = random(-10, 0); color col = color(random(180,240),random(50,100),50); //ランダムに色指定 p[i] = new LimitedParticle(width / 2, height / 2, velX, velY, 2.2, 0.9999, col); }}void draw() { fill(0, 10); rect(0, 0, width, height); fill(255); for (int i = 0; i < p.length; i++) { p[i].update(); p[i].display(); }}

色の付加