2012-03-13 40 views
9

Tôi có lớp A phụ thuộc vào 10 lớp khác. Theo mô hình Dependency Injection, tôi nên chuyển tất cả các phụ thuộc của A bởi hàm tạo của nó.Tiêm phụ thuộc - Phải làm gì khi bạn có nhiều phụ thuộc?

Vì vậy, cho phép giả định constructor này (tất nhiên đây không phải là làm việc hoặc mã thật, vì tôi không được phép để post đoạn code thực sự ở đây)

public ClassA(ClassB b, ClassC c, ClassD d, ClassE e, ClassF f, ClassG g, ClassH h, ClassI i) { 
    this.b = b; 
    this.c = c; 
    this.d = d; 
    this.e = e; 
    this.f = f; 
    this.g = g; 
    this.h = h; 
    this.i = i; 
} 

Tôi đã đọc về cuốn sách Martin Fowler về refactoring rằng có một phương thức với rất nhiều tham số là một mùi mã và không nên xảy ra.

Câu hỏi của tôi là: điều này có ổn không khi chúng ta nói về DI? Có cách nào tốt hơn để tiêm phụ thuộc mà không vi phạm các quy tắc của Martin Fowler không?

Tôi biết tôi có thể vượt qua các phụ thuộc thông qua các thuộc tính, nhưng điều đó có thể gây ra lỗi vì không ai thực sự chắc chắn điều gì sẽ được thông qua để lớp hoạt động.

EDIT

Cảm ơn tất cả những câu trả lời của bạn. Tôi sẽ cố gắng hiện nay để chứng minh một số phụ thuộc class A:

1 - Một lớp học để truy cập vào một DB
2 - Một lớp để truy cập một DB (vâng, tôi cần phải thực hiện các hoạt động trên hai cơ sở dữ liệu)
3 - một lớp học gửi thông báo lỗi bằng cách email
4 - một lớp học để tải cấu hình
5 - một lớp học mà sẽ đóng vai trò là bộ đếm thời gian cho một số hoạt động (có thể một này có thể tránh được)
6 - một lớp học với logic kinh doanh

Có nhiều thứ khác mà tôi đang cố gắng loại bỏ, nhưng đó là thực sự cần thiết và tôi không thấy bất kỳ cách nào để tránh chúng.

EDIT

Sau khi một số refactoring bây giờ tôi có 7 phụ thuộc (giảm so với 10). Nhưng tôi có 4 DAO đối tượng:

CustomerDAO
ProcessDAO
ProductsDAO
CatalogDAO

Is it correct làm tạo ra một lớp được gọi là MyProjectDAO và tiêm những DAO vào nó? Bằng cách này tôi sẽ chỉ có một lớp DAO tổng hợp tất cả các đối tượng DAO của dự án của tôi. Tôi không nghĩ rằng đây là một ý tưởng tốt bởi vì nó vi phạm Nguyên tắc Trách nhiệm duy nhất. Tôi có đúng không?

+0

có thể trùng lặp của [Dependency Injection Constructor Madness] (http://stackoverflow.com/questions/2420193/dependency-injection-constructor-madness) –

+0

khi bạn sử dụng một số loại ORM thì tất cả 4 DAO có thể được thay thế bằng Đối tượng UnitOfWork. – Firo

Trả lời

7

Bạn có thể biện minh (cho chính mình) tại sao lớp học phụ thuộc vào 10 lớp khác không? Có các biến thành viên mà bạn sử dụng để kết hợp với nhau một tập con của các lớp đó không? Nếu vậy, điều đó chỉ ra rằng lớp này sẽ được chia nhỏ sao cho lớp được trích xuất sẽ phụ thuộc vào tập hợp con và các biến liên kết trạng thái đó với nhau sẽ nằm trong lớp được trích xuất. Với 10 phụ thuộc, có thể lớp học này đơn giản phát triển quá lớn và cần phải có nội bộ của nó bị hỏng.

Một lưu ý về câu cuối cùng của bạn: sự phụ thuộc thứ tự như vậy cũng có thể là một mùi mã, do đó, có lẽ tốt không để lộ nó trong giao diện của bạn. Trong thực tế, xem xét liệu các yêu cầu đặt hàng có phải là do các hoạt động cần được thực hiện theo một thứ tự cụ thể (đó là sự phức tạp của thuật toán hoặc giao thức), hoặc vì bạn đã thiết kế các lớp của bạn phụ thuộc lẫn nhau. Nếu sự phức tạp là do thiết kế của bạn, refactor để loại bỏ sự phụ thuộc theo thứ tự nếu có thể.

Nếu bạn không thể tái cấu trúc (sự phức tạp là tất yếu và bạn có vấn đề phối hợp khủng khiếp), bạn có thể trừu tượng hóa sự xấu xí và giữ cho người dùng lớp này được bảo vệ (nhà xây dựng, nhà máy, vòi phun, v.v.).

Chỉnh sửa: Bây giờ tôi đã nghĩ về nó, tôi không tin rằng những phức tạp cần thiết của thuật toán hoặc giao thức của bạn không thể trừu tượng một chút (mặc dù có thể là trường hợp). Tùy thuộc vào vấn đề cụ thể của bạn, các điểm tương đồng trong các thao tác của các lớp phụ thuộc đó có thể được giải quyết tốt hơn với mẫu Chiến lược hoặc mẫu Observer (trình nghe sự kiện). Bạn có thể phải bao bọc các lớp này trong các lớp thích nghi chúng với các giao diện hơi khác so với những gì chúng hiện đang phơi bày. Bạn sẽ phải đánh giá sự cân bằng của việc có mã trong lớp quái vật này trở nên dễ đọc hơn (yay) với chi phí lên đến 10 lớp khác trong dự án của bạn (boo).

Tôi cũng muốn tạo phụ lục tóm tắt việc xây dựng lớp này. Có vẻ quan trọng là bất kỳ lớp nào phụ thuộc vào lớp này cũng sử dụng mẫu Dependency Injection.Bằng cách đó, nếu bạn sử dụng công cụ xây dựng, nhà máy, vòi phun, v.v. bạn không vô tình cướp đi một số lợi ích của việc sử dụng mẫu DI (điều quan trọng nhất trong tâm trí của tôi là khả năng thay thế các đối tượng giả để thử nghiệm) .

Chỉnh sửa 2 (dựa trên chỉnh sửa của bạn):

Suy nghĩ đầu tiên của tôi là "những gì, không phụ thuộc khai thác gỗ?" :)

Ngay cả khi biết phụ thuộc là gì, rất khó để đưa ra lời khuyên hữu ích.

Đầu tiên: trách nhiệm của mọi người là gì? Tại sao lớp này phụ thuộc vào mã điều khiển (logic nghiệp vụ) và trên mã Mô hình (hai lớp truy cập cơ sở dữ liệu khác nhau, với các lớp DAO)?

Tùy thuộc vào cả DAO và các lớp truy cập DB là một mùi mã. Mục đích của DAO là gì? Mục đích của các lớp DB là gì? Bạn đang cố gắng hoạt động ở nhiều cấp độ trừu tượng?

Một trong những nguyên tắc của OO là dữ liệu và hành vi được đưa vào những thứ nhỏ được gọi là lớp học. Bạn có vi phạm điều này khi bạn tạo ra lớp logic kinh doanh này khác với các đối tượng mà nó thao tác riêng biệt với DAO khác biệt với lớp này không? Liên quan: Hãy chuyển hướng ngắn gọn thành SOLID.

Thứ hai: Một lớp để tải cấu hình. Mùi hôi. Dependency Injection giúp bạn xác định các phụ thuộc và trao đổi chúng. Lớp quái vật của bạn phụ thuộc vào các thông số nhất định. Các tham số này được nhóm vào lớp cấu hình này vì ...? Tên của lớp cấu hình này là gì? Là DBparameters? nếu có, nó thuộc về (các) đối tượng DB, không phải lớp này. Có phải nó giống như cấu hình? Nếu vậy, bạn đã có một bộ phun phụ thuộc nhỏ ngay tại đó (được cấp, nó có lẽ chỉ tiêm chuỗi hoặc giá trị int thay vì dữ liệu tổng hợp như các lớp, nhưng tại sao?). Lúng túng.

Thứ ba: Bài học quan trọng nhất tôi học được từ Refactoring là mã của tôi bị hút. Mã của tôi không chỉ hút, nhưng không có một biến đổi nào để làm nó ngừng hút. Điều tốt nhất tôi có thể hy vọng là làm cho nó ít hút hơn. Một khi tôi đã làm điều đó, tôi có thể làm cho nó hút ít hơn một lần nữa. Và một lần nữa. Một số mẫu thiết kế là xấu, nhưng chúng tồn tại để cho phép mã sucky của bạn chuyển sang mã ít sucky hơn. Vì vậy, bạn có globals của bạn và làm cho họ singletons. Sau đó, bạn loại bỏ những người độc thân của bạn. Đừng nản lòng vì bạn vừa mới tái cấu trúc để thấy rằng mã của bạn vẫn còn hút. Nó hút ít hơn. Vì vậy, đối tượng tải cấu hình của bạn có thể ngửi, nhưng bạn có thể quyết định rằng đó không phải là phần dễ vỡ nhất trong mã của bạn. Trong thực tế, bạn có thể thấy rằng nỗ lực để "sửa chữa" nó không phải là giá trị nó.

+0

Vui lòng xem chỉnh sửa của tôi. –

+0

@RafaelColucci - cập nhật – ccoakley

+0

Mặc dù bạn không có nhiều cuộc nổi dậy như những người khác đã làm, tôi chọn chấp nhận bạn trả lời bởi vì bạn mở tâm trí của tôi để một số điều tôi đã không nghĩ đến. –

11

Theo kinh nghiệm của tôi:

  • Hãy thử để thiết kế lớp học của bạn vì vậy nó cần phụ thuộc ít hơn. Nếu nó cần nhiều, nó có thể có quá nhiều trách nhiệm.
  • Nếu bạn thực sự thuyết phục rằng thiết kế lớp học của bạn là phù hợp, hãy cân nhắc xem có nên hợp tác với nhau hay không (ví dụ: thông qua bộ điều hợp chịu trách nhiệm cho một hoạt động "lớn" mà lớp của bạn cần bằng cách ủy quyền với một số phụ thuộc). Sau đó, bạn có thể phụ thuộc vào bộ điều hợp thay vì phụ thuộc "nhỏ hơn".
  • Nếu mọi bit khác thực sự có ý nghĩa, chỉ cần nuốt mùi có nhiều thông số. Thỉnh thoảng no xảy ra.
+0

Trong toàn bộ sự nghiệp của bạn, có bao giờ bạn có một tình huống mà một lớp (hoặc phương pháp) thực sự phải có 10 (!) Tham số? –

+8

@SebastianWeber: Tuyệt đối. Mã chuyên nghiệp là * luôn luôn * uglier hơn so với các tình huống cẩn thận crafted mà đi lên trong sách thiết kế :) Trong một số trường hợp đó là xấu xí nhưng vẫn thích hợp; trong các trường hợp khác, nó có thể tránh được và chắc chắn * nên * tránh được; trong các trường hợp khác, nó có thể tránh được với rất nhiều nỗ lực, nhưng nỗ lực không đáng để hưởng lợi. –

+0

Tôi thấy rất nhiều mã rơi vào loại 2, tránh được và nên tránh. Nếu tôi đã từng gặp một thể loại 3 thì nó đã bị chôn vùi dưới một đống lớn các vấn đề bức xúc hơn thế nên đó là lý do để rời bỏ nó. Nhưng tôi thực sự rất thích nhìn thấy một thể loại 1 từ một hệ thống thế giới thực. –

3

Có - phương pháp sử dụng nhiều thông số này nên được xem là một mùi mã. Phương pháp này có thực sự chỉ làm một việc và một điều duy nhất không?

Nếu điều này vẫn đúng, bạn có thể vẫn giảm số phụ thuộc bằng cách xem xét mối quan hệ giữa các phụ thuộc - là bất kỳ liên quan nào liên quan chặt chẽ, chúng có thể được kết hợp thành phụ thuộc tổng hợp không? Ví dụ. bạn có thể cấu trúc lại bằng cách tạo ra một lớp K mới sử dụng A, B và C trong nội bộ (được đưa vào lớp K bởi hàm tạo, sau đó sử dụng composition) - do đó số tham số cho phương thức sẽ bị giảm xuống hai.

Rửa sạch và lặp lại cho đến khi tổng hợp không có ý nghĩa nữa và/hoặc bạn có số lượng thông số hợp lý.

Cũng thấy một bài đăng blog liên quan: "Refactoring to Aggregate Services"

+0

Vui lòng xem chỉnh sửa của tôi. –

-2

Tôi cũng muốn khuyên để thiết kế lại ứng dụng của bạn. Trong trường hợp không thể, bạn có thể vượt qua container IoC của bạn như một tham số hàm tạo. Nếu bạn không muốn kết hợp mã của bạn với một triển khai cụ thể, bạn luôn có thể trừu tượng nó. Mã sẽ trông giống như thế này.

public interface IAbstractContainer 
{ 
    T Resolve<T>(); 
} 

public class ConcreteContainer: IAbstractContainer 
{ 
    private IContainer _container; // E.g. Autofac container 

    public ConcreteContainer(IContainer container) 
    { 
    _container = container; 
    { 

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

public classA(IAbstractContainer container) 
{ 
    this.B = container.Resolve<ClassB>(); 
    this.C = container.Resolve<ClassC>(); 
    ... 
} 

}

Một ví dụ ConcreteContainer được tiêm theo cách thông thường.

+3

điều này không giải quyết được vấn đề, tất cả điều này là "ẩn" các phụ thuộc vì bạn không sử dụng phép xây dựng nữa, hơn nữa bạn có phụ thuộc mới vào vùng chứa - thực tế không phải là IoC mà là mẫu Định vị Dịch vụ – BrokenGlass

+3

Sử dụng dịch vụ định vị không phải là một ý tưởng tốt là một trong những (nhiều) lợi ích của DI là bạn có một số ý tưởng về những gì phụ thuộc của đối tượng được. –

+3

Cảm ơn. Tôi phải hiểu nó ngay bây giờ. Câu trả lời của tôi sẽ phục vụ như là một minh họa của mô hình chống này để những người khác có thể tránh sử dụng nó. –

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