2018-01-31

Cプログラマのための、
C++オブジェクト指向簡単裏口入門(11)

クラス設計お悩みポイント?

今日はC++のコーディングから少し離れて、コーヒーブレイクのような記事にします。手を休めて、気軽に考えて見てください。

簡単に試すことが難しい?

僕がオブジェクト指向プログラミングを始めてみてそれが短所だと感じることがあったのは、コーディングをして、その動作確認が出来るようになるまでの時間が長くなったことでした。ちょっとだけ辛抱が必要なのです。

原因の1つは、プログラムの依存サイクルの違いです。

C言語では、プログラミングの屋台骨は関数しかありません。関数が、他の関数を呼び、またその関数が他の関数を呼ぶ。

呼ぶ方も呼ばれる方も自分で作る場合、意味的により要約されているのは呼ぶ側であることが一般的です。とりあえず、要約されている部分を実行したいとなった場合、そこから呼ばれる関数をダミーで作っておくことはさほど難しくありません。実行時間やそのタイミング等に依存しないのであれば、戻り値だけを気にすればいいからです。

なので、とりあえず通して動かしてみる、ということが比較的簡単にでき、徐々に詳細部分を作り込んでいく、ということが可能です。

オブジェクト指向プログラミングで中心となるのはクラスです。
通常、クラスは他のクラスのユーザになります。関数が他の関数のユーザになるのと同じです。

クラスを使う、というのはどういうことでしょうか。
関数を使うというのは、引数があって、その結果としての戻り値しかありません。
クラスを使うのは、多くはまずインスタンス化をして、そして、そのクラス独自の使用方法があり、最後に廃棄する。
問題はその独自の使用方法です。ファイルを扱うなら、オープンがあってクローズをしなければならないかも知れません。クラスのインスタンス化の段階から例外をキャッチしてエラー処理を行う必要もあるかも知れません。
クラスを使うというのは、いくつかの手続きが必要なのです。

クラスというのは機能を使うための手続きの集約でもあり、つまりインタフェースそのものと言えます。クラスというインタフェースと、関数という手続きがセットになっているので、依存のサイクルは関数だけの場合と比較すると以下のようになります。

矢印を依存方向だとすると、以下のような依存サイクルになります。

C言語の場合:
[関数][関数] → ・・・

C++のオブジェクト指向プログラミングの場合:
[クラスインタフェース]+[関数] →  [クラスインタフェース]+[関数] → ・・・

つまり、関数が関数だけに依存しているC言語の場合は、使用する関数のダミーを作るだけで仮の実行が可能です。しかも、それは比較的少数のダミー関数を作ればいいだけになります。

クラスに依存している場合、依存されるクラスのダミーを作る必要があるのですが、その中で使用するメソッド(メンバー関数)のダミーを全て(通常は複数)作っておいたり、プロパティ(メンバ変数)に外部から直接アクセスさせるのであれば、そこにダミーのデータを設定するなどの仮の処理が必要になるかも知れません。
これは本当に仮なのでしょうか。仮であっても、やや本番に近いプログラミングをする必要があると言えるのではないでしょうか。

以上のように、ダミーのクラスを作ることが二度手間になる部分もあります。
その無駄を避けるために、クラス中心のプログラミングではトップダウンよりもボトムアップで、依存される側のクラスから実装を完了させていく手順になることが自然な流れになります。
つまり、全体的な動きを試してみる、というのがどうしても後半に回されてしまいがちなのですね。

このことは、最近注目され、導入事例も増えてきているアジャイル開発とは矛盾してしまうような気もするのです。残念ながら、僕にはアジャイル開発のスキルが不足しています。こういったオブジェクト指向開発と、アジャイル開発をどのように折り合いを付けていくのか、ある程度の想像はできますが、現実を知りません。

プロパティ or 継承の問題

最右翼のオブジェクト指向であれば、あまり迷うことはなく、全て継承して解決するのかも知れませんが、現実的な解を求められるC++では悩むことがあります。

たとえば、[人]クラスを作りますが、男女を区別する必要があるとします。
[人]クラスの中にプロパティ(変数)を追加して、そこに男性か女性かを設定する、というのが1つの方法です。
男性は男子トイレを使う、女性は女性トイレを使う、という判定がクラスのメンバ関数の中で必要になったとき、このプロパティを見て判断する訳ですね。

もう1つの解は[人]クラスを継承して[男性]クラス、[女性]クラスを作ることです。継承したこれらのクラスは、自分の性別を最初から知っているので、どのトイレを使うかは最初から決まっています。なので、判定処理が不要になるメリットがあります。

最近では、戸籍上の性別が本人の本質的な性と一致しないケースも無視できなくなっています。だから、戸籍上の男性が、女性用のトイレを使うことだってあるわけです。

プロパティで対応していたクラスであれば、戸籍上の性別だけではなく、本人が実際にはどちらの性で生きているかを示すプロパティを更に追加することになりそうです。

継承の場合はどうでしょうか。
[男性]クラスと[女性]クラスを更に継承し、両方の特徴を兼ね備えた[男女]クラスを作った上で、更に戸籍上の性と実生活上の性の情報を持って処理を分岐させなければならなくなりそうです。

トイレに行くという処理は架空の話ではなくて、たとえばある会社での社員が、どちらのトイレを使うのか統計を取り、トイレの設計を行うといった場合に応用する話です。
他にも、更衣室、場合によっては銭湯のようなお風呂、制服、きっと、それぞれによってどちらを使うのかが異なる人もいるだろう、ということを考えると、一概に決めることはできなくなります。

もし、何かを利用する場合にどちらの性別用のものを選ぶか、という視点で継承を考えた場合、それぞれの性別、たとえばトイレ用の性別クラス、更衣室用の性別クラス、制服用の性別クラス、そんな基底クラスから全ての性質を多重に受け継いだクラスが必要になりそうです。
こうなってくると、やはり人間にとって性別とは単なるプロパティであって、しかも、それは1種類ではないと考えた方が現実にもマッチして自然に思えてきますね。

継承で表現するクラスは、もう少し普遍性のあるものの方が適しているかも知れません。
仮に継承を使ったとしても、判定が必要になるため、結局プロパティを持つことにもなります。

じゃあ、何でもかんでも継承はできるだけ避けて、プロパティで分岐させればいいじゃないか、というのもまた違うのです。それを追求すると、クラスはアプリケーションにたった1つで良くなってしまう可能性すら出てきてしまいます。
それは明らかな間違いである、と判りますね(例示は割愛します)。

考えること、議論することが大切

ああでもない、こうでもないと考えてきましたが、継承とするのか、プロパティで区別するのか、迷うケースは意外にあるものです。しかし、多くの場合最初に思いついた方、あるいは、考えたくないのでプロパティだらけになってオブジェクト指向的ではない設計になってしまうことも多々あります。

どうか、これを考えることから逃げないで、より合理性の高い解を見つける労を惜しまないようにお願いしたいと思うのです。それは、頭でっかちな理想論を追求することではなく、現実的な解を求めるという行為です。
できれば、いろいろな考え方に気づくため、人と議論をすることをお勧めします。この議論こそが、オブジェクト指向のメリットを活かす力のために必要と考えていますし、醍醐味の1つでもあると感じるのです。

オブジェクト指向を「やっている」開発現場では、この議論が活発に行われているはずです。多くの解から、何故それを選ぶのか。いつも、他の可能性があることを知りながら、最初に思いついたものを解としているようでは前に進むこと(上達すること)はありません。

0 件のコメント:

コメントを投稿