カプセル化

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

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

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

アクセス修飾子 意味
public クラスの内部・外部問わず、自由にアクセスすることができます。アクセス制限のレベルが最も低い修飾子です。
protected クラスの内部と、派生したクラスからはアクセスすることができます。
internal 同一プログラム内からであれば、自由にアクセスすることができます。
protected internal protectedとinternalを合わせたアクセス修飾子です。
private クラスの内部からしかアクセスすることができません。アクセス制限のレベルが最も高い修飾子です。
アクセス修飾子一覧

○ プロジェクト

 プロジェクトを作成して確認してみましょう。

プロジェクトの種類 コンソール アプリケーション
プロジェクト名 CapsuleTest

サンプルダウンロード

○ 作成の準備

 「InstanceTest」プロジェクトを修正して作成しましょう。IntsantTestソリューションフォルダーのコピーを作り、フォルダー名をプロジェクト名に変更してください。コピーしてフォルダー名を変更したフォルダーからソリューションファイルを開いてVisual Studioを起動してください。起動するとプロジェクト名は変わっていませんが、複製を編集することになります。

○ プログラム

 Carクラスのフィールドを外部から直接操作できないようにしましょう。次のようにプログラムを変更してください。

Car.cs

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

○ クラス図

○ 解説

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

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

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

○ プログラム

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

Car.cs

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

○ クラス図

○ 解説

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

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

○ プログラム

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

Program.cs

  1. class Program
  2. {
  3.  //車の状態を表示するメソッド
  4.  static void ShowData(Car car)
  5.  {
  6.   //Console.WriteLine("スピード:{0}km", car.speed);
  7.   Console.WriteLine("スピード:{0}km", car.GetSpeed());
  8.   //Console.WriteLine("ガソリン:{0}L", car.gas);
  9.   Console.WriteLine("ガソリン:{0}L", car.GetGas());
  10.  }
  11.  static void Main(string[] args)
  12.  {
  13.   //インスタンスを生成する
  14.   Car car = new Car();
  15.   //フィールド変数を初期化する
  16.   //car.speed = 0;
  17.   //car.gas = 20.0;
  18.   car.Init(20);
  19.   //現在の状態を表示する
  20.   ShowData(car);
  21.   while (true)
  22.   {
  23.    //操作を入力
  24.    Console.Write("1)加速 2)減速 9)終了:");
  25.    string inputdata = Console.ReadLine();
  26.    //操作によって分岐する
  27.    switch (inputdata)
  28.    {
  29.     case "1":
  30.      //加速する
  31.     car.SpeedUp(5);
  32.      ShowData(car);
  33.      break;
  34.     case "2":
  35.      //減速する
  36.      car.SpeedDown(5);
  37.      ShowData(car);
  38.      break;
  39.     case "9":
  40.      //プログラムを終了する
  41.      return;
  42.    }
  43.   }
  44.  }
  45. }

○ 解説

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

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

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

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

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

前へ   次へ