Dần dần di chuyển codebase sang thùng chứa phụ thuộc


9

Tôi có một cơ sở mã lớn với rất nhiều singletons "chống mẫu", các lớp tiện ích với các phương thức tĩnh và các lớp tạo ra các phụ thuộc của riêng chúng bằng newtừ khóa. Nó làm cho một mã rất khó kiểm tra.

Tôi muốn chuyển dần mã sang vùng chứa phụ thuộc (trong trường hợp của tôi Guice, vì đó là một GWTdự án). Từ hiểu biết của tôi về tiêm phụ thuộc, đó là tất cả hoặc không có gì. Tất cả các lớp được quản lý bởi Spring / Guice hoặc không. Vì cơ sở mã lớn nên tôi không thể chuyển đổi mã qua đêm. Vì vậy, tôi cần một cách để làm điều đó dần dần.

Vấn đề là khi tôi bắt đầu với một lớp cần được chèn vào các lớp khác, tôi không thể sử dụng một đơn giản @Injecttrong các lớp đó, vì các lớp đó chưa được quản lý bởi container. Vì vậy, điều này tạo ra một chuỗi dài lên đến các lớp "hàng đầu" không được tiêm ở bất cứ đâu.

Cách duy nhất tôi thấy là làm cho một Injectorbối cảnh / ứng dụng có sẵn trên toàn cầu thông qua một đơn vị trong thời điểm hiện tại, để các lớp khác có thể nhận được một hạt đậu được quản lý từ nó. Nhưng nó mâu thuẫn với ý tưởng quan trọng của việc không tiết lộ composition rootcho ứng dụng.

Một cách tiếp cận khác sẽ là từ dưới lên: để bắt đầu với các lớp "cấp cao", đưa chúng vào thùng chứa phụ thuộc và từ từ chuyển xuống các lớp "nhỏ hơn". Nhưng sau đó tôi phải chờ rất lâu, vì tôi có thể kiểm tra những lớp nhỏ hơn mà vẫn phụ thuộc vào toàn cầu / thống kê.

Điều gì sẽ là cách để đạt được di cư dần dần như vậy?

PS Câu hỏi Cách tiếp cận dần dần để tiêm phụ thuộc tương tự trong tiêu đề, nhưng nó không trả lời câu hỏi của tôi.


1
Bạn có muốn đi thẳng từ việc có các lớp ghép cứng đến sử dụng một thùng chứa phụ thuộc không? Trước tiên, bạn phải thực hiện một số cấu trúc lại để tách lớp, sử dụng các giao diện trước khi nghĩ đến việc kết hôn với bất kỳ khuôn khổ hoặc công cụ tiêm phụ thuộc nào.
Tulains Córdova

Đủ công bằng. Vì vậy, tôi có một số lớp tiện ích A được sử dụng trong 30 lớp khác (một trong số chúng là lớp B), khiến chúng không thể kiểm chứng được. Tôi nghĩ để biến lớp A thành một phụ thuộc hàm tạo của lớp B. Tôi phải thay đổi tất cả các lệnh gọi đến hàm tạo và truyền A vào bên trong. Nhưng tôi nên lấy nó từ đâu?
damluar

Nếu B là đơn vị nhỏ nhất bạn có thể áp dụng DI để bắt đầu, thì tôi thấy không có hại gì khi xây dựng A ở bất cứ nơi nào bạn xây dựng B trong thời gian này - với điều kiện A không có phụ thuộc. Một khi quả bóng đang lăn, bạn có thể quay lại và tái cấu trúc nó.
Andy Hunt

@damluar Một nhà máy để bắt đầu?
Tulains Córdova

1
@damluar một lớp không trở nên không thể kiểm chứng chỉ vì nó dựa vào một lớp tiện ích. Nó chỉ có nghĩa là đơn vị của bạn lớn hơn một chút so với bạn muốn. Nếu bạn có thể tự kiểm tra lớp tiện ích của mình, thì bạn có thể sử dụng nó trong các bài kiểm tra cho tất cả 30 lớp khác mà không phải lo lắng. Đọc blog Martin Fowlers về thử nghiệm đơn vị.
gbjbaanb

Câu trả lời:


2

Xin lỗi C#là ngôn ngữ tôi chọn, tôi có thể đọc Javanhưng có lẽ sẽ bán thịt cú pháp cố gắng viết nó ... Các khái niệm tương tự được áp dụng giữa C#Javamặc dù vậy, hy vọng điều này sẽ chỉ ra các bước trong cách bạn có thể dần dần di chuyển cơ sở mã của mình thành nhiều hơn kiểm tra.

Được:

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = new Foo();
        foo.DoStuff();
    }
}

public class Foo
{

    public void DoStuff()
    {
        Bar bar = new Bar();
        bar.DoSomethingElse();
    }

}

public class Bar
{
    public void DoSomethingElse();
}

có thể dễ dàng được cấu trúc lại để sử dụng DI mà không cần sử dụng bộ chứa IOC - và bạn thậm chí có thể chia nó thành nhiều bước:

(tiềm năng) Bước một - thực hiện các phụ thuộc nhưng không thay đổi mã gọi (UI):

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = new Foo();
        foo.DoStuff();
    }
}

public class Foo
{

    private IBar _iBar;

    // Leaving this constructor for step one, 
    // so that calling code can stay as is without impact
    public Foo()
    {
        _iBar = new Bar();
    }

    // simply because we now have a constructor that take's in the implementation of the IBar dependency, 
    // Foo can be much more easily tested.
    public Foo(IBar iBar)
    {
        _iBar = iBar;
    }

    public void DoStuff()
    {
        _iBar.DoSomethingElse();
    }

}

public interface IBar
{
    void DoSomethingElse();
}

public class Bar
{
    public void DoSomethingElse();
}

Refactor 2 (hoặc refactor đầu tiên nếu triển khai bộ chứa IOC và thay đổi mã cuộc gọi ngay lập tức):

public class MyUI
{
    public void SomeMethod()
    {
        Foo foo = null // use your IOC container to resolve the dependency
        foo.DoStuff();
    }
}

public class Foo
{

    private IBar _iBar;

    // note we have now dropped the "default constructor" - this is now a breaking change as far as the UI is concerned.
    // You can either do this all at once (do only step 2) or in a gradual manner (step 1, then step 2)

    // Only entry into class - requires passing in of class dependencies (IBar)
    public Foo(IBar iBar)
    {
        _iBar = iBar;
    }

    public void DoStuff()
    {
        _iBar.DoSomethingElse();
    }

}

Bước 2 về mặt kỹ thuật có thể tự thực hiện - nhưng (có khả năng) sẽ làm được nhiều việc hơn - phụ thuộc vào số lượng lớp hiện đang "làm mới" chức năng mà bạn đang tìm kiếm DI.

Cân nhắc đi theo bước 1 -> bước 2 - bạn có thể tạo các bài kiểm tra đơn vị cho Foo, độc lập với Bar. Trong khi trước khi tái cấu trúc bước 1, nó không dễ thực hiện được nếu không sử dụng triển khai thực tế của cả hai lớp. Thực hiện bước 1 -> bước 2 (chứ không phải bước 2 ngay lập tức) sẽ cho phép các thay đổi nhỏ hơn theo thời gian và bạn sẽ bắt đầu khai thác thử nghiệm để đảm bảo tốt hơn công cụ tái cấu trúc của bạn hoạt động mà không có hậu quả.


0

Khái niệm này giống nhau cho dù bạn đang sử dụng Java, PHP hay thậm chí là C #. Câu hỏi này được Gemma Anible giải quyết khá tốt trong video YouTube này:

https://www.youtube.com/watch?v=Jccq_Ti8Lck (PHP, xin lỗi!)

Bạn thay thế mã không thể kiểm tra bằng "mặt tiền" (vì không có thuật ngữ tốt hơn) gọi mã mới, có thể kiểm tra được. Sau đó, dần dần bạn có thể thay thế các cuộc gọi cũ bằng các dịch vụ được tiêm. Tôi đã làm điều này trong quá khứ và nó hoạt động khá tốt.

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.