2011-08-26 25 views
8

Theo nguyên tắc SOLID, một lớp không thể phụ thuộc vào các lớp khác, phụ thuộc phải được tiêm. Rất đơn giản:Đảo ngược phụ thuộc. Để tạo đối tượng

class Foo 
{ 
    public Foo(IBar bar) 
    { 
     this.bar = bar; 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 

class Bar: IBar 
{ 
} 

Nhưng nếu tôi muốn lớp Foo của mình có thể tạo Bar, không biết việc triển khai chính xác đằng sau IBar thì sao? tôi có thể nghĩ ra 4 giải pháp ở đây, nhưng tất cả chúng dường như có nhiều nhược điểm:

  1. tiêm loại đối tượng và sử dụng phản ánh
  2. sử dụng Generics
  3. sử dụng "Dịch vụ Locator" và gọi Resolve () phương pháp.
  4. tạo ra một lớp nhà máy tách ra và tiêm nó vào Foo:

class Foo 
{ 
    public void DoSmth(IBarCreator barCreator) 
    { 
     var newBar = barCreator.CreateBar(); 
    } 
} 

interface IBarCreator 
{ 
    IBar CreateBar(); 
} 

class BarCreator : IBarCreator 
{ 
    public IBar CreateBar() 
    { 
     return new Bar(); 
    } 
} 

trường hợp cuối có vẻ tự nhiên, nhưng BarCreator lớp có mã quá litle. Vậy bạn nghĩ thế nào, tốt nhất là gì?

+1

Tùy chọn 4 là câu trả lời đúng: http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container/1945023#1945023 –

+1

Tuy nhiên, tại sao bạn muốn Foo tạo IBar? Hãy chú ý đến các sự trừu tượng bị rò rỉ. –

Trả lời

0

Tôi cảm thấy khi bạn nói "Tôi muốn lớp Foo của tôi có thể tạo ra" một quy tắc nữa của Bar đến chơi đó là "Tách mối quan tâm". Vì vậy, bạn nên ủy nhiệm nhiệm vụ tạo lớp cho một thứ khác và Foo không nên lo lắng về nhiệm vụ đó.

2

tất cả phụ thuộc vào kịch bản chính xác và nhu cầu của bạn.
Tôi nghĩ cách tiếp cận được sử dụng nhiều nhất là, như bạn đã đề cập, một factory.
Nếu bạn đang sử dụng một khung công tác IoC (chẳng hạn như Ninject, hoặc Sprint.Net, Castle Windsor, vv, xem here), một bộ định vị dịch vụ cũng là một giải pháp khả thi.

3

Tôi muốn "tiêm" Func<IBar> trong trường hợp này. Giống như vậy:

class Foo 
{ 
    public Foo(Func<IBar> barCreator) 
    { 
     this.bar = barCreator(); 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 
2

Nếu bạn gặp vấn đề với giao diện nhà máy dư thừa, bạn có thể thực hiện hai cách tiếp cận tại đây.

Làm cho nó tái sử dụng với Generics:

interface IFactory<T> 
{ 
T Create(); 
} 

class DefaultConstructorFactory<T> : IFactory<T>, where T: new() 
{ 
public T Create() { return new T();} 
} 

Hoặc sử dụng chức năng ẩn danh như một nhà máy:

public void DoSomething(Func<IBar> barCreator) 
{ 
var newBar = barCreator(); 
//... 
} 
+0

Điều này không thực sự hoạt động được không? Bạn không thể chuyển một 'DefaultConstructorFactory ' làm một 'IFactory ' – fearofawhackplanet

3

Đây là những gì các nhà máy đã được thực hiện cho.

Nếu bạn cảm thấy nhà máy của bạn có quá ít mã, hãy tự hỏi mình sẽ đem lại lợi ích gì cho việc tạo ra cá thể nội tuyến. Nếu lợi ích vượt quá chi phí của mã được thêm vào, thì đừng lo lắng về nó.

Cá nhân tôi muốn tránh vị trí dịch vụ hoặc nếu bạn thực sự phải sử dụng, tôi sẽ ẩn nó sau nhà máy. Vị trí dịch vụ có xu hướng dễ bị lạm dụng và có thể dẫn đến vùng chứa của bạn tìm đường vào mã nên không cần phải làm gì cả.

Để thuận tiện, một số vùng chứa cho phép bạn chỉ định nhà máy sẽ được vùng chứa sử dụng khi tạo một phiên bản của một thành phần. Trong trường hợp này, lớp học của bạn có thể phụ thuộc trực tiếp vào số IBar nhưng vùng chứa của bạn sẽ gọi số IBarCreator khi cần một phiên bản mới.Castle Windsor có các phương thức UseFactoryUseFactoryMethod trong API của chúng.