この記事の概要
現場での設計の留意点について考察し、その設計手法の一つであるオブジェクト指向について深堀していきます。出てくるキーワードを以下に階層化して示しています。
・コスト
・イニシャルコスト
・ランニングコスト
・設計
・構造化設計
・オブジェクト指向
・カプセル化
・データ隠蔽
・抽象化
・情報隠蔽
・実装
・ポリモフィズム(多様性)
・情報隠蔽を意識した具体的な実装
コストについて
コストについて、以下の2つがあります。
・イニシャルコスト(初期開発コスト)
・ランニングコスト(運用コスト)
これらのコストは、ほとんどの場合トレードオフの関係です。
イニシャルコストを多く使い、ランニングコストを低くするか、
イニシャルコストを抑えて、そこそこのランニングコストで運用するか
このバランスを考え、提案するのがよいと思います。
設計について
■設計とは
・分割と結合(分割したものの関連性を定義する活動)
何に着目し分割するのか?これにより関連性の複雑性が変わってきます。
関係性がシンプルだと仕様変更に強い設計になります。
■コストとの関連性
仕様変更が多発する要件がある場合、仕様変更に強い設計とするメリットが大きいかもしません。
「初期費用+運転コスト(仕様変更、機能追加を含む)×年数」
で案を比較するのがよいですかね。この時、どのような仕様変更、機能追加が想定されるかがポイントになるかと思います。
■代表的な設計方法とその特徴。
設計方法 | 特徴 |
構造化設計 | ・処理に着目した設計(簡単) ・ユーザから見える部分、UI、画面から考え、あとで内部処理を検討 ・画面から考えるため、開発者にもイメージしやすい ・開発が簡単なので、イニシャルコストは低い ・画面と内部の処理、データの結合度が高い ・仕様変更時に、フロント(画面)~内部処理~データアクセスのすべてに改修が入る |
オブジェクト指向 | ・データに着目した設計(難しい)o ・データ構造に着目し、画面の仕様変更に依存しにくい設計 ・できるだけ独立したコンポーネント群を定義する ・仕様変更の影響範囲の特定が簡単 ・仕様変更の影響範囲が狭い |
オブジェクト指向(カプセル化)
■カプセル化のゴール
そのデータを他で使うことができない仕組みとする
仮に自分のカプセル以外からそのデータにアクセスできないなら、そのデータに変更が
入ってもそれにともなう影響範囲はカプセル内になります。
これが、外部からも自由にアクセスできると、その対象を洗い出すだけで膨大な作業となります。
【考察】
クラスのFieldをprivateにするだけで、そのgetter、setterがpublicで外部に公開されていて
誰でも自由に使えるという状況では、影響範囲の特定が難しそうですね。
オブジェクト指向(データ隠蔽)
データ隠蔽の目的はカプセル化の維持です。javaの実装でいうとFieldをpravateにするだけです。
これで、外から直接データにアクセスできなくなります。
オブジェクト指向(抽象化)
■ソフトウェアの設計とは
抽象化と再結合の作業といえます
■抽象化の目的
共通部分を抜き出して抽象化することで、以下のようになります。
「テニス選手」、「野球選手」、「サッカー選手」を「スポーツ選手」へ抽象化
3つあった項目を1つに集約できています。
このように集約することで、項目数を減らし、集約した者同士だけで関連性をもたせると
項目と項目の結合パスが少なくなり、物事をシンプルに考えることができます。
オブジェクト指向(情報隠蔽)
■情報隠蔽
データ隠蔽とは別物です。
部品構造の隠蔽で例えば以下のような感じです。
・あるパッケージに複数のクラスが存在し、その中の一部のクラスが公開クラス
・あるクラスの一部のフィールド、メソッドのみが公開されている
公開する部分としない部分を設計する必要があります。
■目的
なぜ、このようなことをするのか?
全部公開すると直接アクセスしてはいけないデータにアクセスできてしまいその結果保守性が損なわれます。すなわち、あるデータ部分を変更した時にどこからアクセスされているのか分からない状態になります。これを構造的に回避するために制約を設けて直接アクセスすることを不可能とし当初の設計思想を守ります。
実装(ポリモフィズム)
以下の絵のように、「扱い方」という部分で使用するためのIFを準備すると
実装と使用者を切り離すことができます。この「扱い方」については以下に留意する
・可読性
・誤った使用がされない制御
情報隠蔽を意識した具体的な実装
【前提】
アクセス修飾子なしの「デフォルト」の動作は同一パッケージ内まで公開です。
これがJavaの設計思想です。IDEで開発しているとpublicがつきますが、そのまま放置はまずいので、本当にpublicにする必要があるか検討しましょう。
以下にアクセス修飾子について公開範囲をまとめています。
public | protected | デフォルト | private | |
同一クラス | 〇 | 〇 | 〇 | 〇 |
同一パッケージ | 〇 | 〇 | 〇 | × |
サブクラス | 〇 | 〇 | × | × |
無関係 | 〇 | × | × | × |
■実装概要
クラス名 | パッケージ | アクセス修飾子 | 内容 |
A | com.sample | デフォルト | 実装クラス |
B | com.sample | デフォルト | 実装クラス |
S | com.sample | public | 公開インターフェース |
Main | デフォルト | public | 呼び出し元 |
Mainからの呼び出し部分において、実装クラスのA、Bについての記述が存在しません。
これはMainに対して、A、Bの実装クラスを隠蔽できていることになります。
この動作を切り替えるには、Sインターフェースの実装を変更することで、Mainには影響なく
変更することが可能です。
■Mainクラスの説明
・com.sampleパッケージをインポート
・Java8以降の記載ですが、インターフェースにstaticメソッドが実装されているので、
このような呼び出し方ができます。
■Sインターフェースの説明
・test()の抽象メソッドを実装している
・staticメソッドでBクラスの参照を返却
※戻り値でA、Bのインスタンスの参照を切り替えることで、
Mainには隠蔽したまま動作を切り替えることができる。
■B実装クラスの説明
・Sインターフェースの実装