Java によるオブジェクト指向の表現

前回は「クラスの作り方」「フィールド」「メソッド」といった Java の「機能」について説明しました。
# プログラミングの「機能」と「考え方」は異なるお話というトピックを覚えているでしょうか。
# http://d.hatena.ne.jp/java-book/20081103/1225720315

連載初期に少し話しましたが、「Java で書いたプログラムがオブジェクト指向なプログラムになる」わけではなく、「Java で書くとオブジェクト指向な表現がしやすい」ということを、前回の復習も兼ねて改めて説明してみたいと思います。

オブジェクト指向とは?

オブジェクト指向という言葉は曖昧に使われることがあるのですが、今回の記事に限っては、
「オブジェクト(対象)にメッセージという形で頼み事をすると動いてくれる」「オブジェクトは状態を持つことができる」という見方でプログラムの振る舞いをとらえる考え方を指すことにします。

class という機能で実現できること

プログラムを Java では class の中に書くことができるというのが前回の説明にありました。
オブジェクト指向を表現するための準備を class を記述する中で行うことができそうです。

# 「class の機能」と呼ぶには多少語弊があるのですが、class の機能に関しては追々、説明していきたいと思います。

class SavingsBox {
    // 合計金額
    private int total;

    // コンストラクタ
    public SavingsBox ( ) {
        total = 0;
    }

    // 100円玉を入れる。
    public void insertCoin100 ( int coins ) {
        if (coins > 0) {
            total += coins * 100;
        }
    }

    // 500円玉を入れる。
    public void insertCoin500 ( int coins ) {
        if (coins > 0) {
            total += coins * 500;
        }
    }

    // 合計金額を調べる。
    public int getAmount ( ) {
        return total;
    }
}
オブジェクト指向 Java
オブジェクトを定義したい class に定義を書く
オブジェクトを生成したい new でコンストラクタを呼ぶ
メッセージで呼びかけたときの振る舞いを定義したい メソッドを振る舞い定義領域として使用する
状態を定義したい フィールドを状態保持領域として使用する

オブジェクト指向的なシナリオの記述

class Main {
    public static void main(String[] args) {
        SavingsBox box = new SavingsBox(); // シナリオ上に貯金箱を登場させる。
        box.insertCoin100(3); // 貯金箱に100円玉を3枚入れることをメッセージとして伝える。貯金箱の内部状態が100円玉が3枚挿入された状態になる。
        box.insertCoin500(2); // 貯金箱に500円玉を2枚入れることをメッセージとして伝える。貯金箱の内部状態が500円玉が2枚挿入された状態になる。
        int total_yen = box.getAmount(); // 貯金箱に総額を尋ねる。
        System.out.println(total_yen); // 貯金箱の総額を表示する。
    }
}

main メソッドの中で、Java の機能である new でコンストラクタを呼び出したり、メソッドを呼び出しています。
しかし、これはコメント文に書かれているようなシナリオの表現の一つとしてとらえることにします。

プログラムを書くときには、シナリオがあることが前提で、そのシナリオを Java で表現するという流れになります。
前回、最初にメソッドの外装だけ作り、中身を後から書いた理由は、シナリオだけなら中身(実装)がなくても準備することができるからです。
シナリオには、オブジェクトにどんな頼み事をする必要が出てくるかを考え、メソッドの外装を考えることになります。

private フィールドの意味

SavingsBox box = new SavingsBox();
box.insertCoin100(3);
box.total = 12345;      // フィールドの値を直接書き換える。
int total_yen = box.getAmount();
System.out.println(total_yen);

total フィールドを public にしたらメソッドを用意した意味がないので、private にして直接値を書き換えられないように制限をしたというのが前回の説明でした。
だったらメソッドは用意しなくてもいいんじゃないかという声が聞こえてきそうです。
しかしオブジェクト指向を表現するという前提があるならば、「オブジェクト(対象)にメッセージという形で頼み事をすると動いてくれる」ことを、より自然に表現したいものです。
なので、「メッセージはメソッドで表現する」という使い方で統一し、メッセージによって状態を変化させたいために、このような実装となるわけです。

もしも「= (イコール) 演算子」をメッセージと見立ててしまえば、オブジェクト指向の枠からは外れていないため、public フィールドで実現しても構わないことになりそうです。
実際、Ruby のように演算子もメッセージであると見なすプログラミング言語はあります。
しかし Java で実装した場合、= 演算子と public フィールドでは表現力に乏しいのです。
マイナスの金額なども入れることができるようになってしまうのですから。

public void insertCoin100 ( int coins ) {
    if (coins > 0) { // 金額は必ずプラスのはず
        total += coins * 100;
    }
}

これをメソッドで表現することを考えれば、正しい金額のみを投入可能にすることでシナリオから矛盾をなくすことができるのです。
# 安全性を高めるという意味もあります。

インスタンスオブジェクトとクラスオブジェクト

new SavingBox();

この一文も、オブジェクトを生成すると一言で済ませてしまえばそれまでですが、あくまで Java の機能としてとらえてみましょう。
「オブジェクトを生成する」と言いましたが、正確にはインスタンスオブジェクトといいます。
オブジェクト指向らしく「すべてをメッセージとして表現する」ことを前提に置いてしまえば、new もメッセージとしてみることができないでしょうか。
メッセージというからには何かしらのオブジェクト(対象)がいるはずですが、「SavingBox というクラスに対して new というメッセージで話しかけると、インスタンスを1つくれる」という見方をするとどうでしょう。
つまり、クラスもオブジェクトであり、「クラスオブジェクトに new というメッセージで呼びかけるとインスタンスオブジェクトが返ってくる」と見てしまうのです。

Java では、クラスはオブジェクトとは見なしません。しかしそのようにとらえることも可能であり、SmalltalkRuby 等ではこのように考えられています。
# それならクラスオブジェクトはどこから生成されるのかというと、今度はメタクラスという話が出てきますが、Java の機能としてはありません。

class は設計図であるという説明されることもあり、言語によって考え方が異なります。
# いずれかが間違っているというわけではありませんが、そのことを知っているかどうかで大きく違うため、曖昧に「クラスは設計図のようなもので〜」という説明を受けた場合は注意したほうがいいかもしれません・・・?

まとめ

以上のように、オブジェクトの振る舞いと状態を Java の機能であるメソッドとフィールドを使うと表現することができるのであって、プログラミング言語の機能はプログラミングの考え方を表現するツールということを念頭に置くことが、言語に振り回されないプログラマへの一歩ではと考えます。