점차 의존성 주입 컨테이너로 코드베이스 이동


9

많은 "반 패턴"싱글 톤, 정적 메소드가있는 유틸리티 클래스 및 new키워드를 사용하여 자체 종속성을 작성하는 클래스가있는 대형 코드베이스가 있습니다. 코드를 테스트하기가 매우 어렵습니다.

점차 의존성 주입 컨테이너로 코드를 점차 마이그레이션하고 싶습니다 (제 경우 Guice에는 GWT프로젝트 이므로 ). 의존성 주입에 대한 나의 이해에서, 그것은 전부 또는 아무것도 아닙니다. 모든 클래스는 Spring / Guice에서 관리하거나 전혀 관리하지 않습니다. 코드베이스가 크기 때문에 밤새 코드를 변환 할 수 없습니다. 그래서 점차적으로 할 방법이 필요합니다.

문제는 다른 클래스에 주입 해야하는 클래스로 시작할 때 @Inject해당 클래스가 컨테이너에서 아직 관리되지 않기 때문에 해당 클래스에서 단순 을 사용할 수 없다는 것 입니다. 따라서 이것은 어디에도 주입되지 않은 "최상급"클래스까지 긴 체인을 만듭니다.

내가 볼 수있는 유일한 방법 Injector은 당분간 싱글 톤을 통해 / 응용 프로그램 컨텍스트를 전역 적으로 사용할 수 있도록하여 다른 클래스가 관리되는 Bean을 얻을 수 있도록하는 것입니다. 그러나 composition root응용 프로그램을 공개하지 않는 중요한 아이디어와 모순 됩니다.

또 다른 접근법은 상향식입니다. "고수준"클래스로 시작하여 종속성 주입 컨테이너에 포함시키고 천천히 "더 작은"클래스로 이동하십시오. 그러나 여전히 전역 / 정적에 의존하는 작은 클래스를 테스트 할 수 있기 때문에 오랜 시간 기다려야합니다.

이러한 점진적인 마이그레이션을 달성하는 방법은 무엇입니까?

추신 : 의존성 주입에 대한 점진적 접근 방식 은 제목과 유사하지만 내 질문에 대답하지 않습니다.


1
딱딱한 클래스를 갖는 것에서 의존성 주입 컨테이너를 사용하는 것으로 직접 가고 싶습니까? 의존성 주입 프레임 워크 또는 도구와의 결혼을 생각하기 전에 인터페이스를 사용하여 클래스를 분리하기 위해 리팩토링을 먼저해야합니다.
Tulains Córdova

그럴 수 있지. 그래서 30 개의 다른 클래스 (그중 하나는 클래스 B입니다)에서 사용되는 유틸리티 클래스 A가있어 테스트 할 수 없습니다. 클래스 A를 클래스 B의 생성자 종속성으로 만들려고 생각했습니다. 생성자에 대한 모든 호출을 변경하고 A를 내부로 전달해야합니다. 하지만 어디서 구해야합니까?
damluar

B가 시작하기 위해 DI를 적용 할 수있는 가장 작은 단위 인 경우 A가 종속성이없는 한 B를 구성하는 시간마다 A를 구성하는 데 아무런 해가 없습니다. 공이 구르면 돌아와서 리팩터링 할 수 있습니다.
Andy Hunt

@damluar 공장은 시작할까요?
Tulains Córdova

1
@damluar 클래스는 유틸리티 클래스에 의존하기 때문에 테스트 할 수 없습니다. 그것은 단지 당신의 단위가 당신이 원하는 것보다 약간 더 크다는 것을 의미합니다. 유틸리티 클래스 자체를 테스트 할 수 있으면 다른 모든 30 클래스의 테스트에서 걱정없이 사용할 수 있습니다. 단위 테스트에 대한 Martin Fowlers 블로그를 읽으십시오.
gbjbaanb

답변:


2

죄송 C#선택의 나의 언어, 내가 읽을 수 Java있지만, 아마도 그것을 쓰기를 시도하는 구문을 오거라 ... 같은 개념 사이에 적용 C#하고 Java있지만, 그래서 희망이 당신이 점점 더 될 당신의 코드베이스를 이동할 수있는 방법의 단계를 보여줍니다 테스트 가능.

주어진:

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();
}

IOC 컨테이너를 사용하지 않고 DI를 사용하도록 쉽게 리팩토링 할 수 있으며, 여러 단계로 나눌 수도 있습니다.

(잠재적) 1 단계-종속성을 취하지 만 호출 (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();
}

리 팩터 2 (또는 IOC 컨테이너를 구현하고 호출 코드를 즉시 변경하는 경우 첫 번째 리팩터링) :

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();
    }

}

2 단계는 기술적으로 자체적으로 수행 될 수 있지만 현재 잠재적으로 DI에 찾고있는 기능을 "새로 고치는"클래스 수에 따라 훨씬 더 많은 작업이 될 것입니다.

1 단계-> 2 단계 라우트를 고려해보십시오 .에 대해 Foo독립적으로 단위 테스트를 만들 수 있습니다 Bar. 1 단계 리팩토링 이전에 두 클래스의 실제 구현을 사용하지 않으면 쉽게 달성 할 수 없었습니다. 1 단계-> 2 단계 (즉시 2 단계가 아닌)를 수행하면 시간이 지남에 따라 더 작은 변화가 허용되며 결과없이 리 팩터가 더 잘 작동하도록 테스트 하네스를 이미 시작할 수 있습니다.


0

Java, PHP 또는 C #을 사용하든 개념은 동일합니다. 이 질문은이 YouTube 비디오에서 Gemma Anible이 상당히 잘 다루고 있습니다.

https://www.youtube.com/watch?v=Jccq_Ti8Lck (PHP, 죄송합니다!)

테스트 할 수없는 코드를 테스트 가능한 새로운 코드를 호출하는 "외관"(더 나은 용어가 없음)으로 바꿉니다. 그런 다음 점차적으로 이전 통화를 주입 된 서비스로 교체 할 수 있습니다. 나는 과거에 이것을했고 꽤 잘 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.