作成日 2007/5/5
最終更新日 2010/9/11
[ Japanese | English ]

カプセル化と属性と操作

 ここでは、カプセル化について説明します。
 あ、その前に、クラスとオブジェクトについていまいち理解していない人は「クラスとオブジェクト」を読んでください。

 カプセル化と何か?、カプセル化すると具体的に何が良いのか?について説明します。

 カプセル化はオブジェクト指向の3大要素のうちの1つです。
 他には継承と多態性がありますが、それらよりもはるかに大事(保守性、可読性につながる)と思っています。

1.カプセル化とは
2.カプセル化すると具体的に何が良いのか


1.カプセル化とは

 とりあえず、e-Words からの引用で我慢して

 オブジェクト指向プログラミングが持つ特徴の一つ。データとそれを操作する手続きを一体化して「オブジェクト」として定義し、オブジェクト内の細かい仕様や構造を外部から隠蔽すること。外部からは公開された手続きを利用することでしかデータを操作できないようにすることで、個々のオブジェクトの独立性が高まる。カプセル化を進めることによりオブジェクト内部の仕様変更が外部に影響しなくなり、ソフトウェアの保守性や開発効率が高まり、プログラムの部分的な再利用が容易になる。

e-Words カプセル化【encapsulation】より

 すまん。
 よくわからなかった…。
このページのトップへ

2.カプセル化すると具体的に何が良いのか

 オブジェクト指向はDOAが元になっているということを、めちゃくちゃいい加減にだけど、「POA、DOAとオブジェクト指向 3.POA、DOAとオブジェクト指向」で説明した。

 まず、データを設定するときにカプセル化したときとしなかったときのソース例をみて、何が良いのか考えてみよう。
 言語はVBAとします。

カプセル化をしない場合
(クラスモジュール名:Class1)

カプセル化をした場合
(クラスモジュール名:Class2)

Public number as Integer

Private m_number As Integer

Public Property Get number() As Integer
    number = m_number
End Property

Public Property Let number(ByVal newNumber As Integer)
    m_number = newNumber
End Property


 じゃあ、これを使う場合は、

Class1を使うサンプル

Class2を使うサンプル

Sub main()
    Dim obj As Class1
    Set obj = New Class1
    obj.number = 1000
    Debug.Print obj.number
End Sub
Sub main()
    Dim obj As Class2
    Set obj = New Class2
    obj.number = 1000
    Debug.Print obj.number
End Sub 

となる。
 
 …って同じじゃないかって。
 カプセル化って意味ないのかって?
 そうです。単にデータを取得するアクセスメソッド(VBAではProperty Getプロシージャ)とデータを設定するアクセスメソッド(VBAではProperty Let、Property Setプロシージャ)を作っただけではカプセル化なんてほとんど意味ない感じがします。

 じゃあ、こんど、クラスのnumber属性には0から100までの値しか入れたくない(例えば、テストの点数なんかそうです)場合を考えてみよう。

カプセル化をしない場合
(クラスモジュール名:Class3)

カプセル化をした場合
(クラスモジュール名:Class4)

Public number as Integer

Private m_number As Integer

Public Property Get number() As Integer
    number = m_number
End Property

Public Property Let number(ByVal newNumber As Integer)
    If (newNumber < 0) Or (newNumber > 100) Then
        Call Err.Raise(380)
    End If

    m_number = newNumber
End Property


 じゃあ、これを使う場合は、

Class3を使うサンプル

Class4を使うサンプル

Sub main()
    Dim obj As Class3
    Set obj = New Class3
    obj.number = 1000
    Debug.Print obj.number
End Sub
Sub main()
    Dim obj As Class4
    Set obj = New Class4
    obj.number = 1000
    Debug.Print obj.number
End Sub 

となる。

 …って、あれ、Class3を使うサンプルの方だけど、これだとnumberに1000という値が入っちゃうよ。
 ということはこれを防ぐためにどこかで値のチェックをする必要が出てくるけど、それはどこでやる?
 例えば、numberに値を設定する箇所すべてで値のチェックをしたら、もし、numberを使用している箇所が1000箇所もあったら、大変だ。(すべてに値のチェック処理を追加しないといけないし、漏れがあったら大変だ。)
 
 あと、他にも、例えば、2つのデータを一緒に変更しないと駄目なものがあったり、あるデータ(例えばステータス)が特定の値になったときだけ、有効な値を持つことができるデータを定義したい場合はやっぱり、カプセル化しないと厳しいです。(カプセル化しないとデータの不整合が起こりやすくなります。)

 一部繰り返しになりますが、カプセル化するメリットとしては、以下のようなものがあると思います。
  1. データを設定するときにチェックできるので、不整合が起こりにくくすることが出来る(※1)。
    スレッドセーフ 対応を行うことも可能になる。
  2. 2つ以上のデータを一緒に更新しないと駄目な場合、片方だけ更新しちゃったということが防げる。
  3. 設定されたときや取得されたときにログ出力するようなコードを書くことが出来るようになる。
  4. アクセス制御できる。
  5. VBAではできませんが、設定はコンストラクタからのみできるようにして、データ取得用のアクセスメソッドだけ作るようなことが出来る(※2)。
  6. 設定用と取得用のアクセスメソッドで可視性を別にする(VBAではできないけど、設定用のアクセスメソッドの可視性をProtected にし、取得用の可視性をPublicにする)ということが出来る。
  7. データ構造の変更に対して多少強くなる。後で、データ構造やクラス構成が変わっても、メソッドでデータ構造が抽象化されているので、メソッドを使用している側のクラスの修正が少なくてすむ(※3)。
  8. データ設定時に呼び出し側が意識する必要の無い処理を設定メソッド内でさせるようにすることで、呼び出し側の凝集度の低下を防ぐことができる(※4)。
※1:データチェックはデータに関連する機能であると考えると、同じクラスにデータとデータチェック処理があるということはモジュールの強度(凝集度)が高くなるということだと思う。
※2:イミュータブル(immutable)オブジェクト を作れる。
※3:モジュールの結合度が低くなる気がする。
※4:どういう意味かと言うと、例えば、java.util.Vectorクラスではデータの追加時に、容量が足りなかったときに自動で領域を増やすということをする。
このような処理は、呼び出し側はあまりやりたくないし、処理速度を考えたら絶対ではないが、意識もしたくない処理ですね。
もし、このような処理を呼び出し側で行うと、呼び出し側のクラスの凝集度が悪くなる。


 
Prev Up Next  Top
このページのトップへ

このページの利用によって発生した、いかなる損害について、このホームページの作成者は責任を負いません。
このページの間違いや嘘を見つけた方、このページに書いて欲しい情報がある方はメールをお願いします。

Microsoft 、Windows 、Visual Basic および Excel は米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。
ここではExcel® をエクセル、Visual Basic® for Applications をVBAと表記する場合があります。
Mac 、Mac OS 、Mac OS X は米国Apple Computer,Inc.の登録商標または商標です。
その他、社名および商品名、システム名称などは、一般に各社の商標または登録商標です。

このホームページの作成者はこれらの会社とはいっさい関係がありません。