6

Nếu tôi có một lớp cơ sở với các dịch vụ được tiêm thông qua các phụ thuộc của hàm dựng: Có thể khai báo hàm tạo của lớp con mà không sử dụng : base (params) không?Tiêm phụ thuộc vào cả lớp cơ sở và phân lớp với IoC?

public MyBaseClass 
{ 
    private IServiceA _serviceA; 
    private IServiceB _serviceB; 
    private IServiceC _serviceC; 

    public MyBaseClass(null, null, null) 
    public MyBaseClass(IServiceA serviceA, IServiceB serviceB, IServiceC serviceC) 
    { 
     _serviceA = serviceA; 
     _serviceB = serviceB; 
     _serviceC = serviceC; 
    } 
} 

Và một lớp con với một số phụ thuộc thêm tiêm:

public MySubClassA : MyBaseClass 
{ 
    private IServiceD _serviceD; 

    public MySubClassA (null, null, null, null) 
    public MySubClassA (IServiceA serviceA, IServiceB serviceB, 
         IServiceC serviceC, IServiceD serviceD) 
      : base (serviceA, serviceB, serviceC) 
    { 
     _serviceD = serviceD; 
    } 
} 

Vấn đề ở đây là tôi có nhiều lớp con, chỉ có 10 hoặc lâu hơn ngay bây giờ, nhưng số lượng sẽ tăng lên. Mỗi khi tôi cần thêm một phụ thuộc khác vào lớp cơ sở, tôi phải đi qua từng lớp con và tự thêm phụ thuộc vào đó. Công việc thủ công này khiến tôi nghĩ rằng có điều gì đó sai trái với thiết kế của tôi.

Vì vậy, có thể khai báo hàm tạo của MyBaseClassA mà không có các dịch vụ theo yêu cầu của lớp cơ sở trong hàm tạo của lớp con không? ví dụ để các nhà xây dựng của MyBaseClassA chỉ có mã này đơn giản hơn nhiều:

public MySubClassA (null) 
    public MySubClassA (IServiceD serviceD) 
    { 
     _serviceD = serviceD; 
    } 

gì tôi cần phải thay đổi trong lớp cơ sở để các dependency injection xảy ra ở đó và không cần phải được bổ sung vào các lớp học phụ như tốt? Tôi đang sử dụng LightInject IoC.

+2

Không thể thùng chứa IoC của bạn tiêm vào các thuộc tính thay vì hàm tạo? Tôi nghĩ rằng nó sẽ đơn giản hơn nhiều, đặc biệt là nếu bạn là các lớp lồng nhau sâu và rất nhiều tiêm. –

+2

Ngoài ý tưởng khác của Simon .. bạn có thể ném một wrapper xung quanh những phụ thuộc này (một container phụ thuộc, nếu bạn sẽ) như một giải pháp khác. Mặc dù tôi thích cách tiếp cận tài sản. –

+0

@SimonBelanger mọi thứ khác trong ứng dụng sử dụng tiêm tham số. Tôi nghĩ rằng tiêm tài sản đã được thử trong một phần của ứng dụng và đã có một số vấn đề mà tôi đã quên - tôi sẽ phải xem lại nó. –

Trả lời

14

Công việc thủ công này khiến tôi nghĩ rằng có sự cố với thiết kế của tôi.

Có thể có. Thật khó để cụ thể với các ví dụ bạn đã đưa ra, nhưng thường sự cố như vậy là do một trong các lý do sau:

  • Bạn sử dụng các lớp cơ sở để thực hiện các mối quan tâm chéo (chẳng hạn như ghi nhật ký, xử lý giao dịch, kiểm tra bảo mật) , xác nhận, vv) thay vì trang trí.
  • Bạn sử dụng các lớp cơ sở để triển khai hành vi được chia sẻ thay vì đặt hành vi đó trong các thành phần riêng biệt có thể được tiêm.

Các lớp cơ sở thường bị lạm dụng để thêm mối quan tâm xuyên suốt vào việc triển khai. Trong trường hợp đó, lớp cơ sở sẽ sớm trở thành God object: một lớp quá nhiều và biết quá nhiều. Nó vi phạm Single Responsibility Principle (SRP), khiến nó thay đổi thường xuyên, trở nên phức tạp và khó kiểm tra.

Giải pháp cho vấn đề đó là loại bỏ tất cả các lớp cơ sở và sử dụng nhiều decorators thay vì một lớp cơ sở duy nhất. Bạn nên viết một trang trí cho mỗi mối quan tâm chéo. Bằng cách này, mỗi người trang trí nhỏ và tập trung, và chỉ có một lý do để thay đổi (một trách nhiệm duy nhất). Bằng cách sử dụng a design that is based on generic interfaces bạn có thể tạo trang trí chung chung có thể bao bọc toàn bộ các loại có liên quan về mặt kiến ​​trúc. Ví dụ: một trình trang trí đơn có thể bao bọc tất cả các trường hợp sử dụng trong hệ thống của bạn hoặc một trình trang trí bao bọc tất cả truy vấn trong hệ thống có loại trả về nhất định.

Các lớp cơ sở thường bị lạm dụng để chứa một tập hợp các chức năng không liên quan được tái sử dụng bởi nhiều lần triển khai. Thay vì đặt tất cả logic này trong một lớp cơ sở (làm cho lớp cơ sở phát triển thành một cơn ác mộng bảo trì), chức năng này nên được trích xuất thành nhiều dịch vụ mà việc triển khai có thể phụ thuộc vào.

Khi bạn bắt đầu tái cấu trúc theo thiết kế như vậy, bạn sẽ thường thấy rằng các triển khai bắt đầu nhận được nhiều phụ thuộc, một mẫu chống được gọi là tính năng khởi tạo quá trình khởi tạo. Việc xây dựng quá trình xây dựng thường là một dấu hiệu cho thấy một lớp vi phạm SRP (làm cho nó phức tạp và khó kiểm tra). Nhưng việc chuyển logic từ lớp cơ sở sang các phụ thuộc không làm cho việc triển khai khó kiểm tra hơn, và trên thực tế mô hình với lớp cơ sở có cùng vấn đề, nhưng với sự khác biệt là các phụ thuộc được giấu đi.

Khi xem xét kỹ mã trong việc triển khai, bạn sẽ thường thấy một số loại mã định kỳ. Nhiều triển khai đang sử dụng cùng một tập hợp các phụ thuộc theo cùng một cách. Đây là một dấu hiệu cho thấy sự trừu tượng bị thiếu. Mã này và các phụ thuộc của nó có thể được trích xuất thành một aggregate service. Các dịch vụ tổng hợp làm giảm số lượng phụ thuộc một nhu cầu triển khai và kết thúc hành vi chung.

Sử dụng dịch vụ Tổng hợp trông giống như 'trình bao bọc' mà @SimonWhitehead nói về các nhận xét, nhưng xin lưu ý rằng các dịch vụ tổng hợp về trừu tượng cả phụ thuộc lẫn hành vi. Nếu bạn tạo một "thùng chứa" các phụ thuộc và phơi bày các phụ thuộc đó thông qua các thuộc tính công khai để triển khai chúng, bạn không giảm số phụ thuộc mà việc triển khai phụ thuộc và bạn không làm giảm độ phức tạp của lớp đó. thực hiện dễ dàng hơn để kiểm tra. Mặt khác, các dịch vụ tổng hợp làm giảm số lượng phụ thuộc và độ phức tạp của một lớp, giúp dễ dàng nắm bắt và kiểm tra.

Khi tuân theo các quy tắc này, sẽ không cần phải có lớp cơ sở trong hầu hết các trường hợp.

+0

+1 để giới thiệu mối quan tâm và giải pháp cắt ngang bằng cách sử dụng trang trí. (thực sự, nó nên +2) – Fendy

+1

câu trả lời gợi cảm nhất trong số –

+0

Mỗi lần và một lần nữa, tôi nghĩ sẽ tốt hơn nếu bạn dọn dẹp và tạo ra "lớp thần" - Sau đó, tôi đi qua câu trả lời này, một lần nữa, và nhận ra đó là một điều ngu ngốc tôi chết – ppumkin

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