development

생성자 또는 속성 설정자를 통한 종속성 주입?

big-blog 2020. 6. 17. 07:41
반응형

생성자 또는 속성 설정자를 통한 종속성 주입?


클래스를 리팩토링하고 새로운 의존성을 추가하고 있습니다. 클래스는 현재 생성자에서 기존 종속성을 사용하고 있습니다. 일관성을 위해 생성자에 매개 변수를 추가합니다.
물론, 몇 가지 서브 클래스와 단위 테스트를위한 더 많은 클래스가 있으므로 이제 모든 생성자를 변경하여 게임을 진행하고 있으며 시간이 오래 걸립니다.
setter와 함께 속성을 사용하는 것이 종속성을 얻는 더 좋은 방법이라고 생각합니다. 주입 된 의존성이 클래스의 인스턴스를 구성하는 인터페이스의 일부라고 생각하지 않습니다. 종속성을 추가하면 이제 모든 사용자 (서브 클래스 및 직접 인스턴스화하는 사람)가 갑자기이를 알게됩니다. 캡슐화가 깨지는 것 같습니다.

이것은 기존 코드의 패턴이 아닌 것 같으므로 생성자 대 속성의 장단점에 대한 일반적인 합의가 무엇인지 찾고 있습니다. 속성 설정기를 사용하는 것이 더 낫습니까?


글쎄, 그것은 :-)에 달려 있습니다.

클래스가 종속성없이 작업을 수행 할 수 없으면 생성자에 추가하십시오. 클래스 에는 새로운 종속성이 필요 하므로 변경 사항이 변경되기를 원합니다. 또한 완전히 초기화되지 않은 클래스 ( "2 단계 구성")를 만드는 것은 반 패턴 (IMHO)입니다.

클래스가 종속성없이 작동 할 수 있다면 setter가 좋습니다.


클래스의 사용자가되는 가정 주어진 클래스의 종속성에 대해 알고. 예를 들어 데이터베이스에 연결된 클래스가 있고 지속성 계층 종속성을 주입하는 수단을 제공하지 않은 경우 사용자는 데이터베이스에 대한 연결을 사용할 수 있어야한다는 것을 절대 알 수 없습니다. 그러나 생성자를 변경하면 지속성 계층에 대한 종속성이 있음을 사용자에게 알립니다.

또한 이전 생성자의 모든 사용을 변경하지 않으려면 이전 생성자와 새 생성자의 임시 브리지로 생성자 체인을 적용하기 만하면됩니다.

public class ClassExample
{
    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo)
        : this (dependnecyOne, dependencyTwo, new DependnecyThreeConcreteImpl())
    { }

    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo, IDependencyThree dependencyThree)
    {
        // Set the properties here.
    }
}

의존성 주입의 포인트 중 하나는 클래스가 갖는 의존성을 밝히는 것입니다. 클래스가 너무 많은 의존성을 가지고 있다면, 일부 리팩토링이 필요할 때가 있습니다 : 클래스의 모든 메소드가 모든 의존성을 사용합니까? 그렇지 않다면, 그것은 수업이 어디로 나뉘어 질 수 있는지를보기에 좋은 출발점입니다.


물론 생성자를 착용하면 한 번에 모두 확인할 수 있습니다. 사물을 읽기 전용 필드에 할당하면 생성 시점부터 개체의 종속성에 대해 약간의 보증이 제공됩니다.

새로운 의존성을 추가하는 것은 큰 고통이지만 적어도 이런 식으로 컴파일러는 정확해질 때까지 계속 불평합니다. 좋은 것 같아요.


많은 수의 선택적 종속성 (이미 냄새 임)이있는 경우 아마도 setter injection이 좋습니다. 생성자 주입은 의존성을 더 잘 드러냅니다.


일반적으로 선호되는 방법은 생성자 주입을 가능한 많이 사용하는 것입니다.

생성자 주입은 객체가 올바르게 작동하기 위해 필요한 종속성이 무엇인지 정확하게 나타냅니다. 일부 종속성이 설정되지 않았기 때문에 객체를 새로 고치고 메소드를 호출 할 때 충돌하는 것보다 더 성가신 것은 없습니다. 생성자가 반환 한 객체는 작동 상태에 있어야합니다.

생성자를 하나만 사용하면 디자인이 단순하고 모호함을 피할 수 있습니다 (인간이 아닌 경우 DI 컨테이너의 경우).

Mark Seemann 이 자신의 저서 ".NET에 의존성 주입 (Dependency Injection in .NET)"에서 로컬 기본값호출 할 때 특성 주입을 사용할 수 있습니다 . 종속성은 선택 사항입니다. 제대로 작동하는 구현을 제공 할 수 있지만 호출자가 다른 경우를 지정할 수 있기를 바랍니다. 필요합니다.

(아래의 이전 답변)


주입이 필수 인 경우 생성자 주입이 더 좋다고 생각합니다. 생성자가 너무 많으면 생성자 대신 팩토리를 사용하는 것이 좋습니다.

주입이 선택 사항이거나 반쯤 통과 시키려면 세터 주입이 좋습니다. 나는 일반적으로 세터를 좋아하지 않지만 맛의 문제입니다.


그것은 개인적인 취향의 문제입니다. 개인적으로 나는 setter 주입을 선호하는 경향이 있습니다. 왜냐하면 런타임에 구현을 대체 할 수있는 방식으로 더 많은 유연성을 제공하기 때문입니다. 또한 많은 인수를 가진 생성자는 내 의견으로는 깨끗하지 않으며 생성자에서 제공되는 인수는 비 선택적 인수로 제한되어야합니다.

클래스 인터페이스 (API)가 작업을 수행하는 데 필요한 것이 명확하다면, 좋습니다.


나는 개인적으로 생성자에 의존성을 주입 하는 것보다 추출 및 재정의 "패턴"을 선호합니다 . 주로 귀하의 질문에 요약 된 이유 때문입니다. 속성을로 설정 virtual한 다음 파생 된 테스트 가능 클래스에서 구현을 재정의 할 수 있습니다 .


클래스의 종속성 요구 사항을 "강화"하는 데 도움이되므로 생성자 삽입을 선호합니다. 그것이 c'tor에 있다면 소비자 앱이 컴파일되도록 객체를 설정해야합니다. setter injection을 사용하면 런타임까지 문제가 있음을 알지 못할 수 있으며 개체에 따라 런타임에 늦을 수 있습니다.

주입 된 객체가 초기화와 같이 많은 작업 자체가 필요할 때 때때로 setter injection을 사용합니다.


이것이 가장 논리적으로 보이기 때문에 생성자 주입을 사용합니다. 그것의 내 수업을 말하는 등 필요로 그 일을하기 위해 이러한 종속성을. 선택적인 의존성이라면 속성이 합리적으로 보입니다.

또한 컨테이너를 사용하여 만든 발표자의 ASP.NET View와 같은 컨테이너에 참조가없는 것을 설정하기 위해 속성 삽입을 사용합니다.

나는 그것이 캡슐화를 깨뜨릴 것이라고 생각하지 않는다. 내부 작업은 내부적으로 유지되어야하며 종속성은 다른 문제를 처리합니다.


고려해야 할 한 가지 옵션은 간단한 단일 종속성으로 복잡한 다중 종속성을 구성하는 것입니다. 즉, 복합 종속성에 대한 추가 클래스를 정의하십시오. 이렇게하면 WRT 생성자 주입이 훨씬 쉬워집니다 (호출 당 더 적은 매개 변수).

Of course it makes most sense if there's some kind of logical grouping of dependencies, so the compound is more than an arbitrary aggregate, and it makes most sense if there are multiple dependents for a single compound dependency - but the parameter block "pattern" has been around for a long time, and most of those that I've seen have been pretty arbitrary.

Personally, though, I'm more a fan of using methods/property-setters to specify dependencies, options etc. The call names help describe what is going on. It's a good idea to provide example this-is-how-to-set-it-up snippets, though, and make sure the dependent class does enough error checks. You might want to use a finite state model for the setup.


I recently ran into a situation where I had multiple dependencies in a class, but only one of the dependencies was necessarily going to change in each implementation. Since the data access and error logging dependencies would likely only be changed for testing purposes, I added optional parameters for those dependencies and provided default implementations of those dependencies in my constructor code. In this way, the class maintains its default behavior unless overridden by the consumer of the class.

Using optional parameters can only be accomplished in frameworks that support them, such as .NET 4 (for both C# and VB.NET, though VB.NET has always had them). Of course, you can accomplish similar functionality by simply using a property that can be reassigned by the consumer of your class, but you don't get the advantage of immutability provided by having a private interface object assigned to a parameter of the constructor.

All of this being said, if you are introducing a new dependency that must be provided by every consumer, you're going to have to refactor your constructor and all code that consumers your class. My suggestions above really only apply if you have the luxury of being able to provide a default implementation for all of your current code but still provide the ability to override the default implementation if necessary.


This is an old post, but if it is needed in future maybe this is of any use:

https://github.com/omegamit6zeichen/prinject

I had a similar idea and came up with this framework. It is probably far from complete, but it is an idea of a framework focusing on property injection


It depends on how you want to implement. I prefer constructor injection wherever I feel the values that go in to the implementation doesnt change often. Eg: If the compnay stragtegy is go with oracle server, I will configure my datsource values for a bean achiveing connections via constructor injection. Else, if my app is a product and chances it can connect to any db of the customer , I would implement such db configuration and multi brand implementation through setter injection. I have just taken an example but there are better ways of implementing the scenarios I mentioned above.


Constructor injection does explicitly reveal the dependencies, making code more readable and less prone to unhandled run-time errors if arguments are checked in the constructor, but it really does come down to personal opinion, and the more you use DI the more you'll tend to sway back and forth one way or the other depending on the project. I personally have issues with code smells like constructors with a long list of arguments, and I feel that the consumer of an object should know the dependencies in order to use the object anyway, so this makes a case for using property injection. I don't like the implicit nature of property injection, but I find it more elegant, resulting in cleaner-looking code. But on the other hand, constructor injection does offer a higher degree of encapsulation, and in my experience I try to avoid default constructors, as they can have an ill effect on the integrity of the encapsulated data if one is not careful.

Choose injection by constructor or by property wisely based on your specific scenario. And don't feel that you have to use DI just because it seems necessary and it will prevent bad design and code smells. Sometimes it's not worth the effort to use a pattern if the effort and complexity outweighs the benefit. Keep it simple.

참고URL : https://stackoverflow.com/questions/1503584/dependency-injection-through-constructors-or-property-setters

반응형