値のカプセル化 - 萌えJava超入門
第十六章 インスタンス

値のカプセル化

カプセル化の具体的な例を見てみましょう。
アクセス修飾子 private がカギです。


目次


1.値を守れていないインスタンスの例


Member_01のインスタンス変数、max_hpnow_hp があります。
もちろん now_hp には、 max_hp 以下の値が入ることを期待したものです。
そして、now_hp が、正の整数であることも期待しています。
このままでは破綻してくださいと言わんばかりですね。

Member_01.java 前のページと同じです。
//Member_01.java
class Member_01 {
    int no;
    String name;
    int max_hp;
    int now_hp;

    void print(){
        System.out.println();
        System.out.println("NO." + no + "\t" + name);
        System.out.println("MAX_HP:\t" + max_hp);
        System.out.println("NOW_HP:\t" + now_hp);
        System.out.println("------------------");
    }
}

//Driver_02.java
class Driver_02{
  public static void main(String[] args){
        Member_01 shiz = new Member_01();//インスタンス化
        shiz.no = 1;
        shiz.name = "館石静乃";
        shiz.max_hp = 12;
        shiz.now_hp =  7;

        Member_01 maya = new Member_01();//インスタンス化
        maya.no = 2;
        maya.name = "桜井真夜";
        maya.max_hp = 11;
        maya.now_hp =  9;

        Member_01 tema = new Member_01();//インスタンス化
        tema.no = 3;
        tema.name = "安堂てまり";
        tema.max_hp = 14;
        tema.now_hp =  8;

        //-----処理-------------
        shiz.now_hp += 6;   //変数を直接操作
        maya.now_hp -= 10;  //変数を直接操作
        tema.max_hp -= 10;  //変数を直接操作
        //---------------------

        shiz.print();
        maya.print();
        tema.print();
  }
}

コマンドライン
>javac -encoding UTF-8 Driver_02.java
>java Driver_02

NO.1    館石静乃
MAX_HP: 12
NOW_HP: 13
------------------

NO.2    桜井真夜
MAX_HP: 11
NOW_HP: -1
------------------

NO.3    安堂てまり
MAX_HP: 4
NOW_HP: 8
------------------


Driver_02クラスを作る人が
うっかりすれば
そうゆうこともあるわよね。
その通り。
オブジェクト指向のプログラムが
出現する以前は
このようなバグに苦しめられてきた
らしいんだ。
うわ〜。
なんとかならないんスか?


2.値のカプセル化


Member_01クラスを書き換えます。
max_hp と now_hp の整合を守れる構造にしましょう。

//Member_02.java
class Member_02 {
    private int no;
    private String name;
    private int max_hp;
    private int now_hp;

    void setNo(int arg_no){
      /* 制限のない整数 */
      no = arg_no;
    }
    void setName(String arg_name){
      /* 制限のない文字列 */
      name = arg_name;
    }
    void setMax_hp(int hp){
      /* 初期HPは1以上 now_hp = max_hp */
      max_hp = hp<1 ? 1 : hp;
      now_hp = max_hp;
    }
    void setNow_hp(int hp){
      /* now_hpは0以上 max_hp以下 */
      now_hp = hp;
      now_hp = max_hp<now_hp ? max_hp : now_hp;
      now_hp = now_hp<0 ? 0 : now_hp;
    }
    void addMax_hp(int point){
      /* max_hpの変更は増減とも可
         max_hp は1以下にならない
         now_hp は max_hp を超えない */
      max_hp += point;
      max_hp = max_hp<1 ? 1 : max_hp;
      now_hp = max_hp<now_hp ? max_hp : now_hp;
    }
    void addNow_hp(int point){
      /* now_hpの変更は増減とも可
         now_hp はマイナスにならない
         now_hp は max_hp を超えない */
      now_hp += point;
      now_hp = now_hp<0 ? 0 : now_hp;
      now_hp = max_hp<now_hp ? max_hp : now_hp;
    }
    void print(){
        System.out.println();
        System.out.println("NO." + no + "\t" + name);
        System.out.println("MAX_HP:\t" + max_hp);
        System.out.println("NOW_HP:\t" + now_hp);
        System.out.println("------------------");
    }
}

Member_02クラスのインスタンス変数はすべて private によってインスタンス外からからのアクセスを禁止しています。
変数にアクセスするためには、Member_02クラスのインスタンスメソッドを利用するしかありません
(アクセスメソッドと言います。 後述↓)
Driver_02クラスと同じ値を、Driver_03クラスでも与えてみましょう。

//Driver_03.java
class Driver_03{
  public static void main(String[] args){
        Member_02 shiz = new Member_02();//インスタンス化
        shiz.setNo(1);
        shiz.setName("館石静乃");
        shiz.setMax_hp(12);
        shiz.setNow_hp(7);

        Member_02 maya = new Member_02();//インスタンス化
        maya.setNo(2);
        maya.setName("桜井真夜");
        maya.setMax_hp(11);
        maya.setNow_hp(9);

        Member_02 tema = new Member_02();//インスタンス化
        tema.setNo(3);
        tema.setName("安堂てまり");
        tema.setMax_hp(14);
        tema.setNow_hp(8);

        //-----処理-------------
        shiz.addNow_hp(6);   //アクセスメソッドから操作
        maya.addNow_hp(-10); //アクセスメソッドから操作
        tema.addMax_hp(-10); //アクセスメソッドから操作
        //---------------------

        shiz.print();
        maya.print();
        tema.print();
  }
}

コマンドライン
>javac -encoding UTF-8 Driver_03.java
>java Driver_03


NO.1    館石静乃
MAX_HP: 12
NOW_HP: 12
------------------

NO.2    桜井真夜
MAX_HP: 11
NOW_HP: 0
------------------

NO.3    安堂てまり
MAX_HP: 4
NOW_HP: 4
------------------



おお?
NOW_HP が MAX_HPを超えてないし
マイナスにもなってない!
だろ?
Member_02クラスを使えば、
NOW_HP と MAX_HPの整合を考えずに
Driver_03を設計できる。
Driver_03の内容に注力できるんだ。
値を守るって
こうゆうことなのね。

カプセル化とは(狭義のカプセル化、データの隠蔽)
  変数にアクセス制限 private を設けて、直接操作を行えないようにします。
  変数へのアクセス手段を、クラス内のメソッドに絞り込むためです。
  メソッドに、変数の値が適正に保たれるための処理を記述するのがポイントです。
  このような設計をカプセル化といいます。

アクセスメソッド
  オブジェクト内の変数へのアクセス手段として用意されたメソッドのことを
  アクセスメソッドといいます。

値の取り違えが無い
  アクセスメソッドは、インスタンスの中にあるので、
  他のインスタンスにある値を参照出来ません。
  これによって、Driver_02.javaの「処理」の部分のように
  処理の対象となる値が交錯する場面がありません。
カプセル化は値の取り違えを根本から根絶する
仕組みだ!!


まとめ
インスタンスは、複数の変数をまとめて保管するのに向いています。
インスタンスをカプセル化することで大きな効果を発揮します。



3.インスタンスでの private修飾子


インスタンスにしても、privateは効いているのでしょうか。
一応試してみますね。

//Driver_04.java
class Driver_04{
  public static void main(String[] args){
        Member_02 shiz = new Member_02();
        shiz.setNo(1);
        shiz.setName("館石静乃");
        shiz.setMax_hp(12);
        shiz.setNow_hp(7);

        System.out.println(shiz.name);//private変数にアクセス
  }
}

コマンドライン
>javac -encoding UTF-8 Driver_04.java
Driver_04.java:10: エラー: nameはMember_02でprivateアクセスされます
        System.out.println(shiz.name);
                               ^
エラー1個


インスタンス化しても
ちゃんとアクセス制限が
効いてるみたいね。



お疲れ様でした。




© 2019 awasekagami