2009-09-11 39 views
46

tôi sẽ có các thành phần sau đây trong ứng dụng của tôiThiết kế - Trường hợp đối tượng cần được đăng ký khi sử dụng Windsor

  • DataAccess
  • DataAccess.Test
  • Business
  • Business.Test
  • Application

Tôi đã hy vọng sử dụng Castle Windsor như tôi oC để dán các lớp với nhau nhưng tôi không chắc chắn về thiết kế của dán.

Câu hỏi của tôi là ai có trách nhiệm đăng ký các đối tượng vào Windsor? Tôi có một vài ý tưởng;

  1. Mỗi lớp có thể đăng ký đối tượng của riêng mình. Để kiểm tra BL, băng ghế thử nghiệm có thể đăng ký các lớp mô phỏng cho DAL.
  2. Mỗi lớp có thể đăng ký đối tượng phụ thuộc của nó, ví dụ: lớp nghiệp vụ đăng ký các thành phần của lớp truy cập dữ liệu. Để kiểm tra BL, băng ghế thử nghiệm sẽ phải dỡ bỏ đối tượng DAL "thực" và đăng ký các đối tượng giả.
  3. Ứng dụng (hoặc ứng dụng thử nghiệm) đăng ký tất cả các đối tượng của phụ thuộc.

Ai đó có thể giúp tôi với một số ý tưởng và ưu điểm/khuyết điểm với các đường dẫn khác nhau không? Liên kết đến các dự án ví dụ sử dụng Castle Windsor theo cách này sẽ rất hữu ích.

Trả lời

73

Nói chung, tất cả các thành phần trong một ứng dụng phải được soạn càng chậm càng tốt, vì điều đó đảm bảo mô đun tối đa và các mô đun đó được ghép lỏng lẻo nhất có thể.

Trong thực tế, điều này có nghĩa là bạn nên định cấu hình vùng chứa ở gốc ứng dụng của mình.

  • Trong một ứng dụng máy tính để bàn, đó sẽ là trong phương thức Main (hoặc rất gần với nó)
  • Trong một ASP.NET (bao gồm MVC) ứng dụng, đó sẽ là trong Global.asax
  • Trong WCF, đó sẽ là một trong ServiceHostFactory
  • , vv

Bình chứa chỉ đơn giản là động cơ mà soạn module vào một ứng dụng làm việc. Về nguyên tắc, bạn có thể viết mã bằng tay (điều này được gọi là DI Man của người nghèo), nhưng việc sử dụng DI Container như Windsor dễ dàng hơn rất nhiều.

Như một Thành phần gốc lý tưởng sẽ là mảnh duy nhất của mã trong thư mục gốc của ứng dụng, làm cho việc áp dụng một cái gọi là Humble Executable (một thuật ngữ từ tuyệt vời xUnit Test Patterns) mà không cần kiểm tra đơn vị trong chinh no.

Kiểm tra của bạn hoàn toàn không cần thiết vì đối tượng và mô-đun của bạn phải có thể tổng hợp và bạn có thể trực tiếp cung cấp Kiểm tra số đôi cho họ từ các bài kiểm tra đơn vị. Tốt nhất là bạn có thể thiết kế tất cả các mô-đun của mình để trở thành bất khả tri.

Ngoài ra cụ thể trong Windsor bạn nên đóng gói logic đăng ký thành phần của bạn trong các trình cài đặt (các loại thực hiện IWindsorInstaller) Xem the documentation để biết thêm chi tiết

+0

Câu hỏi liên quan đến đoạn cuối cùng của bạn. Bạn có bất kỳ liên kết nào đến các ví dụ về loại thiết kế này không? –

+0

Đó sẽ chỉ là DI cơ bản như Constructor Injection và như vậy ... –

+0

Re đoạn cuối- điều này có nghĩa là bạn không sử dụng IOC trong các bài kiểm tra? Chỉ cần làm rõ. Oh, nhưng nếu mã bạn đang thử nghiệm cần nó để xây dựng chính nó? – Greg

23

Trong khi câu trả lời của Mark là rất tốt cho các kịch bản web, các lỗ hổng quan trọng với việc áp dụng nó cho mọi kiến ​​trúc (cụ thể là rich-client - tức là: WPF, WinForms, iOS, vv) là giả định rằng tất cả các thành phần cần thiết cho một hoạt động có thể/nên được tạo ra cùng một lúc.

Đối với máy chủ web, điều này có ý nghĩa vì mọi yêu cầu đều cực kỳ ngắn ngủi và bộ điều khiển ASP.NET MVC được tạo bởi khung bên dưới (không có mã người dùng) cho mọi yêu cầu đi kèm. có thể dễ dàng được sáng tác bởi một khuôn khổ DI, và có rất ít chi phí bảo trì để làm như vậy. Lưu ý rằng khung công tác web chịu trách nhiệm quản lý tuổi thọ của bộ điều khiển và cho mọi mục đích tuổi thọ của tất cả các phụ thuộc của nó (mà khung DI sẽ tạo/tiêm cho bạn khi tạo bộ điều khiển). Nó là hoàn toàn tốt đẹp mà các phụ thuộc sống trong suốt thời gian yêu cầu và mã người dùng của bạn không cần phải quản lý tuổi thọ của các thành phần và các thành phần phụ. Cũng lưu ý rằng các máy chủ web là không trạng thái trên các yêu cầu khác nhau (ngoại trừ trạng thái phiên, nhưng không liên quan đến cuộc thảo luận này) và bạn không bao giờ có nhiều cá thể điều khiển/bộ điều khiển con cần phải sống cùng một lúc để phục vụ một yêu cầu duy nhất.

Tuy nhiên, trong các ứng dụng khách hàng phong phú, điều này là không đúng. Nếu sử dụng kiến ​​trúc MVC/MVVM (bạn nên dùng!) Phiên của người dùng dài và các bộ điều khiển tạo bộ điều khiển con/bộ điều khiển anh em khi người dùng điều hướng qua ứng dụng (xem lưu ý về MVVM ở phía dưới). Sự tương tự với thế giới web là mọi đầu vào của người dùng (nhấn nút, thao tác được thực hiện) trong ứng dụng khách phong phú tương đương với yêu cầu được khung web nhận được. Tuy nhiên, sự khác biệt lớn là bạn muốn các bộ điều khiển trong một ứng dụng khách hàng phong phú sống sót giữa các hoạt động (rất có thể người dùng thực hiện nhiều thao tác trên cùng một màn hình - được điều khiển bởi một bộ điều khiển cụ thể) và các bộ điều khiển phụ được tạo và hủy khi người dùng thực hiện các hành động khác nhau (suy nghĩ về điều khiển tab tạo ra tab nếu người dùng điều hướng đến nó hoặc một phần giao diện người dùng chỉ cần tải nếu người dùng thực hiện các tác vụ cụ thể trên màn hình).

Cả hai đặc điểm này có nghĩa là là mã người dùng cần quản lý tuổi thọ của bộ điều khiển/bộ điều khiển phụ và không được tạo phụ thuộc của bộ điều khiển trước (ví dụ: bộ điều khiển phụ, chế độ xem , các thành phần trình bày khác, v.v.). Nếu bạn sử dụng một khung công tác DI để thực hiện các trách nhiệm này, bạn sẽ không chỉ có nhiều mã mà nó không thuộc về (Xem: Constructor over-injection anti-pattern), nhưng bạn cũng sẽ cần phải chuyển một thùng chứa phụ thuộc trong hầu hết lớp trình bày của bạn các thành phần của bạn có thể sử dụng nó để tạo các thành phần con của chúng khi cần.

Tại sao mã người dùng của tôi có quyền truy cập vào vùng chứa DI?

1) Vùng chứa phụ thuộc chứa tham chiếu đến nhiều thành phần trong ứng dụng của bạn. Thông qua cậu bé xấu này xung quanh mọi thành phần cần tạo/quản lý thành phần phụ anoter là tương đương với việc sử dụng các hình cầu trong kiến ​​trúc của bạn. Tồi tệ hơn bất kỳ thành phần phụ nào cũng có thể đăng ký thành phần mới vào vùng chứa để đủ nhanh nó cũng sẽ trở thành một lưu trữ toàn cầu. Các nhà phát triển sẽ ném các đối tượng vào thùng chứa chỉ để truyền dữ liệu giữa các thành phần (giữa các bộ điều khiển anh chị em hoặc giữa các hệ thống phân cấp bộ điều khiển sâu - tức là: một bộ điều khiển tổ tiên cần lấy dữ liệu từ bộ điều khiển ông bà). Lưu ý rằng trong thế giới web nơi vùng chứa không được chuyển xung quanh đến mã người dùng, điều này không bao giờ là vấn đề.

2) Vấn đề khác với các gói phụ thuộc so với dịch vụ định vị/nhà máy/instantiation instantiation đối tượng là giải quyết từ một container làm cho nó hoàn toàn mơ hồ cho dù bạn đang tạo một thành phần hoặc đơn giản là REUSING hiện có. Thay vào đó, nó được để lại một cấu hình tập trung (ví dụ: bootstrapper/Root Root) để tìm ra tuổi thọ của thành phần là gì. Trong một số trường hợp, điều này là ổn (ví dụ: bộ điều khiển web, nơi mà nó không phải là mã người dùng cần quản lý tuổi thọ của thành phần nhưng chính khung thời gian xử lý yêu cầu thời gian chạy). Tuy nhiên, điều này là cực kỳ có vấn đề khi thiết kế các thành phần của bạn CHỈ ĐỊNH cho dù đó là trách nhiệm của họ để quản lý một thành phần và tuổi thọ của nó (Ví dụ: Một ứng dụng điện thoại bật lên một bảng yêu cầu người dùng cung cấp một số thông tin. Khi người dùng nhập một số thông tin, trang tính sẽ bị từ chối, và điều khiển được trả lại cho bộ điều khiển ban đầu, điều này vẫn duy trì trạng thái từ những gì người dùng đã thực hiện trước đó). Nếu DI được sử dụng để giải quyết bảng điều khiển phụ nó mơ hồ những gì cuộc đời của nó nên được hoặc ai sẽ chịu trách nhiệm quản lý nó (bộ điều khiển khởi tạo). So sánh điều này với trách nhiệm rõ ràng được quyết định bởi việc sử dụng các cơ chế khác.

Kịch bản A:

// not sure whether I'm responsible for creating the thing or not 
DependencyContainer.GimmeA<Thing>() 

Kịch bản B:

// responsibility is clear that this component is responsible for creation 

Factory.CreateMeA<Thing>() 
// or simply 
new Thing() 

Kịch bản C:

// responsibility is clear that this component is not responsible for creation, but rather only consumption 

ServiceLocator.GetMeTheExisting<Thing>() 
// or simply 
ServiceLocator.Thing 

Như bạn thấy DI làm cho nó rõ ràng ai là chịu trách nhiệm về việc quản lý vòng đời của tiểu hợp phần.

LƯU Ý: Về mặt kỹ thuật nói nhiều khuôn khổ DI có một số cách để tạo thành phần uể oải (Xem: How not to do dependency injection - the static or singleton container) mà là tốt hơn rất nhiều so với đi qua container xung quanh, nhưng bạn vẫn đang phải trả chi phí thay đổi mã của bạn để truyền xung quanh các chức năng tạo ở mọi nơi, bạn thiếu hỗ trợ cấp 1 để chuyển các thông số hàm dựng hợp lệ trong khi tạo, và vào cuối ngày bạn vẫn đang sử dụng cơ chế chuyển hướng không cần thiết ở những nơi mà lợi ích duy nhất là để đạt được khả năng kiểm tra, có thể đạt được theo những cách tốt hơn, đơn giản hơn (xem bên dưới).

Điều này có nghĩa là gì?

Điều đó có nghĩa là DI thích hợp cho một số trường hợp nhất định và không thích hợp cho người khác. Trong các ứng dụng khách hàng phong phú, nó xảy ra để mang lại rất nhiều nhược điểm của DI với rất ít trong số các upsides. Ứng dụng của bạn càng phức tạp càng nhiều thì chi phí bảo trì càng lớn. Nó cũng mang tiềm năng nghiêm trọng cho việc sử dụng sai, tùy thuộc vào mức độ liên lạc của nhóm và quy trình xem xét mã của bạn chặt chẽ, có thể ở bất kỳ đâu từ một vấn đề không đến chi phí nợ công nghệ cao. Có một huyền thoại xảy ra xung quanh rằng các nhà cung cấp dịch vụ hoặc nhà máy cũ hoặc tốt cũ là cơ chế xấu và lỗi thời đơn giản vì chúng có thể không phải là cơ chế tối ưu trong thế giới ứng dụng web, nơi mà có rất nhiều người chơi. Chúng ta không nên over- tổng quát những bài học này cho tất cả các kịch bản và xem mọi thứ như móng tay chỉ vì chúng ta đã học được cách sử dụng một cái búa cụ thể.

Đề xuất của tôi ĐỐI VỚI ỨNG DỤNG RICH-CLIENT là sử dụng cơ chế tối thiểu đáp ứng các yêu cầu cho từng thành phần trong tầm tay. 80% thời gian này nên được kích hoạt trực tiếp. Các định vị dịch vụ có thể được sử dụng để chứa các thành phần lớp kinh doanh chính của bạn (ví dụ: các dịch vụ ứng dụng thường đơn lẻ), và tất nhiên các nhà máy và thậm chí cả mẫu Singleton cũng có vị trí của chúng. Không có gì để nói rằng bạn không thể sử dụng khung DI ẩn đằng sau định vị dịch vụ của bạn để tạo ra các phụ thuộc lớp kinh doanh của bạn và mọi thứ chúng phụ thuộc vào một lần - nếu điều đó làm cho cuộc sống của bạn dễ dàng hơn trong lớp đó, và lớp đó không không hiển thị tải chậm mà các lớp trình bày phong phú của khách hàng thực hiện một cách đầy đủ là. Chỉ cần đảm bảo bảo vệ mã người dùng của bạn khỏi quyền truy cập vào vùng chứa đó để bạn có thể ngăn chặn sự lộn xộn đi qua vùng chứa DI xung quanh có thể tạo.

Điều gì về khả năng kiểm tra?

Khả năng kiểm tra hoàn toàn có thể đạt được mà không có khung DI. Tôi khuyên bạn nên sử dụng một khung đánh chặn như UnitBox (miễn phí) hoặc TypeMock (đắt tiền). Các khung công tác này cung cấp cho bạn các công cụ cần thiết để giải quyết vấn đề (cách bạn giả lập các cuộc gọi tức thời và tĩnh trong C#) và không yêu cầu bạn thay đổi toàn bộ kiến ​​trúc của mình. đã đi vào thế giới .NET/Java). Sẽ khôn ngoan hơn khi tìm ra giải pháp cho vấn đề ở bàn tay và sử dụng các cơ chế ngôn ngữ tự nhiên và các mẫu tối ưu cho thành phần cơ bản sau đó cố gắng phù hợp với mọi chốt vuông vào lỗ DI tròn. Một khi bạn bắt đầu sử dụng những cơ chế đơn giản hơn, cụ thể hơn, bạn sẽ nhận thấy có rất ít nhu cầu DI trong codebase của bạn nếu có.

LƯU Ý: Đối với kiến ​​trúc MVVM

Trong kiến ​​trúc MVVM cơ bản xem mô hình có hiệu quả đảm nhận trách nhiệm quản lý, vì vậy cho tất cả mục đích xem xét 'điều khiển' từ ngữ ở trên để áp dụng cho 'xem -mô hình'. Cơ bản MVVM hoạt động tốt cho các ứng dụng nhỏ nhưng khi sự phức tạp của ứng dụng phát triển, bạn có thể muốn để sử dụng phương pháp tiếp cận MVCVM. Các kiểu xem trở thành các DTO câm nhất thành tạo điều kiện cho ràng buộc dữ liệu với chế độ xem trong khi tương tác với lớp kinh doanh và giữa các nhóm mô hình xem đại diện cho màn hình/phụ được đóng gói thành các thành phần điều khiển/bộ điều khiển phụ rõ ràng . Trong cả hai kiến ​​trúc, trách nhiệm của các bộ điều khiển tồn tại và trưng bày các đặc điểm tương tự được thảo luận ở trên.

+0

IMHO ngay cả đối với các kịch bản web, container web chỉ nên được sử dụng để có được dịch vụ phù hợp ... Nguy hiểm của container.GetService creaping in, chỉ là quá lớn và IMHO tốt hơn của bạn với mã VBA hoặc RAD cũ hơn container.GetService , bạn thực hiện tái cấu trúc và tìm cách triển khai cụ thể một nỗi đau và tốn kém hơn để viết ... – user1496062

+2

Tôi bị nhầm lẫn bởi bài đăng này. Các ứng dụng web yêu cầu xây dựng các đối tượng trong thời gian chạy dựa trên các giá trị thời gian chạy không chỉ dành cho các ứng dụng khách dày. Chúng ta đối phó với điều này bằng cách sử dụng Abstract Factories, Dispose pattern, cấu hình phạm vi IoC (phạm vi VM để liên kết View instance) và sự hỗ trợ cho các thùng chứa con lồng nhau. Ngoài ra, Unit Box dường như không tốt hơn Service Locator vì nó kết hợp logic tạo ra cho lớp cần sự phụ thuộc (SRP và DI vi phạm so với chỉ vi phạm DI). Nhưng những gì tôi đang bối rối nhất là không có một container làm thế nào để bạn giữ thiết kế của bạn RẮN? –

+0

Tôi không hiểu mối quan tâm của bạn với cuộc sống tùy chỉnh ... nó không quan trọng để tạo ra các nhà máy và nhà máy trừu tượng (đặc biệt là thông qua lâu đài windsor trong kinh nghiệm của tôi) - đó là cách đi đến nếu bạn muốn sử dụng một phụ thuộc khi cần thiết trong thời gian chạy (lười biếng tải và dỡ tất cả những gì bạn muốn). Nếu bạn đang tiêm tất cả các phụ thuộc trực tiếp có thể vào một constructor, bạn đang làm sai ... – Vivek

Các vấn đề liên quan