非循環依存関係の原則(ADP)とは?
非循環依存関係の原則(ADP: Acyclic Dependencies Principle)は、「コンポーネントの依存関係に循環を含めてはならない」というソフトウェア設計の重要な原則です。
簡単に言うと、コンポーネント(モジュールやパッケージ)の依存関係が互いにループする形になってはいけないというルールです。
循環依存が発生すると、コードの変更やビルド時にさまざまな問題を引き起こします。
考えが生まれた背景
ソフトウェア開発では、以下のような課題が頻繁に発生していました:
循環依存によるビルドの失敗
- AモジュールがBモジュールに依存し、BモジュールがAモジュールに依存している場合、依存関係が循環しているため、コンパイルやビルドが正常に進まないことがあります。
修正の影響が広がる
- 循環依存があると、1つのモジュールを変更した際、その変更が依存関係をたどって他のモジュールにも影響を与えます。修正箇所が拡大することで、テストやデバッグの手間も増加します。
複雑な依存関係が管理を難しくする
- 依存関係が循環すると、どの部分が原因で問題が発生しているのかを追跡するのが難しくなります。これにより、保守性が低下します。
解決策
週次ビルドの導入
- 定期的にプロジェクト全体をビルドすることで、循環依存の問題を早期に発見できます。
- 小規模な変更でも全体に影響を与えないか確認する仕組みを構築することが重要です。
循環依存の除去
- インターフェースを導入する
- 互いに直接依存しているモジュール間に抽象的なインターフェースを設け、依存の方向を一方向に統制します。
- 依存性逆転の原則(DIP)の適用
- 高水準モジュールが低水準モジュールに依存しないように設計します。
- これにより、モジュール間の依存を柔軟にコントロールできます。
DIPによる循環依存の解決
DIP(依存性逆転の原則)を用いると、循環依存を以下のように解決できます。

DIPとは?依存性逆転の原則を簡単解説
依存性逆転の原則(DIP)の基本を初心者向けに解説!背景、具体例、メリット、課題、実践的な対策をわかりやすく説明します。
適用前:直接依存による循環
登場するモジュール(クラス)
- 注文管理(OrderManager)
→ 注文を管理する機能。 - 在庫管理(InventoryManager)
→ 在庫を管理する機能。 - 請求管理(BillingManager)
→ 料金請求を管理する機能。
Java
// 注文管理クラス
class OrderManager {
private InventoryManager inventoryManager;
private BillingManager billingManager;
public OrderManager(InventoryManager inventoryManager, BillingManager billingManager) {
this.inventoryManager = inventoryManager;
this.billingManager = billingManager;
}
public void processOrder(String bookId) {
System.out.println("Processing order for book: " + bookId);
inventoryManager.updateStock(bookId); // 在庫を更新
billingManager.generateInvoice(bookId); // 請求書を生成
}
}
// 在庫管理クラス
class InventoryManager {
private OrderManager orderManager; // 循環依存
public InventoryManager(OrderManager orderManager) {
this.orderManager = orderManager;
}
public void updateStock(String bookId) {
System.out.println("Updating stock for book: " + bookId);
orderManager.processOrder(bookId); // 注文処理を呼び出す
}
}
// 請求管理クラス
class BillingManager {
public void generateInvoice(String bookId) {
System.out.println("Generating invoice for book: " + bookId);
}
}
問題点
- 循環依存が発生:
OrderManager
がInventoryManager
に依存し、さらにInventoryManager
がOrderManager
に依存しています。- これにより、どちらを先に初期化すべきかが曖昧になります(「卵が先か鶏が先か」状態)。
- 変更が困難:
- 例えば、
OrderManager
のロジックを変更すると、それに依存しているInventoryManager
にも影響が及びます。
- 例えば、
- ビルドやテストが難しい:
- 循環依存により、単体でのテストができず、複数クラスを同時にテストする必要が出てきます。
適用後:インターフェースを使用
Java
// 共通インターフェース
interface InventoryService {
void updateStock(String bookId);
}
// 注文管理クラス(OrderManager)
class OrderManager {
private InventoryService inventoryService;
private BillingManager billingManager;
public OrderManager(InventoryService inventoryService, BillingManager billingManager) {
this.inventoryService = inventoryService;
this.billingManager = billingManager;
}
public void processOrder(String bookId) {
System.out.println("Processing order for book: " + bookId);
inventoryService.updateStock(bookId); // 在庫を更新
billingManager.generateInvoice(bookId); // 請求書を生成
}
}
// 在庫管理クラス(InventoryManager)
class InventoryManager implements InventoryService {
public void updateStock(String bookId) {
System.out.println("Updating stock for book: " + bookId);
}
}
// 請求管理クラス
class BillingManager {
public void generateInvoice(String bookId) {
System.out.println("Generating invoice for book: " + bookId);
}
}
やったこと
- インターフェース(InventoryService)の導入:
OrderManager
は具体的なInventoryManager
に依存するのではなく、抽象化されたInventoryService
に依存しています。- これにより、
OrderManager
とInventoryManager
の直接的な依存関係がなくなりました。
- 循環依存が解消:
OrderManager
とInventoryManager
は直接的に参照し合わないため、循環が発生しません。
- 柔軟性が向上:
InventoryService
の実装を他のクラスに変更することも簡単です(例えば、クラウド在庫システムに切り替える場合)。
まとめ
非循環依存関係の原則(ADP)は、コンポーネント間の依存関係をシンプルに保ち、循環を排除することでソフトウェアの保守性やビルドの安定性を向上させる重要な原則です。
- メリット: 保守性向上、ビルドの安定化、変更の影響を最小化。
- 解決策: インターフェースの導入やDIPの適用、週次ビルドの活用。
- ポイント: モジュール間の依存を明確化し、柔軟な設計を目指す。
ADPを守ることで、安定したソフトウェアを長期的に運用できるシステムを実現できます!
コメント