コールヒエラルキー
メソッドや関数がどのように呼び出されているかに関する階層構造
コールヒエラルキーを使うといい場面の例
影響範囲の把握
- 特定のメソッドが他にどのメソッドから呼び出されているかを知る
→変更を加える予定のメソッドが他のメソッドに影響を与えるかどうかを特定するのに使える
テスト対象の特定
- 変更される可能性のあるメソッドがどのテストケースに関連しているかを特定
→影響を受ける可能性のあるテストケースを重点的に検証可能
リファクタリングのサポート
- リファクタリングを行う際に、変更したいメソッドが他の部分にどのように影響を及ぼすかわかる
→リファクタリングが正確で安全に行える
デバッグとトラブルシューティング
- 特定のメソッドがどのように呼び出され、データや処理がどのように伝播しているかわかる
→問題の原因を特定しやすくなる
パフォーマンス最適化
- コールヒエラルキーを分析することで、パフォーマンスボトルネックを特定できる
パフォーマンス最適化の例
- ウェブアプリケーションのリクエスト処理が多数のコントローラーから共通のサービスメソッドを呼び出す場合
→サービスメソッドのパフォーマンスを最適化することで全体の処理速度を向上できる
コールヒエラルキーを避けた方が良い場面の例
過度な依存
- 過度な依存を持つコードは保守性が弱く、バグの発生リスクが高まる
過度な依存の例
- 以下のような循環的な依存関係がある場合
クラスAがクラスBを呼び出す
↓
クラスBがクラスCを呼び出す
↓
クラスCがクラスAを呼び出す
依存の逆転の原則(Dependency Inversion Principle, DIP)違反
- 高レベルのモジュールが低レベルのモジュールに直接依存している場合
→高レベルのモジュールが低レベルの実装の詳細に依存してしまうということ
高レベルのモジュールが低レベルのモジュールを直接呼び出すのではなく、抽象化されたインターフェースに依存すべき
依存の逆転の原則違反の例(データベースにアクセスするシステムの場合)
依存の逆転の原則に違反する設計
class DatabaseAccess {
// データベースにアクセスするコード
public void query() {
// データベースにクエリを送信する処理
}
}
class Service {
private DatabaseAccess databaseAccess;
public Service() {
this.databaseAccess = new DatabaseAccess();
}
public void doSomething() {
// データベースへのアクセスを使った処理
databaseAccess.query();
}
}
Serviceクラスが直接DatabaseAccessクラスをインスタンス化して使用している
→高レベルのServiceクラスは低レベルのDatabaseAccessクラスに依存している
DIPに従う設計
interface DataAccess {
void query();
}
// DataAccessはデータベースへのアクセスを抽象化するインターフェース
// queryメソッドが定義されているが、具体的な実装は以下のDatabaseAccessへ
class DatabaseAccess implements DataAccess {
// データベースにアクセスするコード
public void query() {
// データベースにクエリを送信する処理
}
}
class Service {
private DataAccess dataAccess;
public Service(DataAccess dataAccess) {
this.dataAccess = dataAccess;
}
public void doSomething() {
// データベースへのアクセスを使った処理
dataAccess.query();
}
}
// Serviceクラスはデータベースアクセスを行うクラス
// DatabaseAccessクラスに直接依存はしていないが、その代わりにDataAccessインターフェースに依存している
ServiceクラスのコンストラクタでDataAccessインターフェースの実装を受け取る
↓
doSomethingメソッド内では、具体的な実装を意識することなくDataAccessのqueryメソッドを呼び出すことができる