カプセル化

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

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

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

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

○ プロジェクト

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

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

サンプルダウンロード

○ 作成の準備

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

○ プログラム

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

Car.vb

  1. Public Class Car
  2.  'フィールド==========
  3.  'Public _speed As Integer     'スピード情報
  4.  Private _speed As Integer     'スピード情報(修正)
  5.  'Public _gas As Double     'ガソリン情報
  6.  Private _gas As Double     'ガソリン情報(修正)
  7.  'メソッド==========
  8.  '加速させるメソッド
  9.  Public Sub SpeedUp(sp As Integer)
  10.   'ガソリンを減らす
  11.   Me._gas -= sp / 10.0
  12.   If Me._gas < 0 Then     'ガソリンがマイナスになる場合
  13.    'ガソリンを0に補正し、スピードを増やさない
  14.    Me._gas = 0
  15.   Else
  16.    'スピードを増やす
  17.    Me._speed += sp
  18.   End If
  19.  End Sub
  20.  '減速させるメソッド
  21.  Public Sub SpeedDown(sp As Integer)
  22.   'スピードを減らす
  23.   Me._speed -= sp
  24.   If Me._speed < 0 Then     'スピードがマイナスになる場合
  25.    'スピードを0に補正する
  26.    Me._speed = 0
  27.   End If
  28.  End Sub
  29. End Class

○ クラス図

○ 解説

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

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

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

○ プログラム

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

Car.vb

  1. Public Class Car
  2.  'フィールド==========
  3.  'Public _speed As Integer     'スピード情報
  4.  Private _speed As Integer     'スピード情報(修正)
  5.  'Public _gas As Double     'ガソリン情報
  6.  Private _gas As Double     'ガソリン情報(修正)
  7.  'メソッド==========
  8.  '加速させるメソッド
  9.  Public Sub SpeedUp(sp As Integer)
  10.   'ガソリンを減らす
  11.   Me._gas -= sp / 10.0
  12.   If Me._gas < 0 Then     'ガソリンがマイナスになる場合
  13.    'ガソリンを0に補正し、スピードを増やさない
  14.    Me._gas = 0
  15.   Else
  16.    'スピードを増やす
  17.    Me._speed += sp
  18.   End If
  19.  End Sub
  20.  '減速させるメソッド
  21.  Public Sub SpeedDown(sp As Integer)
  22.   'スピードを減らす
  23.   Me._speed -= sp
  24.   If Me._speed < 0 Then     'スピードがマイナスになる場合
  25.    'スピードを0に補正する
  26.    Me._speed = 0
  27.   End If
  28.  End Sub
  29.  '初期化を行うメソッド
  30.  Public Sub Init(gas As Double)
  31.   Me._speed = 0
  32.   Me._gas = gas
  33.  End Sub
  34.  'スピードを取得する
  35.  Public Function GetSpeed() As Integer
  36.   Return Me._speed
  37.  End Function
  38.  'ガソリン量を取得する
  39.  Public Function GetGas() As Double
  40.   Return Me._gas
  41.  End Function
  42. End Class

○ クラス図

○ 解説

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

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

○ プログラム

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

Module1.vb

  1. Module Module1
  2.  '車の状態を表示するメソッド
  3.  Sub ShowData(c As Car)
  4.   'Console.WriteLine("スピード:{0}km", c._speed)
  5.   Console.WriteLine("スピード:{0}km", c.GetSpeed())
  6.   'Console.WriteLine("ガソリン:{0}L", c._gas)
  7.   Console.WriteLine("ガソリン:{0}L", c.GetGas())
  8.  End Sub
  9.  Sub Main()
  10.   'インスタンスを生成する
  11.   Dim obj As New Car()
  12.   'フィールド変数を初期化する
  13.   'obj._speed = 0
  14.   'obj._gas = 20.0
  15.   obj.Init(20)
  16.   '現在の状態を表示する
  17.   ShowData(obj)
  18.   While True
  19.    '操作を入力
  20.    Console.Write("1)加速 2)減速 9)終了:")
  21.    Dim inputdata As String = Console.ReadLine()
  22.    '操作によって分岐する
  23.    Select Case inputdata
  24.     Case "1"
  25.      '加速する
  26.      obj.SpeedUp(5)
  27.      ShowData(obj)
  28.     Case "2"
  29.      '減速する
  30.      obj.SpeedDown(5)
  31.      ShowData(obj)
  32.     Case "9"
  33.      'プログラムを終了する
  34.      Return
  35.    End Select
  36.   End While
  37.  End Sub
  38. End Module

○ 解説

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

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

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

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

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

前へ   次へ