カプセル化
オブジェクト指向の利点の一つにカプセル化があります。
カプセル化とは、オブジェクトの内部のデータや振る舞いを隠蔽し
不必要な情報を外部に公開しないようにすることです。
例えばテレビを見るとき、利用者はリモコンの使い方がわかればよく
テレビの動作原理について知る必要はないですよね。
そういう内部の動作原理は隠してしまって(カプセル化)
外部へは、インタフェースだけを公開する(テレビでのインタフェースはリモコン)
というのがカプセル化の考え方です。
この節では、カプセル化のための機能の一つとして
Accessibility Annotationを説明します。
例として、パックマンのような格子状の盤面を動くゲームを考えます。
ここでは、前回レコードだったPoint型をクラスに書き換えたものを考えます。
2点の距離を表す関数distanceを追加したいと思うかもしれません。
例えばこんな感じ
距離を定義したPointクラス
type Point(ix:int,iy:int) = class
member val x = ix with get,set
member val y = iy with get,set
member this.abs x:int = if x>=0 then x else -x
member this.distance (p:Point) =
abs(p.x - this.x) + abs(p.y - this.y) //distanceの実装にだけabsを使いたい
end;;
let p = Point(10,10) in
let q = Point(20,20) in
printfn "%d" (p.distance q);
printfn "%d" (p.abs (-10));; //予期しない使われ方
距離を定義したPointクラス(軽量構文)
type Point(ix:int,iy:int) =
member val x = ix with get,set
member val y = iy with get,set
member this.abs x:int = if x>=0 then x else -x
member this.distance (p:Point) =
abs(p.x - this.x) + abs(p.y - this.y) //distanceの実装にだけabsを使いたい
let p = Point(10,10)
let q = Point(20,20)
printfn "%d" (p.distance q)
printfn "%d" (p.abs (-10)) //予期しない使われ方
この例ではdistanceを実装するのに
補助関数としてabsを定義しています。
しかし、最後の行のように補助関数も公開されているため
ユーザに予期しない使われ方をしてしまう可能性があります。
後になって、ユークリッド距離を使うように変更するため
absを削除して別の処理を書こうと思ったとします。
しかし、既に誰かがabsメソッドを使っているかもしれないため
使わなくなってしまったabsメソッドを
不用意に削除することが出来なくなります。
このような場合に備えて、Accessibility Annotationを使って
公開したくないクラスにアクセス制限をかけることが出来ます。
Accessibility Annotationには次の3つがあります。
・public
全てグローバルに公開する
ほとんどのデータ型のデフォルト
・internal
アセンブリ内に公開(DLLまたはEXE内)
・private
型定義またはモジュール内で公開
これを用いるた例が以下のプログラムです。
アクセス制御
type Point(ix:int,iy:int) = class
member val x = ix with get,set
member val y = iy with get,set
member private this.abs x:int = if x>=0 then x else -x //privateをつけた
member this.distance (p:Point) =
abs(p.x - this.x) + abs(p.y - this.y)
end;;
let p = Point(10,10) in
let q = Point(20,20) in
printfn "%d" (p.distance q);;
//printfn "%d" (p.abs (-10));; //この使い方はエラーになる
アクセス制御(軽量構文)
type Point(ix:int,iy:int) =
member val x = ix with get,set
member val y = iy with get,set
member private this.abs x:int = if x>=0 then x else -x //privateをつけた
member this.distance (p:Point) =
abs(p.x - this.x) + abs(p.y - this.y)
let p = Point(10,10)
let q = Point(20,20)
printfn "%d" (p.distance q)
//printfn "%d" (p.abs (-10)) //この使い方はエラーになる
absメソッドの前にくっついているprivateキーワードによって
Point型の定義の外からはabsメソッドにアクセスできないようになります。
このAccessibility AnnotationはF#のほとんどの場所で使用することが出来、
使用できる場所としては以下のものがあります。
・上記のようなmember定義(後述するクラス、構造体でも同様)
・new(..)オブジェクトコンストラクタ定義
・モジュール定義内のlet,module,type,extern宣言と
パターンの個別の識別子
自分だけが使うようなプログラムならそこまで気にする必要はないかもしれませんが
外部に公開するライブラリを書きたいというケースでは
是非、気をつけたい所です。