2011-06-29 6 views
8

Customerクラスがあり、インスタンスがLoad()のクラスがあるとします。依存性注入と静的メソッドの併用

Load()メソッドが呼び出されると、メソッドは、たとえば次のように注文の詳細を取得します。

var orders = Order.GetAll(customerId, ...); 

GetAll()Orderクラスの静的メソッドであり、入力パラメータはCustomerクラスで定義されたフィールドです。

あなたが見ての通り、はCustomerクラスの依存関係ですが、IOrderを作成して静的メソッドを持つことができないので、そこに注入することはできません。

したがって、この例では依存性注入をどのように導入できますか?

GetAll()は静的メソッドであり、そのように保つ必要があるため、インスタンスメソッドにしたくありません。

たとえば、設計でユーティリティクラスを使用しましたが、そのほとんどは静的メソッドを含んでいます。

答えて

9

の静的メソッドを保持する必要がある場合は、静的呼び出しをリポジトリオブジェクトにラップします。

interface IOrderRepository { 
    IEnumerable<IOrder> GetAll(customerId, ..); 
} 

class OrderRepository : IOrderRepository { 
    IEnumerable<IOrder> GetAll(customerId, ...) 
    { 
    Order.GetAll(customerId,...); // The original static call. 
    } 
} 

今、あなたはあなたのCustomerクラスにこのリポジトリを注入:このように

(私はあなたがテストの目的のために、実行時に偽のIOrdersを注入することができますので、あなたがこれをやっていると仮定しています。私は、一般的には、静的メソッドがテストに重大な障害であると言うべきです。)

+0

これらのリポジトリクラスとインターフェイスはデータアクセスレイヤの一部ですか? My Orderクラスは、Orderテーブル(LINQ to SQLを使用)からマップされたビジネスエンティティです。 –

+0

@ user133212それは、あなたの既存の関数のラッパーです。その関数が論理的にレイヤーに属するどこにでも、私はラッパーも置くでしょう。 –

3

あなたとして見て注文を取得するための集約ルートはあなたの顧客モデルですを強くお勧めします。顧客リポジトリを作成し、それを必要とするサービスに注入することをお勧めします。

ここ

は一例であり:

public class CustomerService 
{ 
    private readonly ICustomerRepository _customerRepository; 

    public CustomerService(ICustomerRepository customerRepository) 
    { 
     if (customerRepository == null) 
     { 
      throw new ArgumentNullException("customerRepository"); 
     } 

     _customerRepository = customerRepository; 
    } 

    public IEnumerable<IOrder> GetOrdersForCustomerId(int customerId) 
    { 
     return _customerRepository.GetOrdersForCustomerId(customerId); 
    } 
} 

public interface ICustomerRepository 
{ 
    IEnumerable<IOrder> GetOrdersForCustomerId(int customerId); 
} 

class CustomerRepository : ICustomerRepository 
{ 
    public IEnumerable<IOrder> GetOrdersForCustomerId(int customerId) 
    { 
     throw new NotImplementedException(); 
    } 
} 
0

関数ポインタ注射

TLDR:

Customer

クラスに関数ポインタを注入。この関数ポインタの値は、プロダクションではOrder.GetAll、テストではMockOrder.GetAllです。

例:

依存(私たちはに依存問題の静的関数):

class Order { 
    static func GetAll() -> [Order] { 
     var orders = ... // Load from production source 
     return orders 
    } 
} 

当社の依存クラス(は静的関数に依存します):

class Customer { 
    func Init(getAllOrdersFunction) { // Arg is a func pointer 
     self.getAllOrdersFunction = getAllOrdersFunction 
    } 

    func Load() { 
     var orders = self.getAllOrdersFunction() 
     // Do stuff... 
    } 
} 

生産クライアントクラス(は依存関係注入を実行します)上:

class BusinessLogicManager { 
    func DoBusinessLogic() { 
     var customer = Customer(Order.GetAll) // Prod func injected here 
     customer.Load() 
     // Do stuff... 
    } 
} 

テストクライアントクラス(ユニットテストは偽の依存関係を注入する方法):

class CustomerUnitTests { 
    static func GetFakeOrders() { 
     var orders = ... // Hardcoded test data 
     return orders 
    } 

    func TestLoad() { 
     var customer = Customer(CustomerUnitTests.GetFakeOrders) // Fake func injected here 
     customer.Load() 
     // Verify results given known behavior of GetFakeOrders 
    } 
} 

議論:

あなたが実際に "関数ポインタを" 注入方法でしょうあなたの言語で利用可能な構文と機能に依存します。ここで私は一般的な概念について話しています。

これはまったく良い解決策ではありません。 GetAllをインスタンスメソッド(おそらくOrdersLoaderオブジェクトを導入するか、またはPaul Phillipsの回答を使用して)に変更することができれば、おそらくもっと簡単でしょう。しかし、実際に静的関数として保持したい場合は、この解決法が有効です。