雰囲気でプログラミングをしているので、OOP (Object-Oriented-Programming, オブジェクト指向プログラミング) について雑に学び直した。
オブジェクトとはモノのこと。オブジェクト指向とは、モノを操作する (操作できるモノを作る) プログラミングの方向性のこと。
モノには様々あるが、例えばリモコンやゲームコントローラなどのアクションを起こせる物体を想像すると良い。
可能な操作が一覧されており、命令を行うと内部の仕組みを知らなくても (隠蔽されていても) 意図した動作になる。
そうした「リモコン」と「テレビ」、「ゲームコントローラ」と「ゲーム機」のようなモノとモノ同士、つまりオブジェクトの操作を組み合わせてプログラミングしていこうという、現実世界での任務遂行方法を模倣したプログラミング手法と言える。
OOPにおける特徴的な概念として、カプセル化が挙げられる。
前述のとおり、リモコンやゲームコントローラの使い方は知っていても、その内部の仕組みは知らないし、知らなくても問題なく利用できる。
逆に直接基盤などへ触れられるようになっていると、意図されていない操作で破壊してしまう可能性がある。
そのような操作を防ぐため、利用者向けにはボタンで操作を、ランプで状態の確認をできるようにしている。
カプセル化とはつまり、オブジェクトの持つフィールドを直接操作する必要がないよう、メソッドで覆うことを指す。これにより、外側の操作を変えずとも、メソッドの実装で実際のフィールドに対する操作を安全に、あらゆる箇所で行える。
継承関係とは、要はIs-a関係の話である。つまり、B extends A
の場合、BはAの一種 (亜種)
であると判断し、Aとして扱うこともできることの話である。
具体例で言うと、View
の一種としてButton
やProgressBar
が存在し、これらには例えば共通の「描画位置情報を返しなさい」というメソッドがあるかもしれない。それらは恐らく各々の描画情報さえあればそれを元に計算が可能であり、別の実装を必要としない。そういった共通の処理を "継承" することを指す。
紛らわしいが、共通の "メソッド名" で各々の異なる実装を呼べることは多態性になるため継承ではない。継承で話しているのはinterface
の話である。override
ではない。
もう少し現実寄りの例にする。例えば "果物" の種類として "リンゴ", "バナナ" があるとする。果物の定義に確固たるものは無いが、たとえば ”草木の実である" とする。
つまり裏を返せばリンゴとバナナには共通処理として「木から実る機能」が存在することになる。これを各果物ごとに実装すると、人的ミスにより定義が揺らぎかねない。もしかしたら間違えて土から生やしてしまうかもしれない。
なので基底となる「果物」自体に木から実る機能を実装してしまえば、手間も掛からずミスも防げる。
多くのリモコンやゲームコントローラは、たとえ機種が異なっても説明書を読まずに利用できる。その理由には、同様のラベル、同様の形状など、説明を不要とする工夫が挙げられる。そしてそのお蔭で、知らずとも期待した動作をする。
しかしながら実際には、ボタンを押す操作により送出される信号は機種により異なる。もちろん画面上の表示も異なるだろう。
このように具体的な中身の処理を知らずとも様々な処理が起こりうるのが多態性である。同様のラベルを持つものに意味付けをし、具体的な処理は実装に任せることができる。
カプセル化すればなんでも隠蔽できると思っちゃダメ。あくまでも1つのオブジェクト (クラス) は1つの役割だけを持つようにする。例えば、データ構造を持つ、他のデータ構造にマッピングする、該当するデータ構造を複数の永続化機構のいずれかから取り出す、など。
リンゴは自身の特徴や状態だけを持つと期待されるため、もしキャッシュ機能が埋め込まれていれば誰も想像できず、実装者は意図しない挙動で混乱するだろう。
自分のサブクラスになり得るものにより処理を変えたりしちゃダメ。たとえば「果物」内で「もし自分がリンゴなら」「もし自分がバナナなら」という実装をしてはいけない。何故ならあくまでも「果物」の一種に「リンゴ」や「バナナ」があるのであって、「果物」自身は自分が何になり得るのかなど気にしちゃいないから。「自分は果物だ」としか知らないし、実際に「果物」として扱われる際にはそれにより挙動が変わってしまってはいけない。実装者はデバッグ時に意図しない挙動が発生し混乱するだろう。
適切な名前付けで実装を担保するため、変化に強い。各オブジェクトが名付けされたとおりの小さな機能のみを持つため、その名前付けされた処理が正しく実装されている限りはオブジェクト間で正しく作用する。
理解するのがどうも難しいらしい。関わる開発者全員がOOPに精通していること、つまり適切な名前付けと責務分離が可能なことを前提としているため、誰か1人でも理解していない場合、暗黙の了解が壊れてしまう。何が起きるかというと、各 "気をつけること" で書いていることが発生し、バグの温床になる。
各オブジェクトがあって、それをどのように調理するか、という思考を全員が身に付けるまでは手続型言語の方が良いかもしれない。