6

Tôi đang viết một thư viện sẽ cung cấp một bộ sưu tập các loại công khai cho người tiêu dùng của nó.Giá trị mặc định cho các đối số hàm dựng trong một dự án thư viện

Tôi muốn tạo các loại từ thư tiêm phụ thuộc thư viện này thân thiện. Điều này có nghĩa là mọi lớp cần có một hàm tạo thông qua đó có thể chỉ định mọi phụ thuộc duy nhất của đối tượng đang được khởi tạo. Tôi cũng muốn thư viện tuân theo quy ước về nguyên tắc cấu hình. Điều này có nghĩa rằng nếu một người tiêu dùng muốn hành vi mặc định, anh ta có thể sử dụng một constructor parameterless và đối tượng sẽ bằng cách nào đó xây dựng các phụ thuộc cho chính nó.

Trong ví dụ (C#):

public class Samurai { 

    private readonly IWeapon _weapon; 

    // consumers will use this constructor most of the time 
    public Samurai() { 
     _weapon = ??? // get an instance of the default weapon somehow 
    } 

    // consumers will use this constructor if they want to explicitly 
    // configure dependencies for this instance 
    public Samurai(IWeapon weapon) { 
     _weapon = weapon; 
    } 
} 

giải pháp đầu tiên của tôi sẽ được sử dụng các mô hình dịch vụ định vị.

Mã này sẽ trông như thế này:

... 
public Samurai() { 
    _weapon = ServiceLocator.Instance.Get<IWeapon>(); 
} 
... 

Tôi có một vấn đề với điều này, mặc dù. Trình định vị dịch vụ đã được gắn cờ là một mẫu chống (link) và tôi hoàn toàn đồng ý với các đối số này. Mặt khác, Martin Fowler chủ trương sử dụng mô hình định vị dịch vụ chính xác trong tình huống này (các dự án thư viện) (link). Tôi muốn cẩn thận và loại bỏ sự cần thiết có thể để viết lại thư viện sau khi nó cho thấy rằng dịch vụ định vị thực sự là một ý tưởng tồi.

Vì vậy, trong kết luận - bạn có nghĩ rằng định vị dịch vụ là tốt trong trường hợp này? Tôi có nên giải quyết vấn đề của mình theo một cách hoàn toàn khác không? Bất kỳ suy nghĩ được chào đón ...

Trả lời

4

Nếu bạn muốn làm cho cuộc sống dễ dàng hơn cho những người dùng không sử dụng một container DI, bạn có thể cung cấp cho các trường hợp mặc định thông qua một lớp học dành riêng Defaults trong đó có phương pháp như thế này:

public virtual Samurai CreateDefaultSamurai() 
{ 
    return new Samurai(CreateDefaultWeapon()); 
} 

public virtual IWeapon CreateDefaultWeapon() 
{ 
    return new Shuriken(); 
} 

Bằng cách này bạn không cần phải gây ô nhiễm bản thân các lớp với các hàm tạo mặc định và người dùng của bạn không có nguy cơ sử dụng các hàm tạo mặc định đó một cách vô tình.

+2

Điều quan trọng là phải giữ một lớp DI thân thiện. +1 –

1

Có một sự thay thế, đó là tiêm một nhà cung cấp cụ thể, chúng ta hãy nói một WeaponProvider trong trường hợp của bạn vào lớp học của bạn để nó có thể thực hiện tra cứu cho bạn:

public interface IWeaponProvider 
{ 
    IWeapon GetWeapon(); 
} 

public class Samurai 
{ 
    private readonly IWeapon _weapon; 

    public Samurai(IWeaponProvider provider) 
    { 
     _weapon = provider.GetWeapon(); 
    } 
} 

Bây giờ bạn có thể cung cấp cho một nhà cung cấp mặc định địa phương cho một vũ khí:

public class DefaultWeaponProvider : IWeaponProvider 
{ 
    public IWeapon GetWeapon() 
    { 
     return new Sword(); 
    } 
} 

Và vì đây là một mặc định địa phương (như trái ngược với một từ một hội đồng khác nhau, do đó, nó không phải là một "khốn tiêm"), bạn có thể sử dụng nó như là một phần của lớp Samurai của bạn:

public class Samurai 
{ 
    private readonly IWeapon _weapon; 

    public Samurai() : this(new DefaultWeaponProvider()) 
    { 
    } 

    public Samurai(IWeaponProvider provider) 
    { 
     _weapon = provider.GetWeapon(); 
    } 
} 
+0

Điều này trông giống như một sự thay thế thực sự thanh lịch.Tôi chỉ muốn hỏi thêm một câu hỏi nữa. – Tomas

+0

Bây giờ tôi phải hardcode ràng buộc của IWeaponProvider để DefaultWeaponProvider trong constructor của lớp Samurai. Bạn có thấy một cách làm thế nào ràng buộc này có thể được refactored ra một số module thành phần? Giống như với các thùng chứa IOC, bạn có một tệp cấu hình duy nhất mà bạn định nghĩa một cái gì đó như thế này "Bind (). Để ();" (- ví dụ từ Ninject). Ngay cả khi không có cách nào để làm điều đó, tôi sẽ chấp nhận câu trả lời của bạn. Chỉ muốn làm rõ ... Cảm ơn – Tomas

+0

chỉ cần làm rõ: bởi "quy ước trên cấu hình" bạn có nghĩa là bạn * vẫn * muốn cấu hình những giá trị mặc định này sẽ là gì? Nhưng có, bạn có thể thực hiện 'DefaultWeaponProvider' của bạn để liên kết' IWeapon' của nó với bất kỳ thứ gì thực sự, tức là dựa trên tệp cấu hình hoặc sử dụng nội bộ của thùng chứa IoC - nhưng lớp 'Samurai' của bạn sẽ không có bất kỳ phụ thuộc nào đó – BrokenGlass

1

Tôi đã sử dụng phương pháp tiếp cận sau trong dự án C# của mình. Mục tiêu là để đạt được tiêm phụ thuộc (cho đơn vị/thử nghiệm mô hình) trong khi không làm tê liệt việc thực hiện mã cho một "trường hợp sử dụng bình thường" (nghĩa là có một số lượng lớn() mới được xếp tầng thông qua luồng thực thi).

public sealed class QueueProcessor : IQueueProcessor 
{ 
    private IVbfInventory vbfInventory; 
    private IVbfRetryList vbfRetryList; 

    public QueueProcessor(IVbfInventory vbfInventory = null, IVbfRetryList vbfRetryList = null) 
    { 
     this.vbfInventory = vbfInventory ?? new VbfInventory(); 
     this.vbfRetryList = vbfRetryList ?? new VbfRetryList(); 
    } 
} 

Điều này cho phép DI nhưng cũng có nghĩa là bất kỳ người tiêu dùng nào cũng không phải lo lắng về "lưu lượng thể hiện mặc định".

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