問題の歴史
Javaは最初からオブジェクト指向言語として設計されており、主な課題はクラスのインスタンスを作成することによって解決されました。しかし、よく使用されるメソッド(例えば、ソートやデータの変換)について、静的メソッドのセットを持つユーティリティクラス(例:java.util.Collections)が出現し始めました。
問題
静的メソッドは補助関数の呼び出しを簡略化しますが、状態を保持したり、依存性を注入したり、孤立したテストを行ったりするには適していません。一方で、クラスのインスタンスはより柔軟ですが、初期化に必要なコード量が増え、慎重なライフサイクル管理が必要になります。
解決策
ユーティリティクラス — 状態を持たない静的メソッドのセットであり、オブジェクトを生成する必要がありません。コレクションの操作、変換、数学的操作に適しています。
クラスのインスタンス — 状態を保持し、依存性を使用し、拡張性とテスト可能性を提供します。ビジネスロジック、サービス、コントローラーなどに使用されます。
コード例:
// ユーティリティクラスの例 public class MathUtils { public static int add(int a, int b) { return a + b; } } // 使用法: int sum = MathUtils.add(1, 2); // ビジネスロジックのためのクラスインスタンスの例 public class OrderService { private final OrderRepository repo; public OrderService(OrderRepository repo) { this.repo = repo; } public void placeOrder(Order o) { repo.save(o); } }
主な特徴:
ユーティリティクラスを継承して静的メソッドを拡張できますか?
いいえ、通常、ユーティリティクラスはfinalとして宣言され、プライベートコンストラクタを持ちます。静的メソッドの継承は可能ですが、実際には意味がありません。なぜなら、静的メソッドはインスタンス呼び出しのレベルでは継承されないからです。
コード例:
public final class MyUtils { private MyUtils() {} // インスタンス生成の防止 }
ユーティリティクラスは状態を持つことができますか?
いいえ。ユーティリティクラスが状態(インスタンスまたはstaticフィールド)を持つと、ユーティリティの原則に違反し、マルチスレッドの問題を引き起こし、可読性が低下します。
テスト時にユーティリティクラスの静的メソッドをモックできますか?
特別なツール(PowerMockなど)を使用してのみ、テストがより複雑になり、時には不安定になります。通常のケースでは、DIフレンドリーなアプローチでインスタンスを使用する方がテストには好ましいです。
プロジェクトのすべてのサービスがユーティリティの静的メソッドを使用して実装されています。依存性の注入ができず、ユニットテストは孤立しておらず、各テストは環境の状態に依存しています。
利点:
欠点:
サービスでは依存性の注入を通じてインターフェースを介して個別のクラスを使用しています。変換や単純な操作には、状態を持たないユーティリティクラスを使用しています。
利点:
欠点: