カプセル化

 今まで作成したサンプルは、危険が潜んでいます。それは、スピードやガソリン残量を直接指定することができるという点です。急発進や、急停車、走行中にもかかわらず、ガソリン残量を変更できてしまいます。このままでは、完成されたオブジェクトとは言えません。

フィールドに自由にアクセスできる
フィールドに自由にアクセスできる

 そこで、オブジェクト指向の特徴のひとつである「カプセル化」を使用して、安全なクラスを作成しましょう。クラスを定義する際に、アクセス修飾子を指定することにより、カプセル化を行うことができます。アクセス修飾子は以下のような指定ができます。

アクセス修飾子 意味
public 同一パッケージ、クラスの内部・外部問わず、自由にアクセスすることができます。アクセス制限のレベルが最も低い修飾子です。
protected 同一パッケージ、クラスの内部と、派生したクラスからはアクセスすることができます。
(省略) 同一パッケージ内からであれば、自由にアクセスすることができます。
private クラスの内部からしかアクセスすることができません。アクセス制限のレベルが最も高い修飾子です。
アクセス修飾子一覧

サンプルダウンロード

○ プログラム

 次のようにCarクラスを編集してください。

Car.java

  1. public class Car{
  2.  //フィールド==========
  3.  //public int speed;     //スピード情報
  4.  private int speed;     //スピード情報(修正)
  5.  //public double gas;     //ガソリン情報
  6.  private double gas;     //ガソリン情報(修正)
  7.  //メソッド==========
  8.  //加速させるメソッド
  9.  public void speedUp(int sp){
  10.   //ガソリンを減らす
  11.   this.gas -= sp / 10.0;
  12.   if(this.gas < 0){     //ガソリンがマイナスになる場合
  13.    //ガソリンを0に補正し、スピードを増やさない
  14.    this.gas = 0;
  15.   }else{
  16.    //スピードを増やす
  17.    this.speed += sp;
  18.   }
  19.  }
  20.  //減速させるメソッド
  21.  public void speedDown(int sp){
  22.   //スピードを減らす
  23.   this.speed -= sp;
  24.   if(this.speed < 0){     //スピードがマイナスになる場合
  25.    //スピードを0に補正する
  26.    this.speed = 0;
  27.   }
  28.  }
  29. }

○ クラス図

○ 解説

 3行目と6行目のフィールド変数のアクセス修飾子は「public」を指定していたため、外部のクラスから直接アクセスできました。今回は、カプセル化をするために一旦これらの行をコメントアウトしてプログラムから外しています。4行目と7行目でアクセス修飾子を「private」に変更することにより、外部のクラスからアクセスすることができなくなります。

カプセル化によりアクセスできなくなる
カプセル化によりアクセスできなくなる

 すると、このクラスを利用する記述の部分で変化が生じます。フィールドに値を代入したり、参照したりする記述ができなくなります。そのため、必要であればメソッドとして値を代入したり、参照したりする機能を追加しなくてはなりません。

○ コンパイル

C:¥work>javac Run1.java
Run1.java:6: エラー: speedはCarでprivateアクセスされます
         System.out.println("スピード:" + car.speed + "km");
                          ^
Run1.java:7: エラー: gasはCarでprivateアクセスされます
         System.out.println("ガソリン:" + car.gas + "L");
                          ^
Run1.java:15: エラー: speedはCarでprivateアクセスされます
         car.speed = 0;
           ^
Run1.java:16: エラー: gasはCarでprivateアクセスされます
         car.gas = 20.0;
           ^
エラー4個

C:¥work>

サンプルダウンロード

○ プログラム

 次のようにCarクラスにメソッドを追加してください。

Car.java

  1. public class Car{
  2.  //フィールド==========
  3.  //public int speed;     //スピード情報
  4.  private int speed;     //スピード情報(修正)
  5.  //public double gas;     //ガソリン情報
  6.  private double gas;     //ガソリン情報(修正)
  7.  //メソッド==========
  8.  //加速させるメソッド
  9.  public void speedUp(int sp){
  10.   //ガソリンを減らす
  11.   this.gas -= sp / 10.0;
  12.   if(this.gas < 0){     //ガソリンがマイナスになる場合
  13.    //ガソリンを0に補正し、スピードを増やさない
  14.    this.gas = 0;
  15.   }else{
  16.    //スピードを増やす
  17.    this.speed += sp;
  18.   }
  19.  }
  20.  //減速させるメソッド
  21.  public void speedDown(int sp){
  22.   //スピードを減らす
  23.   this.speed -= sp;
  24.   if(this.speed < 0){     //スピードがマイナスになる場合
  25.    //スピードを0に補正する
  26.    this.speed = 0;
  27.   }
  28.  }
  29.  //初期化を行うメソッド
  30.  public void init(double gas){
  31.   this.speed = 0;
  32.   this.gas = gas;
  33.  }
  34.  //スピードを取得する
  35.  public int getSpeed(){
  36.   return this.speed;
  37.  }
  38.  //ガソリン量を取得する
  39.  public double getGas(){
  40.   return this.gas;
  41.  }
  42. }

○ クラス図

○ 解説

 Carクラスを利用するクラスからフィールド変数に代入することで初期化をしていましたが、カプセル化によりフィールド変数にアクセスできなくなったために、34~37行目ではフィールド変数を初期化する手続きを用意しました。引数にガソリン量を受け取り、スピード情報を「0km」に、ガソリン量を引数のデータで初期化しています。

 また、Carクラスを利用するクラスからフィールド変数を直接参照してスピード情報やガソリン量を取得していましたが、カプセル化によりフィールド変数にアクセスできなくなったために、40~42行目ではスピードを戻り値として返す手続きを、45~47行目ではガソリン量を戻り値として返す手続きを用意しました。

○ プログラム

 では、Carオブジェクトを生成して利用しているプログラムも修正しましょう。

Run1.java

  1. import java.io.*;
  2. public class Run1{
  3.  //車の状態を表示するメソッド
  4.  public static void showData(Car car){
  5.   //System.out.println("スピード:" + car.speed + "km");
  6.   System.out.println("スピード:" + car.getSpeed() + "km");
  7.   //System.out.println("ガソリン:" + car.gas + "L");
  8.   System.out.println("ガソリン:" + car.getGas() + "L");
  9.  }
  10.  public static void main(String[] args) throws Exception{
  11.   //インスタンスを生成する
  12.   Car car = new Car();
  13.   //フィールド変数を初期化する
  14.  //car.speed = 0;
  15.  //car.gas = 20.0;
  16.  car.init(20.0);
  17.   //現在の状態を表示する
  18.   showData(car);
  19.   //キーボード入力の準備をする
  20.   BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  21.   while(true){
  22.    //操作を入力
  23.    System.out.print("1)加速 2)減速 9)終了:");
  24.    String inputdata = br.readLine();
  25.    //操作によって分岐する
  26.    switch (inputdata){
  27.     case "1":
  28.      //加速する
  29.      car.speedUp(5);
  30.      showData(car);
  31.      break;
  32.     case "2":
  33.      //減速する
  34.      car.speedDown(5);
  35.      showData(car);
  36.      break;
  37.     case "9":
  38.      //プログラムを終了する
  39.      return;
  40.    }
  41.   }
  42.  }
  43. }

○ 解説

 6、9行目では、Carクラスのカプセル化によりフィールド変数にアクセスできなくなったためにエラーとなります。そのため、コメントアウトしています。その代わりに7、10行目で新たに手続きとして用意されたスピードやガソリン量を取得するメソッドを呼び出しています。

 18、19行目では、Carクラスのカプセル化によりフィールド変数にアクセスできなくなったためにエラーとなります。そのため、コメントアウトしています。その代わりに20行目で新たに手続きとして用意された初期化を行なうメソッドを呼び出しています。

 このようにカプセル化をすることにより、クラスの利用者側から直接データを参照・設定できなくすることにより、クラス作成者の意図に沿わない方法で使用されるのを避けることができます。手続きを用意することによって必要な処理をさせることができます。また、クラスを利用する側も特に意識することなく安全に、クラスを利用することができるようになります。

カプセル化したフィールドにアクセスするメソッドを用意
カプセル化したフィールドにアクセスするメソッドを用意

 一般的に、フィールドはprivate指定で非公開にして、メソッドをpublicで公開にします。ただし、内部処理などで使うメソッドはprivateにして、外部から利用されないようにします。

前へ   次へ