Tại sao tôi cần một bộ chứa IoC trái ngược với mã DI đơn giản? [đóng cửa]


598

Tôi đã sử dụng Dependency Injection (DI) trong một thời gian, tiêm vào trong hàm tạo, thuộc tính hoặc phương thức. Tôi chưa bao giờ cảm thấy cần phải sử dụng bộ chứa Inversion of Control (IoC). Tuy nhiên, càng đọc, tôi càng cảm thấy áp lực từ cộng đồng khi sử dụng bộ chứa IoC.

Tôi chơi với container NET như StructureMap , Ninject , Unity , và Funq . Tôi vẫn không thấy làm thế nào một container IoC sẽ mang lại lợi ích / cải thiện mã của tôi.

Tôi cũng sợ bắt đầu sử dụng một container tại nơi làm việc vì nhiều đồng nghiệp của tôi sẽ thấy mã mà họ không hiểu. Nhiều người trong số họ có thể miễn cưỡng học công nghệ mới.

Xin hãy thuyết phục tôi rằng tôi cần sử dụng một container IoC. Tôi sẽ sử dụng những lập luận này khi tôi nói chuyện với các nhà phát triển đồng nghiệp của mình tại nơi làm việc.


6
Câu hỏi hay. Tôi đang trả lời điều này trong các ý kiến ​​vì tôi rất mới với ý tưởng của IOC. Nó trông giống như ý tưởng về các thành phần cắm và chơi và khớp nối lỏng lẻo. Cho dù bạn sẽ có nhu cầu sử dụng một số thành phần khác thay cho thành phần hiện tại, là cần thiết. Sử dụng IOC là để chuẩn bị mã cho một thay đổi như vậy, nếu nó phát sinh IMO.
shahkalpesh

3
@shahkalpesh nhưng tôi có thể đạt được khớp nối lỏng lẻo với DI đơn giản. Tuy nhiên, tôi thấy quan điểm của bạn trong trường hợp tôi sử dụng tệp cấu hình. Tuy nhiên, tôi không chắc mình có thích sử dụng tập tin cấu hình không. Các tệp cấu hình rất dài dòng, khó khăn trong việc tái cấu trúc và chuyển đổi giữa một số tệp.
Vadim

110
chỉ cần thêm một nhận xét vì có vẻ như bạn đang tìm kiếm lý do để sử dụng IoC là chủ yếu; nhưng một cái gì đó khác cần phải nói về vấn đề của bạn. Bạn không thể viết mã của mình đến cấp độ của người xấu hơn trong nhóm của bạn hoặc vì sợ rằng họ sẽ không muốn học. Bạn có trách nhiệm không chỉ là một chuyên gia mà còn cho tương lai của bạn để phát triển và nhóm của bạn không nên giữ bạn lại.
Kevin Sheffield

4
@Kevin, Thật ra chúng tôi đang sử dụng IoC. Không khó như mọi người nghĩ để dạy mọi người về IoC.
Vadim

21
Tôi không biết tại sao người điều hành lại ồ ạt đưa ra những câu hỏi hữu ích. Có lẽ trong trường hợp này, Will sẽ không hiểu câu hỏi, hoặc anh ta không hiểu mọi người sử dụng stackexchange để làm gì? Nếu nó "không phù hợp với định dạng Hỏi & Đáp của stackexchange" thì có lẽ nên xem xét rằng chính sách của bạn là sai và không phải tất cả hàng trăm câu hỏi hữu ích này với hàng trăm câu trả lời được bình chọn và trả lời hữu ích.
NickG

Câu trả lời:


441

Wow, không thể tin rằng Joel sẽ ủng hộ điều này:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

về điều này:

var svc = IoC.Resolve<IShippingService>();

Nhiều người không nhận ra rằng chuỗi phụ thuộc của bạn có thể được lồng vào nhau và nó nhanh chóng trở nên khó sử dụng để kết nối chúng theo cách thủ công. Ngay cả với các nhà máy, việc sao chép mã của bạn cũng không đáng.

IoC container có thể phức tạp, vâng. Nhưng đối với trường hợp đơn giản này, tôi đã cho thấy nó cực kỳ dễ dàng.


Được rồi, hãy biện minh cho điều này nhiều hơn nữa. Giả sử bạn có một số thực thể hoặc đối tượng mô hình mà bạn muốn liên kết với giao diện người dùng thông minh. Giao diện người dùng thông minh này (chúng tôi sẽ gọi nó là Shindows Morms) muốn bạn triển khai INotifyPropertyChanged để nó có thể thay đổi theo dõi và cập nhật giao diện người dùng phù hợp.

"OK, điều đó không có vẻ quá khó" vì vậy bạn bắt đầu viết.

Bạn bắt đầu với điều này:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..và kết thúc với điều này :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Đó là mã hệ thống ống nước kinh tởm, và tôi bảo đảm rằng nếu bạn viết mã như thế bằng tay thì bạn đang đánh cắp từ khách hàng của bạn . Có cách làm việc tốt hơn, thông minh hơn.

Bao giờ nghe thuật ngữ đó, làm việc thông minh hơn, không khó hơn?

Hãy tưởng tượng một số người thông minh trong nhóm của bạn đã đến và nói: "Đây là một cách dễ dàng hơn"

Nếu bạn thực hiện tài sản của bạn ảo (bình tĩnh xuống, nó không phải là lớn của một thỏa thuận), sau đó chúng ta có thể dệt ở chỗ hành vi sở hữu tự động. (Đây được gọi là AOP, nhưng đừng lo lắng về tên, hãy tập trung vào những gì nó sẽ làm cho bạn)

Tùy thuộc vào công cụ IoC nào bạn đang sử dụng, bạn có thể làm một cái gì đó trông như thế này:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Gặp sự cố! Tất cả các hướng dẫn sử dụng INotifyPropertyChanged BS hiện được tạo tự động cho bạn, trên mọi trình thiết lập thuộc tính ảo của đối tượng được đề cập.

Đây có phải là phép thuật không? ! Nếu bạn có thể tin tưởng vào thực tế rằng mã này thực hiện công việc của nó, thì bạn có thể bỏ qua một cách an toàn tất cả các thuộc tính đó gói mumbo-jumbo. Bạn đã có vấn đề kinh doanh để giải quyết.

Một số cách sử dụng thú vị khác của công cụ IoC để thực hiện AOP:

  • Giao dịch cơ sở dữ liệu khai báo & lồng nhau
  • Đơn vị khai báo & lồng nhau
  • Ghi nhật ký
  • Điều kiện trước / đăng (Thiết kế theo hợp đồng)

28
Rất tiếc, bạn đã quên làm cho tất cả các thuộc tính ảo và nó không hoạt động. Là thời gian bạn cần để gỡ lỗi ăn cắp này từ khách hàng của bạn quá? (Xin lỗi nếu bạn không sử dụng DynamicProxy .: P)
Thom

17
Bạn hy sinh RẤT NHIỀU bằng cách sử dụng trình bao bọc INotifyPropertyChanged. Nếu bạn mất hơn một vài phút để viết hệ thống ống nước đó để bắt đầu thì bạn đang kinh doanh sai. Ít hơn không phải là nhiều hơn khi nói đến tính dài dòng của mã của bạn!
overstood

39
@ hiểu - Tôi hoàn toàn không đồng ý. Đầu tiên, sự rõ ràng ở đâu trong ví dụ này? Gần người tiêu dùng nhất. Người tiêu dùng rõ ràng đang yêu cầu INotifyPropertyChanged. Chỉ vì chúng ta có thể tạo loại mã này bằng cách tạo mã vi mô hoặc vĩ mô, không có nghĩa là nên viết mã này. Gunk INotifyPropertyChanged đó chỉ là vẹt, trần tục, hệ thống ống nước và thực sự làm mất khả năng đọc của lớp trong câu hỏi.
Ben Scheirman

31
Đã đánh dấu xuống vì bạn đang nhầm lẫn giữa mẫu Tiêm phụ thuộc và mẫu Định vị dịch vụ. Cái trước có nghĩa là được sử dụng thay vì cái sau. Ví dụ: một đối tượng (hoặc thực sự là toàn bộ hệ thống) sẽ không bao giờ gọi Resolve (...) để lấy phiên bản của IShippingService. Các đối tượng cần IShippingService sẽ nhận được một tham chiếu đến thể hiện mà chúng nên sử dụng khi chúng được xây dựng và một số lớp cao hơn chịu trách nhiệm kết nối hai đối tượng lại với nhau.
Nat

30
Dù đó là gì (IoC, DynamicProxy, AOP hoặc ma thuật đen ) ai đó có thể liên kết tôi với một bài viết cụ thể / tài liệu cụ thể trong khung cung cấp thêm chi tiết để đạt được điều này?
fostandy

138

Tôi đi với bạn, Vadim. Các container IoC có một khái niệm đơn giản, thanh lịch và hữu ích, và biến nó thành thứ gì đó bạn phải học trong hai ngày với một hướng dẫn 200 trang.

Cá nhân tôi cảm thấy bối rối khi cộng đồng IoC đã lấy một bài báo đẹp, thanh lịch của Martin Fowler và biến nó thành một loạt các khung phức tạp thường với hướng dẫn sử dụng 200-300 trang.

Tôi cố gắng không phán xét (HAHA!), Nhưng tôi nghĩ rằng những người sử dụng bộ chứa IoC là (A) rất thông minh và (B) thiếu sự đồng cảm với những người không thông minh như họ. Mọi thứ đều có ý nghĩa hoàn hảo với họ, vì vậy họ gặp khó khăn khi hiểu rằng nhiều lập trình viên bình thường sẽ tìm thấy các khái niệm khó hiểu. Đó là lời nguyền của kiến ​​thức . Những người hiểu về container IoC gặp khó khăn khi tin rằng có những người không hiểu nó.

Lợi ích có giá trị nhất của việc sử dụng bộ chứa IoC là bạn có thể có một công tắc cấu hình ở một nơi cho phép bạn thay đổi giữa chế độ thử nghiệm và chế độ sản xuất. Ví dụ: giả sử bạn có hai phiên bản của các lớp truy cập cơ sở dữ liệu của mình ... một phiên bản đã đăng nhập mạnh mẽ và đã xác thực rất nhiều, mà bạn đã sử dụng trong quá trình phát triển và một phiên bản khác mà không đăng nhập hoặc xác nhận nhanh chóng để sản xuất. Thật tuyệt khi có thể chuyển đổi giữa chúng ở một nơi. Mặt khác, đây là một vấn đề khá nhỏ dễ dàng xử lý một cách đơn giản hơn mà không có sự phức tạp của các container IoC.

Tôi tin rằng nếu bạn sử dụng các bộ chứa IoC, mã của bạn sẽ trở nên khó đọc hơn rất nhiều. Số lượng địa điểm bạn phải xem để tìm ra những gì mã đang cố gắng tăng lên ít nhất một. Và đâu đó trên thiên đường một thiên thần khóc thét.


81
Tôi nghĩ rằng bạn có sự thật của bạn trộn lẫn một chút Joel. IoC và container xuất hiện đầu tiên, sau đó là chuyên luận của Martin chứ không phải ngược lại.
Khối Glenn

55
eesh hầu hết các container cho phép bạn đi rất nhanh. Với autofac, bản đồ cấu trúc, thống nhất, ninject, v.v. bạn sẽ có thể để container hoạt động trong khoảng 5 phút. Vâng, họ có các tính năng nâng cao nhưng bạn không cần những thứ đó để khởi đầu.
Khối Glenn

58
Joel tôi đã từng nghĩ giống như bạn. Sau đó, tôi thực sự dành thời gian và nghiêm túc dành năm phút để tìm ra cách chạy thiết lập cơ bản nhất và rất ngạc nhiên về quy tắc 80/20 trong hành động. Nó làm cho mã sạch hơn nhiều đặc biệt là trong các trường hợp đơn giản.
Justin Bozonier

55
Tôi đã lập trình được chưa đầy hai năm và tôi đã đọc wiki Ninject và đã nhận được nó. Tôi đã sử dụng DI ở rất nhiều nơi kể từ đó. Bạn có thấy điều đó không? Chưa đầy hai năm và đọc một vài trang trên wiki. Tôi không phải là phù thủy hay gì cả. Tôi không coi mình là một thiên tài, nhưng thật dễ hiểu cho tôi. Tôi không thể tưởng tượng ai đó thực sự hiểu OO không thể nắm bắt được nó. Ngoài ra, những gì hướng dẫn 200-300 trang bạn đang đề cập đến? Tôi chưa bao giờ nhìn thấy chúng. Tất cả các container IoC tôi đã sử dụng đều khá ngắn và đi vào vấn đề.
JR Garcia

35
+1 cũng được viết và nghĩ ra. Một điều tôi nghĩ rằng nhiều người không nhận ra là việc thêm vào một khung hoặc như vậy để "xử lý một cách kỳ diệu" một cái gì đó tự động làm tăng thêm sự phức tạp và hạn chế cho một hệ thống. Đôi khi nó đáng giá, nhưng thường thì một cái gì đó bị bỏ qua và entropy được phát hành vào mã cố gắng khắc phục một vấn đề được thi hành bởi các giới hạn. Nó có thể tiết kiệm thời gian lên phía trước, nhưng mọi người phải nhận ra rằng có thể tốn 100 lần xuống dòng cố gắng khắc phục một số vấn đề mơ hồ chỉ một số ít người thực sự có đủ kiến ​​thức để giải quyết.
kemiller2002

37

Có lẽ không ai bắt bạn phải sử dụng khung chứa DI. Bạn đã sử dụng DI để tách lớp của bạn và cải thiện khả năng kiểm tra, vì vậy bạn sẽ nhận được nhiều lợi ích. Nói tóm lại, bạn thích sự đơn giản, nói chung là một điều tốt.

Nếu hệ thống của bạn đạt đến mức độ phức tạp trong đó DI thủ công trở thành một việc vặt (nghĩa là tăng cường bảo trì), hãy cân nhắc điều đó với đường cong học tập nhóm của khung container DI.

Nếu bạn cần kiểm soát nhiều hơn đối với quản lý trọn đời phụ thuộc (nghĩa là, nếu bạn cảm thấy cần phải thực hiện mẫu Singleton), hãy xem các thùng chứa DI.

Nếu bạn sử dụng bộ chứa DI, chỉ sử dụng các tính năng bạn cần. Bỏ qua tệp cấu hình XML và định cấu hình nó trong mã nếu điều đó là đủ. Dính vào tiêm xây dựng. Những điều cơ bản của Unity hoặc StructMap có thể được cô đọng lại thành một vài trang.

Có một bài đăng blog tuyệt vời của Mark Seemann về điều này: Khi nào nên sử dụng DI Container


Phản ứng rất tốt. Điều duy nhất tôi khác về quan điểm là liệu tiêm EVER có thể được coi là ít phức tạp hơn hay không.
wekempf

+1. Một trong những câu trả lời tốt nhất ở đây. Cảm ơn cũng cho các liên kết bài viết blog.
stakx - không còn đóng góp

1
+1 cho quan điểm cân bằng. Các container IoC không phải lúc nào cũng tốt và chúng không phải lúc nào cũng xấu. Nếu bạn không thấy nhu cầu, đừng sử dụng nó. Chỉ cần biết rằng công cụ là có nếu bạn thấy cần.
Phil

32

Theo tôi, lợi ích số một của IoC là khả năng tập trung hóa cấu hình của các phụ thuộc của bạn.

Nếu bạn hiện đang sử dụng tiêm Dependency, mã của bạn có thể trông như thế này

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Nếu bạn đã sử dụng một lớp IoC tĩnh, trái ngược với, IMHO các tệp cấu hình khó hiểu hơn, bạn có thể có một cái gì đó như thế này:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Sau đó, lớp IoC tĩnh của bạn sẽ trông như thế này, tôi đang sử dụng Unity tại đây.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

11
Ben, những gì bạn đang nói thực sự là Vị trí dịch vụ, không phải là Dependency Injection - có một sự khác biệt. Tôi luôn thích cái đầu tiên hơn cái sau. stevenharman.net/blog/archive/2009/09/11/ ích
stevenharman

23
Thay vì thực hiện IoC.Resolve <T> trong hàm tạo mặc định của bạn, bạn nên tận dụng khả năng của trình chứa IOC để xây dựng đối tượng cho bạn. Loại bỏ hàm tạo trống đó và để bộ chứa IOC xây dựng đối tượng cho bạn. var presenter = IoC.Resolve <CustomerPresenter> (); thì đấy! tất cả các phụ thuộc đã lên dây.
Ben Scheirman

12
Mặt trái của việc tập trung hóa loại cấu hình này là phi ngữ cảnh hóa kiến ​​thức. Joel chỉ ra vấn đề này khi nói, "mã trở nên, thật lòng mà nói, khó đọc hơn rất nhiều".
Scott Bellware

6
@sbellware, kiến ​​thức gì? Giao diện được thực hiện như một sự phụ thuộc nguyên thủy đóng vai trò như một hợp đồng và có đủ để truyền đạt mục đích & hành vi của nó không? Tôi cho rằng bạn có thể đề cập đến kiến ​​thức thu được từ việc chơi trò chơi trong việc thực hiện hợp đồng, trong trường hợp nào bạn cần theo dõi cấu hình phù hợp? Tôi dựa vào một máy tính (R # trong VS, IntelliJ, v.v.) vì điều đó rất tốt trong việc điều hướng các nút và cạnh của đồ thị; Tôi bảo lưu ngăn xếp bộ não của tôi cho bối cảnh của trang web công việc tôi hiện đang tập trung vào.
stevenharman

7
Steve, tôi biết văn bản .NET và đi bộ trong nhiều năm. Tôi cũng có sự quen thuộc gần gũi với các chế độ và hình thức khác, và tôi hiểu một cách trực quan rằng có sự đánh đổi thực sự. Tôi biết làm thế nào và khi nào thực hiện những sự đánh đổi đó, nhưng sự đa dạng trong cuộc sống công việc của tôi ít nhất cho phép tôi hiểu một cách hữu hình quan điểm của Joel. Thay vì giảng cho tôi về các công cụ và thủ thuật mà tôi đã sử dụng lâu hơn hầu hết các nhà văn hóa đơn ngữ .NET, hãy nói cho tôi biết những điều tôi không biết. Tôi bảo lưu bộ não của mình cho tư duy phê phán, đa dạng thay vì ứ đọng và chính thống alt.net.
Scott Bellware

32

IoC Container cũng tốt cho việc tải các phụ thuộc lớp lồng nhau sâu. Ví dụ: nếu bạn đã có đoạn mã sau bằng cách sử dụng Depedency Injection.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Nếu bạn đã tải tất cả các phụ thuộc này vào và bộ chứa IoC, bạn có thể Giải quyết Dịch vụ khách hàng và tất cả các phụ thuộc con sẽ tự động được giải quyết.

Ví dụ:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

Nhưng lớp IoC được gọi là sử dụng mô hình chống định vị dịch vụ. Should_container được truyền bằng cách sử dụng hàm tạo DI thay vì gọi tĩnh T Resolve <T> ()?
Michael Freidgeim

@bendewey Ví dụ của bạn ngụ ý rằng nó tự động chuyển các đối số cho các nhà xây dựng cho Dịch vụ khách hàng mới và Dịch vụ khách hàng mới. Đây có phải là cách làm việc này? Điều gì nếu bạn có các tham số bổ sung khác là tốt?
Brain2000

28

Tôi là một fan hâm mộ của lập trình khai báo (xem có bao nhiêu câu hỏi SQL tôi trả lời), nhưng các bộ chứa IoC mà tôi đã xem có vẻ quá phức tạp vì lợi ích của riêng họ.

... hoặc có lẽ các nhà phát triển container IoC không có khả năng viết tài liệu rõ ràng.

... Hoặc nếu không cả hai đều đúng ở mức độ này hay mức độ khác.

Tôi không nghĩ rằng khái niệm về một container IoC là xấu. Nhưng việc triển khai phải đủ mạnh mẽ (nghĩa là linh hoạt) đủ hữu ích trong nhiều ứng dụng, nhưng đơn giản và dễ hiểu.

Có lẽ là sáu trong số một nửa tá thứ khác. Một ứng dụng thực tế (không phải đồ chơi hay bản demo) chắc chắn sẽ phức tạp, chiếm nhiều trường hợp góc và ngoại lệ theo quy tắc. Hoặc bạn gói gọn sự phức tạp đó trong mã mệnh lệnh, hoặc khác trong mã khai báo. Nhưng bạn phải đại diện cho nó ở đâu đó.


5
Tôi thích quan sát của bạn về sự hiện diện không thể tránh khỏi của sự phức tạp. Sự khác biệt giữa nối dây phụ thuộc vào container và thủ công là khi sử dụng một container, bạn có thể tập trung vào từng thành phần riêng lẻ, và do đó quản lý sự phức tạp ngày càng tăng theo cách khá tuyến tính. Các hệ thống có hệ thống dây phụ thuộc thủ công khó phát triển hơn, vì hành động cấu hình một thành phần khó tách khỏi cấu hình của mọi thứ khác.
Nicholas Blumhardt

Tôi thích AspectJ. Nó không phải là Java, nhưng nó cũng không phải là XML. Nó loại IS Java, ở chỗ nó cảm thấy và trông giống như Java bằng cách trở thành một phần mở rộng của nó. Tôi muốn giữ các khía cạnh của tôi ra khỏi POJO của tôi và trong logic kinh doanh của tôi. Tôi gần như muốn giữ các IoC của mình. Có quá nhiều XML trong việc kết nối các công cụ, IMHO. Nếu tôi phải có tệp cấu hình, hãy để chúng là tập lệnh BeanShell. / Java, áp dụng các công việc .Net của bạn tại đây ->.
Chris K

2
Tôi chắc chắn hiểu câu trả lời của bạn, nhưng tôi nghĩ một phần lớn của vấn đề là rất nhiều khung IoC (và, thực sự, rất nhiều khung khác cho những thứ khác nữa) được mô tả và ghi lại cho những người khác đã rất quen thuộc với khái niệm cần thiết và thuật ngữ. Tôi đang tìm hiểu về rất nhiều thứ này khi vừa thừa hưởng mã của một lập trình viên khác. Tôi đang đấu tranh để hiểu lý do tại sao mã đang sử dụng IoC khi 'đồ thị đối tượng' khá nhỏ và không có phạm vi kiểm tra thực tế của mã. Là một lập trình viên SQL, có lẽ bạn có thể đánh giá cao hơn khi sử dụng IoC với ORM.
Kenny Evitt

1
@KennyEvitt, điểm tốt, có vẻ như còn sớm để sử dụng khung IoC nếu một ứng dụng đơn giản và thậm chí không bận tâm để có phạm vi kiểm tra tốt.
Bill Karwin

27

Sử dụng một container chủ yếu là về việc thay đổi từ một kiểu khởi tạo và cấu hình bắt buộc / kịch bản sang kiểu khai báo . Điều này có thể có một vài tác dụng có lợi khác nhau:

  • Giảm thói quen khởi động chương trình chính của Hairball .
  • Kích hoạt khả năng cấu hình lại thời gian triển khai khá sâu.
  • Làm cho phong cách tiêm phụ thuộc trở thành con đường ít kháng cự nhất cho công việc mới.

Tất nhiên, có thể có những khó khăn:

  • Mã yêu cầu quản lý khởi động / tắt máy / vòng đời phức tạp có thể không dễ dàng thích ứng với một container.
  • Bạn có thể sẽ phải điều hướng bất kỳ vấn đề cá nhân, quy trình và văn hóa nhóm nào - nhưng sau đó, đó là lý do tại sao bạn hỏi ...
  • Một số bộ công cụ đang nhanh chóng trở nên nặng nề, khuyến khích loại phụ thuộc sâu sắc mà nhiều container DI bắt đầu như một phản ứng dữ dội.

23

Tôi nghe có vẻ như bạn đã xây dựng bộ chứa IoC của riêng mình (sử dụng các mẫu khác nhau được mô tả bởi Martin Fowler) và đang hỏi tại sao việc triển khai của người khác tốt hơn của bạn.

Vì vậy, bạn có một loạt các mã đã hoạt động. Và đang tự hỏi tại sao bạn muốn thay thế nó bằng cách thực hiện của người khác.

Ưu điểm khi xem xét một container IoC của bên thứ ba

  • Bạn nhận được lỗi cố định miễn phí
  • Thiết kế thư viện có thể tốt hơn của bạn
  • Mọi người có thể đã quen thuộc với thư viện cụ thể
  • Thư viện có thể nhanh hơn thư viện của bạn
  • Nó có thể có một số tính năng bạn muốn bạn triển khai nhưng không bao giờ có thời gian (bạn có định vị dịch vụ không?)

Nhược điểm

  • Bạn nhận được lỗi giới thiệu, miễn phí :)
  • Thiết kế thư viện có thể tệ hơn của bạn
  • Bạn phải học một API mới
  • Quá nhiều tính năng bạn sẽ không bao giờ sử dụng
  • Việc gỡ lỗi mã mà bạn không viết thường khó hơn
  • Di chuyển từ một container IoC trước đó có thể rất tẻ nhạt

Vì vậy, cân nhắc ưu điểm của bạn chống lại khuyết điểm của bạn và đưa ra quyết định.


Tôi đồng ý với bạn Sam - DI là một loại IoC, vì vậy nếu người đăng ban đầu đang làm DI, thì họ đã làm IoC rồi nên câu hỏi thực sự là tại sao lại là của riêng bạn.
Jamie yêu

3
Tôi không đồng ý. IOC là một cách làm DI. Các bộ chứa IOC thực hiện DI bằng cách kiểm soát khởi tạo kiểu bằng cách sử dụng phản xạ thời gian chạy động để đáp ứng và thêm các phụ thuộc. OP đang đề nghị anh ta thực hiện DI tĩnh và đang suy nghĩ tại sao anh ta cần phải đánh đổi lợi ích của việc kiểm tra thời gian biên dịch cho các lợi ích thường liên quan đến phản xạ thời gian chạy động của IOC.
Maxm007

17

Tôi nghĩ rằng hầu hết giá trị của một IoC được thu thập bằng cách sử dụng DI. Vì bạn đã làm điều đó, phần còn lại của lợi ích là tăng dần.

Giá trị bạn nhận được sẽ phụ thuộc vào loại ứng dụng bạn đang làm việc:

  • Đối với nhiều người thuê, bộ chứa IoC có thể đảm nhiệm một số mã cơ sở hạ tầng để tải các tài nguyên máy khách khác nhau. Khi bạn cần một thành phần dành riêng cho máy khách, hãy sử dụng bộ chọn tùy chỉnh để xử lý logic và đừng lo lắng về nó từ mã máy khách của bạn. Bạn chắc chắn có thể tự xây dựng cái này nhưng đây là một ví dụ về cách IoC có thể giúp đỡ.

  • Với nhiều điểm mở rộng, IoC có thể được sử dụng để tải các thành phần từ cấu hình. Đây là một điều phổ biến để xây dựng nhưng các công cụ được cung cấp bởi container.

  • Nếu bạn muốn sử dụng AOP cho một số mối quan tâm xuyên suốt, IoC cung cấp các móc nối để chặn các yêu cầu phương thức. Điều này ít được thực hiện đặc biệt trên các dự án nhưng IoC làm cho nó dễ dàng hơn.

Tôi đã viết chức năng như thế này trước đây nhưng nếu tôi cần bất kỳ tính năng nào trong số này bây giờ, tôi muốn sử dụng một công cụ được xây dựng và thử nghiệm trước nếu nó phù hợp với kiến ​​trúc của tôi.

Như đã đề cập bởi những người khác, bạn cũng có thể định cấu hình tập trung các lớp bạn muốn sử dụng. Mặc dù điều này có thể là một điều tốt, nhưng nó đi kèm với chi phí sai lầm và biến chứng. Các thành phần cốt lõi cho hầu hết các ứng dụng không được thay thế nhiều vì vậy việc đánh đổi khó hơn một chút.

Tôi sử dụng bộ chứa IoC và đánh giá cao chức năng nhưng phải thừa nhận rằng tôi đã nhận thấy sự đánh đổi: Mã của tôi trở nên rõ ràng hơn ở cấp độ lớp và ít rõ ràng hơn ở cấp ứng dụng (tức là hiển thị luồng điều khiển).


16

Tôi là một người nghiện IOC đang hồi phục. Tôi thấy thật khó để biện minh cho việc sử dụng IOC cho DI trong hầu hết các trường hợp hiện nay. Các thùng chứa IOC hy sinh kiểm tra thời gian biên dịch và được cho là đổi lại cho bạn thiết lập "dễ dàng", quản lý trọn đời phức tạp và nhanh chóng phát hiện ra các phụ thuộc trong thời gian chạy. Tôi thấy việc mất thời gian biên dịch kiểm tra và dẫn đến phép thuật / ngoại lệ thời gian chạy, không đáng để đánh chuông và huýt sáo trong phần lớn các trường hợp. Trong các ứng dụng doanh nghiệp lớn, họ có thể làm cho rất khó để theo dõi những gì đang diễn ra.

Tôi không mua đối số tập trung vì bạn cũng có thể tập trung thiết lập tĩnh rất dễ dàng bằng cách sử dụng một nhà máy trừu tượng cho ứng dụng của bạn và trì hoãn việc tạo đối tượng cho nhà máy trừu tượng, ví dụ như thực hiện DI.

Tại sao không làm DI tĩnh không có ma thuật như thế này:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

Cấu hình bộ chứa của bạn là triển khai nhà máy trừu tượng của bạn, đăng ký của bạn là triển khai của các thành viên trừu tượng. Nếu bạn cần một phụ thuộc singleton mới, chỉ cần thêm một thuộc tính trừu tượng khác vào nhà máy trừu tượng. Nếu bạn cần một sự phụ thuộc thoáng qua, chỉ cần thêm một phương thức khác và tiêm nó dưới dạng Func <>.

Ưu điểm:

  • Tất cả các thiết lập và cấu hình tạo đối tượng được tập trung.
  • Cấu hình chỉ là mã
  • Biên dịch kiểm tra thời gian giúp dễ dàng duy trì vì bạn không thể quên cập nhật đăng ký của mình.
  • Không có phép thuật phản xạ thời gian chạy

Tôi khuyên những người hoài nghi hãy cho nó một dự án lĩnh vực xanh tiếp theo và thành thật tự hỏi mình tại điểm nào bạn cần container. Thật dễ dàng để tính đến một bộ chứa IOC sau này vì bạn chỉ cần thay thế một triển khai của nhà máy bằng mô-đun cấu hình IOC Container.


Điều này đối với tôi không giống như một cuộc tranh cãi với IoC, mà là một cuộc tranh luận chống lại các khung IoC có thể định cấu hình . Không phải các nhà máy của bạn ở đây chỉ sôi sục với các IoC mã hóa đơn giản ?
Mr.Mindor

Tôi nghĩ khi người đăng đề cập đến IoC, một khung công tác chứa IoC có ý nghĩa. Cảm ơn cho bài viết gốc này giúp thuyết phục tôi đi mà không có khuôn khổ. Vậy nếu nó có thể cấu hình trong các thử nghiệm và trong mã sản xuất thì lợi thế của khung "có thể cấu hình" là gì?
huggie

Từ hiểu biết của tôi về thuật ngữ, Inversion Of Control có nghĩa là tìm kiếm sự phụ thuộc vào thời gian chạy. Vì vậy, có, bạn có thể nói rằng nhà máy về cơ bản là một thùng chứa được mã hóa cứng hoặc tĩnh, nhưng nó không phải là một thùng chứa IOC. Các loại được giải quyết tại thời gian biên dịch. Đó là một đối số chống lại việc sử dụng các khung có thể định cấu hình sử dụng tra cứu từ điển để giải quyết các loại khi chạy.
Maxm007

14

Lợi ích lớn nhất của việc sử dụng các bộ chứa IoC đối với tôi (cá nhân, tôi sử dụng Ninject) là loại bỏ sự chuyển qua các cài đặt và các loại đối tượng trạng thái toàn cầu khác.

Tôi không lập trình cho web, của tôi là một ứng dụng bảng điều khiển và ở nhiều nơi sâu trong cây đối tượng tôi cần có quyền truy cập vào các cài đặt hoặc siêu dữ liệu được chỉ định bởi người dùng được tạo trên một nhánh hoàn toàn riêng biệt của cây đối tượng. Với IoC, tôi chỉ cần nói với Ninject coi Cài đặt là đơn lẻ (vì luôn chỉ có một phiên bản của chúng), yêu cầu Cài đặt hoặc Từ điển trong hàm tạo và chúng sẽ xuất hiện một cách kỳ diệu khi tôi cần chúng!

Nếu không sử dụng bộ chứa IoC, tôi sẽ phải chuyển các cài đặt và / hoặc siêu dữ liệu xuống qua 2, 3, ..., n đối tượng trước khi nó thực sự được sử dụng bởi đối tượng cần nó.

Có nhiều lợi ích khác đối với các thùng chứa DI / IoC như những người khác đã nêu chi tiết ở đây và chuyển từ ý tưởng tạo đối tượng sang yêu cầu các đối tượng có thể gây khó chịu, nhưng sử dụng DI rất hữu ích cho tôi và nhóm của tôi vì vậy bạn có thể thêm nó để kho vũ khí của bạn!


2
Đây là một điểm cộng rất lớn. Không thể tin rằng không ai khác đã đề cập đến nó trước đó. Không có nhu cầu chuyển hoặc lưu trữ các đối tượng nhịp độ làm cho mã sạch hơn nhiều.
Fingerlas

1
Các đối tượng toàn cầu @qble hoạt động rất tốt ... nếu bạn muốn cơ sở mã của mình trở thành một cơn ác mộng liên kết chặt chẽ, không thể kiểm chứng, càng ngày càng khó sửa đổi theo thời gian. chúc may mắn với điều đó.
Jeffrey Cameron

2
@JeffreyCameron IoC Container đối tượng toàn cầu. Nó không loại bỏ sự phụ thuộc toàn cầu của bạn.
qble

3
Đúng, tuy nhiên, bộ chứa IoC trong suốt với phần còn lại của ứng dụng. Bạn gọi nó một lần khi khởi động để lấy một thể hiện và sau đó là lần cuối cùng bạn nhìn thấy nó. Với các đối tượng toàn cầu, mã của bạn bị lấp đầy bởi các phụ thuộc cứng
Jeffrey Cameron

1
@qble sử dụng các nhà máy để tạo các thể hiện thoáng qua một cách nhanh chóng. Các nhà máy là dịch vụ đơn lẻ. Nếu các thể hiện thoáng qua cần một cái gì đó từ thùng chứa IOC, sau đó nối chúng vào nhà máy và yêu cầu nhà máy chuyển sự phụ thuộc vào thể hiện thoáng qua.
Jeffrey Cameron

14

Khung IoC là tuyệt vời nếu bạn muốn ...

  • ... vứt bỏ sự an toàn. Nhiều (tất cả?) Các khung IoC buộc bạn phải thực thi mã nếu bạn muốn chắc chắn mọi thứ được nối chính xác. "Này! Hy vọng tôi đã thiết lập mọi thứ để việc khởi tạo 100 lớp này không bị thất bại trong sản xuất, ném ra các ngoại lệ con trỏ rỗng!"

  • ... xả mã của bạn với toàn cầu (khung IoC là tất cả về việc thay đổi trạng thái toàn cầu).

  • ... Viết mã tào lao với các phụ thuộc không rõ ràng rất khó để cấu trúc lại vì bạn sẽ không bao giờ biết cái gì phụ thuộc vào cái gì.

Vấn đề với IoC là những người sử dụng chúng đã từng viết mã như thế này

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

điều này rõ ràng là thiếu sót vì sự phụ thuộc giữa Foo và Bar là khó khăn. Sau đó, họ nhận ra rằng sẽ tốt hơn để viết mã như

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

đó cũng là thiếu sót, nhưng ít rõ ràng là như vậy. Trong Haskell loại Foo()sẽ là IO Foonhưng bạn thực sự không muốn IO-part và nên là một dấu hiệu cảnh báo rằng có gì đó không đúng với thiết kế của bạn nếu bạn có nó.

Để loại bỏ nó (phần IO), hãy tận dụng tất cả các lợi thế của khung IoC và không có nhược điểm nào bạn có thể sử dụng một nhà máy trừu tượng.

Giải pháp đúng sẽ là một cái gì đó như

data Foo = Foo { apa :: Bar }

hoặc có thể

data Foo = forall b. (IBar b) => Foo { apa :: b }

và chích (nhưng tôi sẽ không gọi nó là tiêm) Bar.

Ngoài ra: xem video này với Erik Meijer (nhà phát minh LINQ) nơi anh ấy nói rằng DI dành cho những người không biết toán (và tôi không thể đồng ý nhiều hơn): http://www.youtube.com/watch?v = 8Mttjyf-8P4

Không giống như ông Spolsky, tôi không tin rằng những người sử dụng khung IoC rất thông minh - tôi chỉ đơn giản tin rằng họ không biết toán.


9
loại bỏ an toàn - ngoại trừ ví dụ của bạn vẫn là loại an toàn, đó là giao diện. Bạn không chuyển qua các loại đối tượng, mà là loại giao diện. Và loại an toàn không loại bỏ các ngoại lệ tham chiếu null, dù sao bạn cũng có thể vượt qua các tham chiếu null dưới dạng tham số an toàn loại - đó hoàn toàn không phải là vấn đề an toàn loại.
blowdart

Bạn vứt bỏ sự an toàn của loại vì bạn không biết loại đó có được nối dây hay không trong bộ chứa IoC (loại IoC <IBar> không IBar, thực tế là vậy IO IBar) .. Và vâng, trong ví dụ của Haskell Tôi không thể có được một tài liệu tham khảo null.
finnsson

Loại an toàn không thực sự về một đối tượng được nối dây, ít nhất là không phải trong Java / C #, nó chỉ đơn giản là về đối tượng thuộc đúng loại. Và ConcreteClass myClass = null vẫn là đúng loại. Nếu bạn muốn thực thi không null thì các hợp đồng mã sẽ được sử dụng.
blowdart

2
Tôi đồng ý với điểm 1 của bạn ở một mức độ nào đó, tôi muốn một số lời giải thích về thứ 2 và thứ 3 chưa bao giờ là vấn đề đối với tôi. Mẫu C # của bạn sử dụng bộ chứa IoC làm công cụ định vị dịch vụ, một lỗi phổ biến. Và so sánh C # vs Haskell = táo với cam.
Mauricio Scheffer

2
Bạn không vứt bỏ sự an toàn. Một số (nếu không phải hầu hết) các thùng chứa DI cho phép bạn xác minh biểu đồ đối tượng hoàn chỉnh sau quá trình đăng ký. Khi thực hiện việc này, bạn có thể ngăn ứng dụng bắt đầu cấu hình sai (không nhanh) và các ứng dụng của tôi luôn có một vài thử nghiệm đơn vị gọi cấu hình sản xuất để cho phép tôi tìm thấy các lỗi này trong quá trình kiểm tra. Tôi sẽ khuyên mọi người sử dụng bộ chứa IoC để viết một vài bài kiểm tra đơn vị để đảm bảo tất cả các đối tượng cấp cao nhất có thể được giải quyết.
Steven

10

Tôi thấy rằng việc triển khai chính xác Dependency Injection có xu hướng buộc các lập trình viên sử dụng nhiều cách thực hành lập trình khác giúp cải thiện khả năng kiểm tra, tính linh hoạt, khả năng duy trì và khả năng mở rộng của mã: các thực hành như Nguyên tắc trách nhiệm đơn lẻ, Tách biệt mối quan tâm và mã hóa chống lại API. Cảm giác như tôi bị buộc phải viết các lớp và phương thức mô đun có kích thước cắn hơn, giúp mã dễ đọc hơn, bởi vì nó có thể được thực hiện trong các đoạn có kích thước cắn.

Nhưng nó cũng có xu hướng tạo ra các cây phụ thuộc khá lớn, dễ dàng quản lý hơn thông qua một khung (đặc biệt nếu bạn sử dụng các quy ước) hơn là bằng tay. Hôm nay tôi muốn thử nghiệm một cái gì đó thực sự nhanh chóng trong LINQPad và tôi nghĩ rằng sẽ quá phiền khi tạo kernel và tải trong các mô-đun của mình, và cuối cùng tôi đã viết nó bằng tay:

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

Nhìn lại, việc sử dụng khung IoC sẽ nhanh hơn vì các mô-đun xác định khá nhiều tất cả những thứ này theo quy ước.

Đã dành thời gian nghiên cứu các câu trả lời và nhận xét về câu hỏi này, tôi tin rằng những người phản đối việc sử dụng một container IoC không thực hành tiêm phụ thuộc thực sự. Các ví dụ tôi đã thấy là các thực tiễn thường bị nhầm lẫn với tiêm phụ thuộc. Một số người phàn nàn về khó khăn trong việc "đọc" mã. Nếu được thực hiện chính xác, phần lớn mã của bạn sẽ giống hệt nhau khi sử dụng DI bằng tay như khi sử dụng bộ chứa IoC. Sự khác biệt sẽ nằm hoàn toàn trong một vài "điểm khởi động" trong ứng dụng.

Nói cách khác, nếu bạn không thích các bộ chứa IoC, có lẽ bạn không thực hiện Dependency Injection theo cách nó được thực hiện.

Một điểm khác: Tiêm phụ thuộc thực sự không thể thực hiện bằng tay nếu bạn sử dụng phản xạ ở bất cứ đâu. Mặc dù tôi ghét những gì sự phản chiếu làm mã điều hướng, bạn phải nhận ra rằng có những khu vực nhất định mà nó thực sự không thể tránh được. ASP.NET MVC, ví dụ, cố gắng khởi tạo bộ điều khiển thông qua sự phản chiếu trên mỗi yêu cầu. Để thực hiện tiêm phụ thuộc bằng tay, bạn sẽ phải làm cho mọi bộ điều khiển trở thành "gốc ngữ cảnh", như vậy:

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

Bây giờ hãy so sánh điều này với việc cho phép một khung DI làm điều đó cho bạn:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

Sử dụng khung DI, lưu ý rằng:

  • Tôi có thể kiểm tra đơn vị lớp học này. Bằng cách tạo một bản giả ISimpleWorkflowInstanceMerger, tôi có thể kiểm tra xem nó có được sử dụng theo cách tôi dự đoán không mà không cần kết nối cơ sở dữ liệu hay bất cứ thứ gì.
  • Tôi sử dụng mã ít hơn nhiều, và mã dễ đọc hơn nhiều.
  • Nếu một trong những thay đổi của người phụ thuộc của tôi thay đổi, tôi không phải thực hiện bất kỳ thay đổi nào đối với bộ điều khiển. Điều này đặc biệt tốt khi bạn xem xét rằng nhiều bộ điều khiển có khả năng sử dụng một số phụ thuộc giống nhau.
  • Tôi không bao giờ tham chiếu rõ ràng các lớp từ lớp dữ liệu của tôi. Ứng dụng web của tôi chỉ có thể bao gồm một tham chiếu đến dự án có chứa ISimpleWorkflowInstanceMergergiao diện. Điều này cho phép tôi chia ứng dụng thành các mô-đun riêng biệt và duy trì kiến ​​trúc đa tầng thực sự, điều này giúp mọi thứ linh hoạt hơn nhiều.

Một ứng dụng web thông thường sẽ có khá nhiều bộ điều khiển. Tất cả những nỗi đau khi làm DI bằng tay trong mỗi bộ điều khiển sẽ thực sự tăng lên khi ứng dụng của bạn phát triển. Nếu bạn có một ứng dụng chỉ có một gốc ngữ cảnh, không bao giờ cố gắng khởi tạo dịch vụ bằng phản xạ, thì đây không phải là vấn đề lớn. Tuy nhiên, bất kỳ ứng dụng nào sử dụng Dependency Injection sẽ trở nên cực kỳ tốn kém để quản lý một khi nó đạt đến một kích thước nhất định, trừ khi bạn sử dụng một khung nào đó để quản lý biểu đồ phụ thuộc.


9

Bất cứ khi nào bạn sử dụng từ khóa "mới", bạn đang tạo ra một sự phụ thuộc lớp cụ thể và một tiếng chuông báo thức nhỏ sẽ vang lên trong đầu bạn. Nó trở nên khó khăn hơn để kiểm tra đối tượng này trong sự cô lập. Giải pháp là lập trình cho các giao diện và đưa ra sự phụ thuộc để đối tượng có thể được kiểm tra đơn vị với bất kỳ thứ gì thực hiện giao diện đó (ví dụ: giả).

Vấn đề là bạn phải xây dựng các đối tượng ở đâu đó. Mẫu Factory là một cách để chuyển khớp nối ra khỏi POXO của bạn (Plain Old "chèn ngôn ngữ OO của bạn vào đây" Đối tượng). Nếu bạn và đồng nghiệp của bạn đều viết mã như thế này thì bộ chứa IoC là "Cải tiến tăng dần" tiếp theo mà bạn có thể thực hiện cho cơ sở mã của mình. Nó sẽ chuyển tất cả mã soạn sẵn của Nhà máy khó chịu ra khỏi các đối tượng sạch và logic kinh doanh của bạn. Họ sẽ nhận được nó và yêu nó. Heck, nói chuyện với một công ty về lý do tại sao bạn yêu thích nó và khiến mọi người say mê.

Nếu đồng nghiệp của bạn chưa làm DI, thì tôi khuyên bạn nên tập trung vào đó trước. Truyền bá về cách viết mã sạch dễ kiểm tra. Mã DI sạch là phần khó, một khi bạn đã ở đó, việc chuyển logic dây đối tượng từ các lớp Factory sang một thùng chứa IoC sẽ tương đối tầm thường.


8

Bởi vì tất cả các phụ thuộc đều có thể nhìn thấy rõ, nó thúc đẩy việc tạo các thành phần được ghép lỏng lẻo và đồng thời dễ dàng truy cập và tái sử dụng trên ứng dụng.


7

Bạn không cần một container IoC.

Nhưng nếu bạn nghiêm túc theo mô hình DI, bạn sẽ thấy rằng có một cái sẽ loại bỏ rất nhiều mã nhàm chán, dư thừa.

Dù sao, đó thường là thời điểm tốt nhất để sử dụng thư viện / khung - khi bạn hiểu những gì nó đang làm và có thể làm mà không cần thư viện.


6

Tôi thực sự đang trong quá trình lấy mã DI được trồng tại nhà và thay thế nó bằng IOC. Có lẽ tôi đã loại bỏ hơn 200 dòng mã và thay thế nó bằng khoảng 10. Vâng, tôi đã phải học một chút về cách sử dụng container (Winsor), nhưng tôi là một kỹ sư làm việc về các công nghệ internet trong Thế kỷ 21 nên tôi đã quen với điều đó. Tôi có lẽ đã dành khoảng 20 phút để xem qua cách làm. Điều này cũng có giá trị thời gian của tôi.


3
"nhưng tôi là một kỹ sư làm việc về các công nghệ internet trong thế kỷ 21 nên tôi đã quen với điều đó" -> +1
user96403

5

Khi bạn tiếp tục tách các lớp của mình và đảo ngược các phụ thuộc của bạn, các lớp tiếp tục duy trì ở mức nhỏ và "biểu đồ phụ thuộc" tiếp tục tăng kích thước. (Điều này không tệ.) Sử dụng các tính năng cơ bản của bộ chứa IoC làm cho việc kết nối tất cả các đối tượng này trở nên tầm thường, nhưng thực hiện thủ công có thể rất nặng nề. Ví dụ: nếu tôi muốn tạo một phiên bản mới của "Foo" nhưng nó cần một "Bar". Và một "Thanh" cần "A", "B" và "C". Và mỗi thứ cần 3 thứ khác, v.v. (vâng, tôi không thể nghĩ ra tên giả tốt :)).

Sử dụng bộ chứa IoC để xây dựng biểu đồ đối tượng cho bạn giảm độ phức tạp một tấn và đẩy nó ra thành cấu hình một lần. Tôi chỉ đơn giản nói "tạo cho tôi một 'Foo'" và nó chỉ ra những gì cần thiết để xây dựng một cái.

Một số người sử dụng các bộ chứa IoC cho cơ sở hạ tầng nhiều hơn, điều này rất tốt cho các kịch bản nâng cao nhưng trong những trường hợp đó, tôi đồng ý rằng nó có thể làm xáo trộn và làm cho mã khó đọc và gỡ lỗi cho các nhà phát triển mới.


1
Tại sao chúng ta tiếp tục xoa dịu mẫu số chung thấp nhất thay vì làm việc để kéo chúng lên?
stevenharman

4
Tôi đã không nói bất cứ điều gì về việc xoa dịu. Tôi chỉ nói các kịch bản nâng cao có thể khiến mọi thứ trở nên khó hiểu hơn đối với các nhà phát triển mới - đây là một sự thật. Tôi đã không nói "vì vậy đừng làm điều đó". Nhưng ngay cả sau đó (thời gian dành cho người ủng hộ của quỷ) thường có những cách khác để thực hiện điều tương tự làm cho một cơ sở mã hóa rõ ràng và dễ tiếp cận hơn. Ẩn sự phức tạp trong cấu hình tùy chỉnh công cụ cơ sở hạ tầng của bạn chỉ vì bạn không thể là lý do chính đáng và nó không kéo ai lên.
Aaron Lerch

4

Dittos về sự thống nhất. Trở nên quá lớn, và bạn có thể nghe thấy tiếng kêu cót két.

Tôi không bao giờ làm tôi ngạc nhiên khi mọi người bắt đầu nói về việc mã IoC sạch sẽ trông giống như những người đã từng nói về việc các mẫu trong C ++ là cách thanh lịch để quay trở lại trong những năm 90, nhưng ngày nay sẽ giải mã chúng như một cách phức tạp . Bah!


2

Trong thế giới .NET AOP không quá phổ biến, vì vậy, đối với DI, khung là lựa chọn thực sự duy nhất của bạn, cho dù bạn tự viết hay sử dụng khung khác.

Nếu bạn đã sử dụng AOP, bạn có thể tiêm khi biên dịch ứng dụng của mình, điều này phổ biến hơn trong Java.

Có nhiều lợi ích đối với DI, chẳng hạn như giảm khớp nối để kiểm tra đơn vị dễ dàng hơn, nhưng bạn sẽ thực hiện nó như thế nào? Bạn có muốn sử dụng sự phản chiếu để tự làm điều đó?


Khung MEF mới chỉ cho phép điều này cho .NET và tôi phải nói rằng mã sạch hơn và dễ hiểu hơn, giúp hiểu khái niệm DI dễ dàng hơn nhiều.
terjetyl

4
@TT: MEF không phải là thùng chứa phụ thuộc và không liên quan gì đến AOP.
Mauricio Scheffer

2

Vì vậy, đã gần 3 năm rồi hả?

  1. 50% những người bình luận ủng hộ khung IoC không hiểu sự khác biệt giữa Khung IoC và Khung IoC. Tôi nghi ngờ họ biết rằng bạn có thể viết mã mà không cần triển khai đến máy chủ ứng dụng

  2. Nếu chúng ta sử dụng khung công tác Java Spring phổ biến nhất, thì đó là cấu hình IoC được chuyển từ XMl sang mã và bây giờ trông như thế này

`@Configuration lớp công khai AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `Và chúng ta cần một khung để làm điều này tại sao chính xác?


1

Thành thật mà nói tôi không thấy có nhiều trường hợp cần có các bộ chứa IoC và hầu hết thời gian, chúng chỉ làm tăng thêm sự phức tạp không cần thiết.

Nếu bạn đang sử dụng nó chỉ để làm cho việc xây dựng một đối tượng đơn giản hơn, tôi phải hỏi, bạn có đang khởi tạo đối tượng này ở nhiều hơn một vị trí không? Một singleton không phù hợp với nhu cầu của bạn? Bạn đang thay đổi cấu hình trong thời gian chạy? (Chuyển đổi loại nguồn dữ liệu, vv).

Nếu có, thì bạn có thể cần một container IoC. Nếu không, thì bạn chỉ cần di chuyển việc khởi tạo ra khỏi nơi mà nhà phát triển có thể dễ dàng nhìn thấy nó.

Ai nói rằng một giao diện tốt hơn so với thừa kế? Giả sử bạn đang thử nghiệm một Dịch vụ. Tại sao không sử dụng hàm tạo DI và tạo các bản sao của các phụ thuộc bằng cách sử dụng kế thừa? Hầu hết các dịch vụ tôi sử dụng chỉ có một vài phụ thuộc. Thực hiện kiểm tra đơn vị theo cách này sẽ ngăn việc duy trì hàng tấn giao diện vô dụng và có nghĩa là bạn không phải sử dụng Resharper để nhanh chóng tìm thấy khai báo của phương thức.

Tôi tin rằng đối với hầu hết các triển khai, việc nói rằng IoC Container loại bỏ mã không cần thiết là một huyền thoại.

Đầu tiên, có thiết lập container ở nơi đầu tiên. Sau đó, bạn vẫn phải xác định từng đối tượng cần được khởi tạo. Vì vậy, bạn không lưu mã khi khởi tạo, bạn di chuyển nó (trừ khi đối tượng của bạn được sử dụng nhiều lần. Nó có tốt hơn khi là Singleton không?). Sau đó, đối với mỗi đối tượng bạn đã khởi tạo theo cách này, bạn phải tạo và duy trì giao diện.

Có ai có suy nghĩ nào về vấn đề này nữa ko?


Ngay cả đối với các dự án nhỏ, việc đặt cấu hình trong XML làm cho nó trở nên gọn gàng hơn và nhiều mô đun hơn - và lần đầu tiên, làm cho mã có thể được sử dụng lại (vì nó không còn bị nhiễm keo nữa). Đối với các sản phẩm phần mềm phù hợp, bạn có thể định cấu hình hoặc trao đổi ví dụ ShippingCostCalculatorhoặc ProductUIPresentation, hoặc bất kỳ chiến lược / triển khai nào khác, trong cùng một cơ sở mã , bằng cách triển khai chỉ một tệp XML đã thay đổi.
Thomas W

Nếu nó được thực hiện willy-nilly, như một thông lệ chung, tôi nghĩ bạn sẽ phức tạp hơn chứ không phải ít hơn. Khi bạn cần trao đổi một cái gì đó trong thời gian chạy, chúng rất tuyệt, nhưng tại sao lại thêm obfuscation? Tại sao phải giữ chi phí hoạt động của các giao diện bổ sung cho những thứ sẽ không bao giờ bị tắt? Tôi không nói không sử dụng IoC để thử nghiệm. Bằng mọi cách, sử dụng thuộc tính hoặc thậm chí là phụ thuộc hàm tạo. Bạn có cần sử dụng một giao diện? Tại sao không phân lớp các đối tượng thử nghiệm của bạn? Điều đó có sạch hơn không?
Fred

Bạn hoàn toàn không cần giao diện, để thực hiện cấu hình XML / IoC. Tôi đã tìm thấy những cải tiến lớn về sự rõ ràng trong một dự án nhỏ chỉ với ~ 8 trang và 5 hoặc 6 dịch vụ. Có ít sự xáo trộn hơn vì các dịch vụ và bộ điều khiển không còn cần mã keo.
Thomas W

1

Bạn sẽ cần một bộ chứa IoC nếu bạn cần tập trung cấu hình các phụ thuộc của mình để chúng có thể dễ dàng hoán đổi hàng loạt. Điều này có ý nghĩa nhất trong TDD, nơi nhiều phụ thuộc được hoán đổi, nhưng ở đó có ít sự phụ thuộc lẫn nhau giữa các phụ thuộc. Điều này được thực hiện với chi phí làm xáo trộn dòng kiểm soát xây dựng đối tượng ở một mức độ nào đó, do đó, việc có một cấu hình tài liệu hợp lý và được tổ chức tốt là rất quan trọng. Nó cũng tốt để có một lý do để làm điều này, nếu không, nó chỉ là trừu tượng mạ vàng . Tôi đã thấy nó được thực hiện kém đến mức nó được kéo xuống tương đương với một câu lệnh goto cho các nhà xây dựng.


1

Đây là lý do tại sao. Dự án được gọi là IOC-with-Ninject. Bạn có thể tải xuống và chạy nó với Visual Studio. Ví dụ này sử dụng Ninject nhưng TẤT CẢ các câu lệnh 'mới' nằm ở một vị trí và bạn hoàn toàn có thể thay đổi cách ứng dụng của bạn chạy bằng cách thay đổi mô-đun liên kết nào sẽ sử dụng. Ví dụ được thiết lập để bạn có thể liên kết với phiên bản giả của dịch vụ hoặc phiên bản thực. Trong các dự án nhỏ có thể không quan trọng, nhưng trong các dự án lớn thì đó là một vấn đề lớn.

Để rõ ràng, những lợi thế như tôi thấy chúng: 1) TẤT CẢ các câu lệnh mới trong một vị trí ở gốc mã. 2) Hoàn toàn mã lại hệ số với một thay đổi. 3) Thêm điểm cho 'yếu tố mát mẻ' 'vì nó ... tốt: tuyệt vời. : p


1

Tôi sẽ cố gắng tìm ra lý do tại sao IOC có thể không tốt cho quan điểm của tôi.

Như mọi thứ khác, IOC container (hoặc như Einstein sẽ đặt nó I = OC ^ 2) là một khái niệm bạn phải tự quyết định xem bạn có cần nó hay không trong mã của bạn. Thời trang gần đây phản đối IOC chỉ có vậy, thời trang. Đừng yêu thời trang, đó là lần đầu tiên. Có rất nhiều khái niệm ngoài kia bạn có thể thực hiện trong mã của mình. Trước hết, tôi đang sử dụng phép nội xạ phụ thuộc kể từ khi tôi bắt đầu lập trình và tự học thuật ngữ này khi nó được phổ biến dưới tên đó. Kiểm soát phụ thuộc là một chủ đề rất cũ và nó đã được giải quyết cho đến hàng nghìn tỷ cách, tùy thuộc vào những gì được tách ra từ những gì. Tách mọi thứ khỏi mọi thứ là vô nghĩa. Vấn đề với IOC container là nó cố gắng hữu ích như Entity Framework hoặc NHibernate. Mặc dù việc viết một trình ánh xạ quan hệ đối tượng chỉ đơn giản là bắt buộc ngay khi bạn phải ghép bất kỳ cơ sở dữ liệu nào với hệ thống của mình, bộ chứa IOC không phải lúc nào cũng cần thiết. Vì vậy, khi IOC container hữu ích:

  1. Khi bạn có một tình huống có nhiều phụ thuộc bạn muốn tổ chức
  2. Khi bạn không quan tâm đến việc ghép mã của mình với sản phẩm của bên thứ ba
  3. Khi nhà phát triển của bạn muốn tìm hiểu cách làm việc với một công cụ mới

1: Không phải thường xuyên bạn có quá nhiều phụ thuộc trong mã của mình, hoặc bạn nhận thức được chúng sớm trong thiết kế. Tư duy trừu tượng là hữu ích khi tư duy trừu tượng là do.

2: Ghép mã của bạn với mã của bên thứ ba là một vấn đề của HuGe. Tôi đã làm việc với mã đã hơn 10 năm tuổi và đó là các khái niệm tiên tiến và tiên tiến ATL, COM, COM +, v.v. Không có gì bạn có thể làm với mã đó bây giờ. Điều tôi đang nói là một khái niệm tiên tiến mang lại lợi thế rõ ràng, tuy nhiên điều này bị hủy bỏ trong thời gian dài với chính lợi thế đã lỗi thời. Nó chỉ làm cho tất cả của nó đắt hơn.

3: Phát triển phần mềm là đủ khó. Bạn có thể mở rộng nó đến mức không thể nhận ra nếu bạn cho phép một số khái niệm nâng cao cắt vào mã của bạn. Có một vấn đề với IOC2. Mặc dù nó đang tách rời các phụ thuộc, nhưng nó cũng tách rời luồng logic. Hãy tưởng tượng bạn đã tìm thấy một lỗi và bạn cần đặt thời gian nghỉ để kiểm tra tình hình. IOC2, như bất kỳ khái niệm nâng cao nào khác, đang làm cho điều đó trở nên khó khăn hơn. Sửa một lỗi trong một khái niệm khó hơn sửa một lỗi trong mã đơn giản hơn, bởi vì khi bạn sửa một lỗi, một khái niệm phải được tuân theo một lần nữa. (Chỉ để cho bạn một ví dụ, C ++ .NET liên tục thay đổi cú pháp nhiều đến mức bạn cần phải suy nghĩ kỹ trước khi bạn cấu trúc lại một số phiên bản .NET cũ hơn.) Vậy vấn đề với IOC là gì? Vấn đề là trong việc giải quyết phụ thuộc. Logic để giải quyết thường được ẩn trong chính IOC2, được viết có thể theo cách không phổ biến mà bạn cần phải học và duy trì. Sản phẩm của bên thứ ba của bạn sẽ ở đó trong 5 năm chứ? Microsoft thì không.

Hội chứng "Chúng tôi biết làm thế nào" được viết ở khắp mọi nơi liên quan đến IOC2. Điều này tương tự như thử nghiệm tự động hóa. Thuật ngữ lạ mắt và giải pháp hoàn hảo thoạt nhìn, bạn chỉ cần đặt tất cả các bài kiểm tra của mình để thực hiện qua đêm và xem kết quả vào buổi sáng. Thật là đau đớn để giải thích cho công ty sau khi công ty thử nghiệm tự động thực sự có nghĩa là gì. Kiểm tra tự động chắc chắn không phải là cách nhanh chóng để giảm số lượng lỗi mà bạn có thể giới thiệu qua đêm để tăng chất lượng sản phẩm. Nhưng, thời trang đang làm cho khái niệm đó trở nên khó chịu. IOC2 bị hội chứng tương tự. Người ta tin rằng bạn cần phải thực hiện nó để phần mềm của bạn được tốt. EvErY cuộc phỏng vấn gần đây Tôi đã được hỏi nếu tôi đang thực hiện IOC2 và tự động hóa. Đó là một dấu hiệu của thời trang: công ty đã có một phần mã được viết bằng MFC mà họ sẽ không từ bỏ.

Bạn cần học IOC2 như bất kỳ khái niệm nào khác trong phần mềm. Quyết định nếu IOC2 cần được sử dụng là trong nhóm và công ty. Tuy nhiên, ít nhất TẤT CẢ các lập luận trên phải được đề cập trước khi đưa ra quyết định. Chỉ khi bạn thấy rằng mặt cộng lớn hơn mặt tiêu cực, bạn mới có thể đưa ra quyết định tích cực.

Không có gì sai với IOC2 ngoại trừ việc nó chỉ giải quyết các vấn đề mà nó giải quyết và giới thiệu các vấn đề mà nó đưa ra. Không có gì khác. Tuy nhiên, đi ngược lại thời trang là rất khó khăn, họ có mồ hôi miệng, tín đồ của bất cứ điều gì. Thật kỳ lạ khi không ai trong số họ ở đó khi vấn đề với sự huyền ảo của họ trở nên rõ ràng. Nhiều khái niệm trong ngành công nghiệp phần mềm đã được bảo vệ bởi vì chúng tạo ra lợi nhuận, sách được viết, các hội nghị được tổ chức, các sản phẩm mới được thực hiện. Đó là thời trang, thường là ngắn ngủi. Ngay khi mọi người tìm thấy một cái gì đó khác, họ từ bỏ nó hoàn toàn. IOC2 rất hữu ích nhưng nó cho thấy các dấu hiệu giống như nhiều khái niệm đã biến mất khác mà tôi đã thấy. Tôi không biết nó sẽ sống sót. Không có quy tắc cho điều đó. Bạn nghĩ rằng nếu nó hữu ích, nó sẽ tồn tại. Không, nó không đi theo cách đó. Một công ty giàu có là đủ và khái niệm này có thể chết trong vòng vài tuần. Chúng ta sẽ thấy. NHibernate sống sót, EF đứng thứ hai. Có lẽ IOC2 cũng sẽ tồn tại. Đừng quên rằng hầu hết các khái niệm trong phát triển phần mềm đều không có gì đặc biệt, chúng rất logic, đơn giản và rõ ràng và đôi khi khó nhớ quy ước đặt tên hiện tại hơn là hiểu chính khái niệm này. Kiến thức về IOC2 có làm cho nhà phát triển trở thành nhà phát triển tốt hơn không? Không, bởi vì nếu nhà phát triển không thể đưa ra một khái niệm có bản chất tương tự IOC2 thì anh ta sẽ khó hiểu được vấn đề nào mà IOC2 đang giải quyết, sử dụng nó sẽ trông giả tạo và anh ta hoặc cô ta có thể bắt đầu sử dụng nó vì lợi ích của một số loại chính trị. Đừng quên rằng hầu hết các khái niệm trong phát triển phần mềm đều không có gì đặc biệt, chúng rất logic, đơn giản và rõ ràng và đôi khi khó nhớ quy ước đặt tên hiện tại hơn là hiểu chính khái niệm này. Kiến thức về IOC2 có làm cho nhà phát triển trở thành nhà phát triển tốt hơn không? Không, bởi vì nếu nhà phát triển không thể đưa ra một khái niệm có bản chất tương tự IOC2 thì anh ta sẽ khó hiểu được vấn đề nào mà IOC2 đang giải quyết, sử dụng nó sẽ trông giả tạo và anh ta hoặc cô ta có thể bắt đầu sử dụng nó vì lợi ích của một số loại chính trị. Đừng quên rằng hầu hết các khái niệm trong phát triển phần mềm đều không có gì đặc biệt, chúng rất logic, đơn giản và rõ ràng và đôi khi khó nhớ quy ước đặt tên hiện tại hơn là hiểu chính khái niệm này. Kiến thức về IOC2 có làm cho nhà phát triển trở thành nhà phát triển tốt hơn không? Không, bởi vì nếu nhà phát triển không thể đưa ra một khái niệm có bản chất tương tự IOC2 thì anh ta sẽ khó hiểu được vấn đề nào mà IOC2 đang giải quyết, sử dụng nó sẽ trông giả tạo và anh ta hoặc cô ta có thể bắt đầu sử dụng nó vì lợi ích của một số loại chính trị. Kiến thức về IOC2 có làm cho nhà phát triển trở thành nhà phát triển tốt hơn không? Không, bởi vì nếu nhà phát triển không thể đưa ra một khái niệm có bản chất tương tự IOC2 thì anh ta sẽ khó hiểu được vấn đề nào mà IOC2 đang giải quyết, sử dụng nó sẽ trông giả tạo và anh ta hoặc cô ta có thể bắt đầu sử dụng nó vì lợi ích của một số loại chính trị. Kiến thức về IOC2 có làm cho nhà phát triển trở thành nhà phát triển tốt hơn không? Không, bởi vì nếu nhà phát triển không thể đưa ra một khái niệm có bản chất tương tự IOC2 thì anh ta sẽ khó hiểu được vấn đề nào mà IOC2 đang giải quyết, sử dụng nó sẽ trông giả tạo và anh ta hoặc cô ta có thể bắt đầu sử dụng nó vì lợi ích của một số loại chính trị.


0

Cá nhân, tôi sử dụng IoC như một số loại bản đồ cấu trúc của ứng dụng của mình (Vâng, tôi cũng thích StructMap;)). Nó giúp dễ dàng thay thế các triển khai giao diện thông thường của tôi bằng các triển khai Moq trong các thử nghiệm. Tạo một thiết lập thử nghiệm có thể dễ dàng như thực hiện một cuộc gọi khởi động mới vào khung IoC của tôi, thay thế bất kỳ lớp nào là ranh giới thử nghiệm của tôi bằng một giả.

Đây có lẽ không phải là thứ IoC dành cho, nhưng đó là thứ tôi thấy mình sử dụng nó nhiều nhất ..




0

Tôi nhận ra đây là một bài viết khá cũ, nhưng dường như nó vẫn hoạt động hợp lý và tôi nghĩ rằng tôi đã đóng góp một vài điểm chưa được đề cập trong các câu trả lời khác.

Tôi sẽ nói rằng tôi đồng ý với lợi ích của việc tiêm phụ thuộc, nhưng tôi thích tự mình xây dựng và quản lý các đối tượng, sử dụng một mô hình không giống như được Maxm007 nêu ra trong câu trả lời của mình. Tôi đã tìm thấy hai vấn đề chính khi sử dụng các container bên thứ 3:

1) Có thư viện bên thứ 3 quản lý vòng đời của các đối tượng của bạn một cách "tự động" có thể tự cho vay kết quả không mong muốn. Chúng tôi đã phát hiện ra rằng đặc biệt là trong các dự án lớn, bạn có thể có nhiều bản sao của một đối tượng hơn bạn mong đợi và nhiều hơn bạn sẽ làm nếu bạn tự quản lý vòng đời. Tôi chắc chắn điều này thay đổi tùy thuộc vào khung được sử dụng, nhưng dù sao thì vấn đề vẫn tồn tại. Điều này cũng có thể có vấn đề nếu đối tượng của bạn giữ tài nguyên, kết nối dữ liệu, v.v., vì đối tượng đôi khi có thể sống lâu hơn bạn mong đợi. Vì vậy, chắc chắn, các bộ chứa IoC có xu hướng tăng mức sử dụng tài nguyên và dung lượng bộ nhớ của một ứng dụng.

2) Theo tôi, container IoC là một dạng "lập trình hộp đen". Tôi đã thấy rằng đặc biệt, các nhà phát triển ít kinh nghiệm của chúng tôi có xu hướng lạm dụng chúng. Nó cho phép lập trình viên không phải suy nghĩ về việc các vật thể nên liên quan với nhau như thế nào hoặc làm thế nào để tách chúng ra, bởi vì nó cung cấp cho chúng một cơ chế trong đó chúng có thể đơn giản lấy bất kỳ vật nào chúng muốn ra khỏi không khí mỏng. Ví dụ, có thể có một lý do thiết kế tốt mà ObjectA không bao giờ nên biết về ObjectB trực tiếp, nhưng thay vì tạo một nhà máy hoặc cầu hoặc dịch vụ định vị, một lập trình viên thiếu kinh nghiệm sẽ nói đơn giản là "không vấn đề gì, tôi sẽ lấy ObjectB từ bộ chứa IoC ". Điều này thực sự có thể dẫn đến tăng khả năng ghép đối tượng, đó là điều mà IoC được cho là giúp ngăn chặn.


-8

Dependency Injection trong một dự án ASP.NET có thể được thực hiện với một vài dòng mã. Tôi cho rằng có một số lợi thế khi sử dụng một container khi bạn có một ứng dụng sử dụng nhiều giao diện người dùng và cần kiểm tra đơn vị.


1
bạn có thể đưa ra một ví dụ không? DI khác với ASP.NET như thế nào so với bất kỳ loại ứng dụng nào khác?
tofi9
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.