bối cảnh xung quanh vs tiêm xây dựng


9

Tôi có nhiều lớp lõi yêu cầu ISessionContext của cơ sở dữ liệu, ILogManager để ghi nhật ký và IService được sử dụng để liên lạc với các dịch vụ khác. Tôi muốn sử dụng tiêm phụ thuộc cho lớp này được sử dụng bởi tất cả các lớp cốt lõi.

Tôi đã thực hiện hai khả năng. Lớp lõi chấp nhận IAmbientContext với tất cả ba lớp hoặc tiêm cho cả ba lớp.

public interface ISessionContext 
{
    ...
}

public class MySessionContext: ISessionContext 
{
    ...
}

public interface ILogManager 
{

}

public class MyLogManager: ILogManager 
{
    ...
}

public interface IService 
{
    ...
}

public class MyService: IService
{
    ...
}

Giải pháp đầu tiên:

public class AmbientContext
{
    private ISessionContext sessionContext;
    private ILogManager logManager;
    private IService service;

    public AmbientContext(ISessionContext sessionContext, ILogManager logManager, IService service)
    {
        this.sessionContext = sessionContext;
        this.logManager = logManager;
        this.service = service;
    }
}


public class MyCoreClass(AmbientContext ambientContext)
{
    ...
}

giải pháp thứ hai (không có ambientcontext)

public MyCoreClass(ISessionContext sessionContext, ILogManager logManager, IService service)
{
    ...
}

Không có giải pháp tốt nhất trong trường hợp này?


"Được IServicesử dụng để liên lạc với các dịch vụ khác là gì?" Nếu IServiceđại diện cho một sự phụ thuộc mơ hồ vào các dịch vụ khác thì nó có vẻ như là một công cụ định vị dịch vụ và không nên tồn tại. Lớp học của bạn nên phụ thuộc vào các giao diện mô tả rõ ràng những gì người tiêu dùng của họ sẽ làm với họ. Không có lớp nào cần một dịch vụ để cung cấp quyền truy cập vào một dịch vụ. Một lớp cần một sự phụ thuộc làm một cái gì đó cụ thể mà lớp cần.
Scott Hannen

Câu trả lời:


4

"Tốt nhất" là quá chủ quan ở đây. Như thường thấy với các quyết định như vậy, đó là sự đánh đổi giữa hai cách có giá trị như nhau để đạt được điều gì đó.

Nếu bạn tạo AmbientContextvà đưa nó vào nhiều lớp, bạn có khả năng cung cấp nhiều thông tin hơn cho mỗi lớp mà họ cần (ví dụ: lớp Foochỉ có thể sử dụng ISessionContext, nhưng cũng được nói về ILogManagerISessioncũng vậy).

Nếu bạn truyền từng thông qua một tham số, thì bạn chỉ cho mỗi lớp biết về những điều mà nó cần biết. Tuy nhiên, số lượng tham số có thể nhanh chóng tăng lên và bạn có thể thấy bạn có quá nhiều hàm tạo và phương thức với nhiều tham số, được lặp lại nhiều, có thể được đơn giản hóa thông qua một lớp ngữ cảnh.

Vì vậy, đó là một trường hợp cân bằng cả hai và chọn một cái phù hợp với hoàn cảnh của bạn. Nếu bạn chỉ có một lớp và ba tham số, cá nhân tôi sẽ không bận tâm AmbientContext. Đối với tôi, điểm tới hạn có thể sẽ là bốn tham số. Nhưng đó là ý kiến ​​thuần túy. Điểm bùng phát của bạn có thể sẽ khác với tôi, vì vậy hãy đi với những gì cảm thấy phù hợp với bạn.


4

Thuật ngữ từ câu hỏi không thực sự khớp với mã ví dụ. Mẫu Ambient Contextnày được sử dụng để lấy một phụ thuộc từ bất kỳ lớp nào trong bất kỳ mô-đun nào một cách dễ dàng nhất có thể, mà không gây ô nhiễm cho mọi lớp để chấp nhận giao diện của phụ thuộc, nhưng vẫn giữ ý tưởng đảo ngược điều khiển. Các phụ thuộc như vậy thường được dành riêng để ghi nhật ký, bảo mật, quản lý phiên, giao dịch, lưu trữ, kiểm toán cho bất kỳ mối quan tâm xuyên suốt nào trong ứng dụng đó. Đó là bằng cách nào đó gây phiền nhiễu để thêm một ILogging, ISecurity, ITimeProviderđơn vị thi công và hầu hết thời gian không phải tất cả các lớp học cần tất cả cùng một lúc, vì vậy tôi hiểu nhu cầu của bạn.

Điều gì xảy ra nếu thời gian tồn tại của ISessioncá thể khác với trường hợp ILogger? Có lẽ phiên bản ISession nên được tạo trên mỗi yêu cầu và ILogger một lần. Vì vậy, có tất cả các phụ thuộc này được chi phối bởi một đối tượng không phải là chính nó sẽ không phải là lựa chọn đúng đắn vì tất cả những vấn đề này với quản lý và bản địa hóa trọn đời và các đối tượng khác được mô tả trong chuỗi này.

Trong IAmbientContextcâu hỏi không giải quyết được vấn đề không gây ô nhiễm cho mọi nhà xây dựng. Bạn vẫn phải sử dụng nó trong chữ ký của nhà xây dựng, chắc chắn, chỉ một lần duy nhất lần này.

Vì vậy, cách dễ nhất là KHÔNG sử dụng phương thức tiêm xây dựng hoặc bất kỳ cơ chế tiêm nào khác để xử lý các phụ thuộc cắt chéo, mà sử dụng lệnh gọi tĩnh . Chúng tôi thực sự thấy mô hình này khá thường xuyên, được thực hiện bởi chính khuôn khổ. Kiểm tra Thread.CienPrincipal , một thuộc tính tĩnh trả về việc thực hiện IPrincipalgiao diện. Nó cũng có thể giải quyết để bạn có thể thay đổi việc triển khai nếu bạn muốn như vậy, do đó bạn không được kết hợp với nó.

MyCore bây giờ trông giống như

public class MyCoreClass
{
    public void BusinessFeature(string data)
    {
        LoggerContext.Current.Log(data);

        _repository.SaveProcessedData();

        SessionContext.Current.SetData(data);
        ...etc
    }
}

Mô hình này và các triển khai có thể đã được mô tả chi tiết bởi Mark Seemann trong bài viết này . Có thể có các triển khai dựa vào chính bộ chứa IoC mà bạn sử dụng.

Bạn muốn tránh AmbientContext.Current.Logger, AmbientContext.Current.Sessionvì những lý do tương tự như mô tả ở trên.

Nhưng bạn có các tùy chọn khác để giải quyết vấn đề này: sử dụng trang trí, đánh chặn động nếu container của bạn có khả năng này hoặc AOP. Bối cảnh xung quanh nên là giải pháp cuối cùng vì thực tế khách hàng của họ che giấu sự phụ thuộc của họ thông qua nó. Tôi vẫn sẽ sử dụng Ambient Context nếu giao diện thực sự bắt chước sự thúc đẩy của tôi để sử dụng một phụ thuộc tĩnh như DateTime.Nowhoặc ConfigurationManager.AppSettingsvà nhu cầu này tăng lên khá thường xuyên. Nhưng cuối cùng, tiêm constructor có thể không phải là một ý tưởng tồi để có được những phụ thuộc phổ biến này.


3

Tôi sẽ tránh AmbientContext.

Đầu tiên, nếu lớp học phụ thuộc vào AmbientContextthì bạn không thực sự biết nó làm gì. Bạn phải xem xét việc sử dụng phụ thuộc đó để tìm ra phụ thuộc lồng nhau nào mà nó sử dụng. Bạn cũng không thể nhìn vào số lượng phụ thuộc và cho biết liệu lớp của bạn có làm quá nhiều không vì một trong những phụ thuộc đó thực sự có thể đại diện cho một số phụ thuộc lồng nhau.

Thứ hai, nếu bạn đang sử dụng nó để tránh nhiều phụ thuộc của nhà xây dựng thì phương pháp này sẽ khuyến khích các nhà phát triển khác (bao gồm cả chính bạn) thêm thành viên mới vào lớp bối cảnh xung quanh đó. Sau đó, vấn đề đầu tiên là gộp.

Thứ ba, việc chế giễu sự phụ thuộc AmbientContexttrở nên khó khăn hơn bởi vì bạn phải tìm ra trong từng trường hợp liệu có nên chế giễu tất cả các thành viên của nó hay chỉ những người bạn cần, và sau đó thiết lập một giả để trả lại các giả đó bài kiểm tra đơn vị của bạn khó hơn để viết, đọc và duy trì.

Thứ tư, nó thiếu sự gắn kết và vi phạm Nguyên tắc Trách nhiệm duy nhất. Đó là lý do tại sao nó có một cái tên như "AmbientContext", bởi vì nó có rất nhiều thứ không liên quan và không có cách nào để đặt tên theo những gì nó làm.

Và nó có thể vi phạm Nguyên tắc phân chia giao diện bằng cách đưa các thành viên giao diện vào các lớp không cần chúng.


2

Thứ hai (không có trình bao bọc giao diện)

Trừ khi có một số tương tác giữa các dịch vụ khác nhau cần được gói gọn trong một lớp trung gian, nó chỉ làm phức tạp mã của bạn và hạn chế tính linh hoạt khi bạn giới thiệu 'giao diện của giao diện'

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.