構造化プログラミング→オブジェクト指向の流れはなんとなく知っていたが、いまいちどういうものかイメージがついていなかった.
C言語での実装が直近多いため、それらのために今一度学び直しをしておこうかと考えていた.
詰まるところ
「処理の流れのみを構造化」したのが構造化プログラミングで、「データと処理の流れをまとめた」のがオブジェクト指向プログラミングの、乱暴な分類.
構造化プログラミングではデータに対して意識が向いていなかったため、あくまでどういう封に処理を分けるかと言ったことのみに注意していた.
もちろんオブジェクト指向でも制御の流れを構造化する箇所が必要なところはあるが、それよりもオブジェクトを中心に扱うというのを強く意識していることが大事.
これらの背景
構造化プログラミングやオブジェクト指向もなぜそんなことをわざわざ意識するのかといえば、学生やバイトで作る小さなアプリやWebプロジェクトでは大してメリットを感じられないが、
- 数万行のコードで構成される数百のファイルで作られるソフトウェア
- 1から実装ではなく、すでに誰かが作った後からそれを改造して開発する
ときに一番効くからだ.
私も正直学生のときは全くメリットを感じられず、ただの横文字製造を生業としている人たちの考え程度に思っていた. 会社でとんでもないモンスターコードに出会うまでは…
とかく自分が知らないソースコードを読むのはしんどいのに、さらにどうやって動くのかソースコードをコメントもなしに見ても想像つきにくい. 色々なところから呼び出されて思わぬところから変数が書き換えられる状況. とりあえずManager/マネージャーという名前がついた具体的に何をしてくれるのか全くわからないクラス. 一つのファイルで数万行になって読みにくさなんてものじゃない関数群. 乱発されてもはやGlobal変数と化したSingletonクラス. 一つのメンバークラスを調べたらなぜか100以上の外部クラスからの参照…
そんなところに仕様変更なんて入れるのは怖すぎる. 何がどう変わるかわからない.
ってことで、ちゃんと設計してちゃんと名前つけて、ちゃんと作りましょうって話.
構造化プログラミング時の注意点
ってことでここからが本題. 羅列してく. というか構造化プログラミングに限定されない話な気がするが.
名前をつける
フォルダ名 / ファイル名 / 関数名 / 変数名では、適切な処理の名前をつける. 「~を-する」の名前にして、明瞭にする. 名前から想像つかない処理はしない.
XXXManagerをつけるとその処理の単一責任がわからなくなるので忌避すべき
分割 / 凝集 / 集約したモジュール
システムの機能を適当な粒度に少しずつ分解して、関係のある処理を凝集して、同じような目的の似たような処理を集約。
この際に抽象化を行うが、”何をする処理(WHAT)”を意識して機能を整理.
必ず各モジュールは”単一責務”になるように、一つの仕事以外させない.
もし二つ以上のことをしている / 気にしているなら他のモジュールが必要.
上記によって”関心事の分離“が自ずとできていくはず. 横断的な関心ごとに関してはなかなか手を打てない(logやトランザクション管理?).
適切な階層分け
同じレベルの抽象化の階層を用意して、そこに適切なモジュールを配置.必ず抽象的なレベルから具体的なレベルを呼び出して、最後はドライバのレベルになるはず. モジュール同士は必ずどちらか一方が上になるべき. もしできていないなら、モジュール分割か、関数切り出しか上位モジュールによる調停が必要. “直進する”モジュール→”車輪に前への移動量をセットする”モジュール→”モータにドライバからセットする”モジュール. 上の方がより”目的”に近く、現実的なデバイスから遠い.
変数をカプセル化
色々なところからアクセスされるGlobal変数を用意したり、externしてしまう変数があると誰がいつ変更するかわからない. 使わないのならカプセル化/隠蔽してやる. 隠蔽されることで、他に勝手にいじられなくなるため、そのファイルの中でいじられることだけを意識して変更できるようになる.
設計作業
時間軸を整理する動的設計と時間に関係しないモジュールの関係を設計するのを静的設計とする. 多くの場合は静的設計を行ってから動的設計に当て込んでいく.時系列を表現しやすいシーケンス図と全体を整理するのに向いている通信図(コミュニケーションズ)をそれぞれ使用する.
ソフトウェア品質指標
凝集度 : モジュールの要素がどれくらい関連したものになっているか
機能的凝集度 / 逐次的 / 通信的 / 手順的 / 一時的 / 論理的 / 偶発的
前から3つはあり. 他は凝集度としては微妙
結合度 : モジュール間の結びつきの強さ. 低く独立しているとよい.
グローバル変数などによる依存がある : 共有結合 よくない
受け渡すデータだけで依存している : よい
データだけで依存していれば変更は伝播しない. 変更したい処理がモジュールの内側に収まってインターフェスが変わらないから.
設計方針列挙
- STS分割 : 入力 , 変換 , 出力の3つに分割して処理の流れを構成
- 変数はかならず一つの目的のために使われるべきで使いまわさないこと
- 段階的な設計