2015年9月14日月曜日

クラス、インスタンス、オブジェクトとはなにか

Javaでのプログラミングにおけるクラスとは何でしょうか。Javaでは何かを処理する場合、その処理を行う「モノ」(オブジェクト、またはインスタンス)を作って、処理をします。この「モノ」を作り出す設計図となるのがクラスです。一度クラスを定義すれば、そのクラスから同じ働きをする「モノ」をたくさん作り出すことができるわけですね。

と言ってもわからないですね。いきなりたとえ話をされても意味不明だと思います。私も最初はそうでした。この設計図から作られる「モノ」、つまりオブジェクト指向を理解するためには「オブジェクト指向でない言語」にはどういう問題があったのかを理解する必要があります。

説明をわかりやすくするため、いったんバカになってみます。ここにバカなAさんがいるとします。バカのAさんがどういう風にコードを書くのか、Aさんのコードのなにが問題なのかを見てみましょう。

ある日、ちょっと規模の大きなプログラムを作れと命令されたバカのAさんはさっそくコードを書き始めました。彼は、以下のように下に下に伸ばしていくようにコードを書きました。延々とこのように書いており、何の工夫もありません。別にこの書き方でもコンパイルは通りますし、プログラムは作れます。

プログラムA
処理1
処理2
処理3

しかし、コードを書きながらAさんはふと思いました。「つ、疲れた・・・これ終わるのかよ・・・」そうです。Aさんはもう10時間以上も延々とコードを書いていたのです。何の工夫もなく下に下に書いていく方法だと大変効率が悪いため、ちょっと大規模なプログラムを作るとなると気の遠くなるような時間がかかるのです。しかも、ちょっと計画の修正が入るたびにコード全体を書き直すはめになります。

このままだとAさんは永遠に仕事が終わらないので、工夫が必要になってきます。うーん、ここの「消費税込みの値段を計算するためのコード群」だけど、プログラムのなかで何度も必要になるから、何度も何度も全く同じコードを書いてるなあ。

あれ?この「消費税込みを計算する」コードはひとつにまとめておいて、必要になった時にだけ呼び出したら読みやすくなるしコードを書くのが楽になるんじゃね?しかも一か所にまとめて書いておけば修正も楽じゃね?
double resultPrice = originalPrice * 1.05;
System.out.println(resultPrice);
double priceA = priceA * 1.05;
double employeeWage1 = originalWageA * 1.05;
double employeeWage2 = originalWageB * 1.05;
こんな感じのが延々と繰り返されているけど、青い部分のやってることって全く同じじゃん。 そこで、こういう風に書き直しました。
double resultPrice = taxCalc(originalPrice);
System,out,println(resultPrice);
double priceA = taxCalc(priceA);
double employeeWage1 = taxCalc(originalWageA);
double employeeWage2 = taxCalc(originalWageB);  
private static double taxCalc(double priceOriginal){
  return priceOriginal * 1.05;
これで万が一消費税が5%じゃなくなっても修正は1箇所だけで済むし、わざわざ「原価×1.05」という処理を何千回もコピペしなくて済むぞ!
こういう工夫をしたことでAさんのダラダラ長いコードはぐんと短くなりました。こういうふうにある手続きをひとまとめにして後で呼び出せるようにしたものを関数といいます。Javaでは関数をメソッドといいます。

さて関数というコードを管理するための名案を思い付いたことでAさんのプログラムはだいぶ見通しがよくなったものの、まだまだ見ずらいわ長いわ修正の際に直す必要がある箇所が死ぬほど多いわで良いプログラムと言えません。

しかし、ふとAさんは思いました。

「コードのここのところってさっき書いたコードとほとんど同じことをしてんだよなあ。さっき書いたコードを丸々コピペして持ってくるとまた長くなって読みづらいから、なんとかさっきの書いたコードを利用してなんとかならないかなあ」ここで役に立つのがオブジェクト指向の考え方です。まず、処理の大本となる共通操作をまとめて書いておきます。こういうコードのひとまとまりをクラスといいます。

このクラスから、「インスタンス」というクラスの分身を作り出したり、あるいは母クラスを継承しマイナーチェンジを加えた新たな子クラスを作り出したりします。そしてインスタンスを主体に実際の処理をします。そうすると仮に万が一計画の変更があっても、クラスに変更を加えるだけでそのクラスのインスタンスすべてに変更が反映されます。

この「クラスからインスタンスをつくって、インスタンスで実際の処理をする」ことの何が嬉しいかというと、そうすることで何度も同じ処理をコピペしなくて済むし、全体のコードも短くなって読みやすくなるし、開発が楽になるのです。

そう、オブジェクト指向プログラミングとはプログラマーさん達の開発を楽にしてくれるものなのです。コードを読み、実行する機械さんサイドからすればオブジェクト指向だろうがそうでなかろうがどうでもいいのです。しかし、開発者さんサイドからするとオブジェクト指向プログラミングをうまく使うことによって膨大な無駄な労力を省くことができるのです。

オブジェクト指向プログラミングとは

だいたいオブジェクト指向の意味がわかったところで、より具体的な説明をします。オブジェクト指向のプログラミングではまずクラスを定義し、そのクラスから実際の働きをするインスタンスを作り出すことになります。言ってみればクラスは設計図のようなもので、インスタンスはそこから作られるクラスの分身のようなものです。ただし、速度が違ったり、大きさが違ったり、微妙に違う分身を一つの同じ設計図からいくつでも作ることができます。

「オブジェクト」とはなんでしょうか?基本的にオブジェクト指向では、すべてのモノや事柄を「オブジェクト」として捉えています。クラスオブジェクトと言えばクラスのことを指しますし、インスタンスオブジェクトと言えばインスタンスのことを指します。

でも、実際のプログラミングではオブジェクトもインスタンスもほぼ同じ意味として使われているようです。よってインスタンスのことを指してオブジェクトと言うこともあります。この辺は結構混同されているようなのでご注意ください。

実際に書いてみる

実際にコードを書く中で何がクラスで何がインスタンスなのかを見てみましょう。

class Sample01{
public static void main(String args[]){
       //これがインスタンス(の作成)
        Subsample instancedayo = new Subsample(); 
        }
//これがインスタンスの設計図となるクラス
class Subsample{
void display(int x ,int y){
       System.out.println(" int x = " + x);
       System.out.println(" int y = " + y);
       }
}
この例でいえば、
「Sample01」と「Subsample」がクラス
「instancedayo」がインスタンス
「Sample01」と「Subsample」がクラス・オブジェクトで「instancedayo」がインスタンス・オブジェクト(両方ともオブジェクト)
です。

クラス「Subsample」からインスタンス「instancedayo」を作りました。これがインスタンスです。
次はインスタンス「instancedayo」を実際に使ってみます。

class Sample01{
public static void main(String args[]){
        Subsample instancedayo = new Subsample();
        instancedayo.display(100, 200); 
        }
class Subsample{
void display(int x ,int y){
       System.out.println(" int x = " + x);
       System.out.println(" int y = " + y);
       }
}

これを実行するとこうなります。

きちんと実行されていますね。さらにこのインスタンス「instancedayo」を使ってみます。

class Sample01{
public static void main(String args[]){
        Subsample instancedayo = new Subsample();
        instancedayo.display(100, 200);
        instancedayo.display(300, 400);
        instancedayo.display(1, 2);
        instancedayo.display(5, 8); 

        }
class Subsample{
void display(int x ,int y){
       System.out.println(" int x = " + x);
       System.out.println(" int y = " + y);
       }
}

これを実行するとこうなります。
こんなふうに一つのクラス「Subsample」からインスタンス「instancedayo」を作ることができます。

クラス「Subsample」からインスタンス「instancedayo」を作ることで、クラス「Subsample」内部のdisplay(int x ,int y)にアクセスすることができます。instancedayo.displayというのは、インスタンス「instancedayo」が、クラス「Subsample」内部のdisplay(int x ,int y)にアクセスしていることを意味しています。

一度クラスを定義し、インスタンスを作れば、扱いがかなり簡単になることがわかりますね。





もうすこし機能を拡張してみます。
class Sample01{
public static void main(String args[]){
        Subsample instancedayo = new Subsample();
        instancedayo.display(100, 200);
        System.out.println(" int x = " + instancedayo.getValuex(5));
        }
class Subsample{
int getValuex(int x){
        return x;
        }
void display(int x ,int y){
       System.out.println(" int x = " + x);
       System.out.println(" int y = " + y);
       }
}
このように書いたとき、instancedayo.getValuex()はクラス「Subsample」内部のint getValuex(int x)にアクセスしています。同じようにすることで、どんどん機能を拡張してどんどんインスタンス・オブジェクトを作ることが可能です。このようなプログラミングの仕方をオブジェクト指向プログラミングといいます。

なお、インスタンスは一度にいくつも作ることができます。
public class Sample01{
public static void main(String args[]){
        Subsample instancedayo = new Subsample();
        instancedayo.display(100, 200);
        System.out.println(" int x = " + instancedayo.getValueX(5));

  Subsample instancedayo2 = new Subsample();
  instancedayo2.display(500, 500);
  System.out.println(" int x = " + instancedayo2.getValueX(500));
        }
}
class Subsample{
int getValueX(int x){
        return x;
        }
void display(int x ,int y){
       System.out.println(" int x = " + x);
       System.out.println(" int y = " + y);
       }
}
上記の赤い部分を見てみてください。「instancedayo2」というインスタンスを新たに作成しています。青い部分で実際にインスタンスからメソッドを使用しています。

一度クラス(設計書のようなもの)を作ってしまえば、容易に様々なインスタンスを作って扱うことができます。