programing

의존성 주입 생성자의 광기를 방지하는 방법

shortcode 2022. 10. 26. 22:43
반응형

의존성 주입 생성자의 광기를 방지하는 방법

제 컨스트럭터들은 이렇게 보이기 시작했습니다.

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

파라미터 리스트가 계속 증가하고 있습니다."컨테이너"는 종속성 주입 컨테이너이기 때문에 이 작업을 수행할 수 없습니다.

public MyClass(Container con)

모든 수업에서요?단점은 무엇입니까?이렇게 하면 마치 미화된 스태틱을 사용하는 것 같아요.IOC와 의존성 주입 광기에 대한 생각을 공유해 주세요.

컨테이너를 서비스 로케이터로 사용한다면 거의 정적인 공장이라고 할 수 있습니다.여러 가지 이유로 나는 이것을 반패턴이라고 생각한다(내 책에서 발췌한 것도 참조).

컨스트럭터 주입의 훌륭한 이점 중 하나는 단일 책임 원칙의 위반을 명백하게 만든다는 것입니다.

그런 일이 일어나면, Passide Services에 리팩터링할 때입니다.즉, 현재 필요한 일부 또는 모든 세부 종속성 간의 상호작용을 숨기는 보다 세밀한 인터페이스를 새로 만듭니다.

당신의 클래스 컨스트럭터들은 당신의 IOC 컨테이너 기간을 언급해서는 안 된다고 생각합니다.이는 클래스와 컨테이너 간의 불필요한 종속성(IOC가 피하려고 하는 종속성 유형!)을 나타냅니다.

매개 변수 전달의 어려움은 문제가 아닙니다.문제는 당신의 수업이 너무 많은 것을 하고 있고, 더 세분화되어야 한다는 것입니다.

종속성 주입은 특히 모든 종속성을 통과해야 하는 고통이 증가하고 있기 때문에 클래스가 너무 커지면 조기 경고로 작용할 수 있습니다.

저는 컨스트럭터 기반의 의존성 주입과 모든 의존성 전달이 얼마나 복잡해졌는지에 대해 비슷한 질문을 받았습니다.

지금까지 사용한 접근법 중 하나는 서비스 레이어를 사용하여 애플리케이션 전면 패턴을 사용하는 것입니다.이것은 조잡한 API를 가지고 있습니다.이 서비스가 저장소에 종속된 경우 개인 속성의 세터 주입을 사용합니다.이를 위해서는 추상 팩토리를 생성하고 저장소를 생성하는 로직을 팩토리로 이동해야 합니다.

자세한 코드는 이쪽에서 확인할 수 있습니다.

복잡한 서비스 계층의 IoC에 대한 베스트 프랙티스

문제:

1) 파라미터 리스트가 계속 증가하는 컨스트럭터.

2) 상속된 (예: 2)RepositoryBase컨스트럭터 시그니처를 변경하면 파생 클래스가 변경됩니다.

솔루션 1

IoC Container

왜죠

  • 파라미터 리스트는 더 이상 증가하지 않는다.
  • 컨스트럭터의 서명이 간단해지다

그거 좋지

  • IoC 컨테이너와 긴밀하게 연결된 클래스를 만듭니다. (1. 다른 IoC 컨테이너를 사용하는 다른 프로젝트에서 해당 클래스를 사용하려는 경우 문제가 발생합니다. 2. IOC 컨테이너를 변경하기로 결정함)
  • 클래스 설명에 도움이 되지 않습니다.(클래스 생성자를 보고 기능하기 위해 무엇이 필요한지 말할없습니다.
  • 클래스는 잠재적으로 모든 서비스에 액세스할 수 있습니다.

솔루션 2

모든 서비스를 그룹화하는 클래스를 만들어 생성자에게 전달합니다.

 public abstract class EFRepositoryBase 
 {
    public class Dependency
    {
        public DbContext DbContext { get; }
        public IAuditFactory AuditFactory { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
        }
    }

    protected readonly DbContext DbContext;        
    protected readonly IJobariaAuditFactory auditFactory;

    protected EFRepositoryBase(Dependency dependency)
    {
        DbContext = dependency.DbContext;
        auditFactory= dependency.JobariaAuditFactory;
    }
  }

파생 클래스

  public class ApplicationEfRepository : EFRepositoryBase      
  {
     public new class Dependency : EFRepositoryBase.Dependency
     {
         public IConcreteDependency ConcreteDependency { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory,
            IConcreteDependency concreteDependency)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
            ConcreteDependency = concreteDependency;
        }
     }

      IConcreteDependency _concreteDependency;

      public ApplicationEfRepository(
          Dependency dependency)
          : base(dependency)
      { 
        _concreteDependency = dependency.ConcreteDependency;
      }
   }

왜죠

  • 클래스에 새 종속성을 추가해도 파생 클래스에 영향을 주지 않습니다.
  • 클래스는 IoC 컨테이너에 의존하지 않습니다.
  • 클래스는 (의존 관계 측면에서) 설명이 됩니다. 어떤 A에 따라서는, 그 되어 있습니다.A.Dependency
  • 생성자 서명이 단순해집니다.

그거 좋지

  • 추가 클래스 생성 필요
  • 서비스 등록이 복잡해진다(매회 개별적으로 등록 필요)
  • 으로 패스하는 .IoC Container
  • ..

솔루션 2는 미완성일 뿐이지만, 이에 대한 확실한 반론이 있다면 설명적인 코멘트를 주시면 감사하겠습니다.

저는 이 글을 두 번 읽었습니다.저는 사람들이 물어보는 것이 아니라 아는 것으로 반응한다고 생각합니다.

JP의 원래 질문은 리졸버를 보내고 여러 개의 클래스를 보내서 오브젝트를 구축하는 것처럼 보이지만, 우리는 그 클래스/오브젝트 자체가 서비스이며, 주입할 수 있는 상태가 되었다고 가정합니다.만약 그렇지 않다면?

JP, DI를 활용하여 컨텍스트 데이터와 혼합 주입의 영광을 얻고 싶다면 이러한 패턴(또는 "반(反) 패턴"이라고 생각됨) 중 어느 것도 특별히 다루지 않습니다.그것은 사실상 그러한 노력을 지원하는 패키지를 사용하는 것으로 요약된다.

Container.GetSevice<MyClass>(someObject1, someObject2)

...이 형식은 거의 지원되지 않습니다.이러한 서포트의 프로그래밍의 어려움과 도입에 수반하는 비참한 퍼포먼스가 더해지기 때문에, 오픈 소스 개발자에게는 매력적이지 않다고 생각합니다.

하지만 My Class의 공장을 만들고 등록할 수 있어야 하며, 데이터 전달을 위해 "서비스"에 강요되지 않는 데이터/입력도 받을 수 있어야 합니다.「반패턴」이 부정적인 결과인 경우, 데이터/모델을 전달하기 위해서 인위적인 서비스 타입의 존재를 강요하는 것은, 확실히 부정적입니다(클래스를 컨테이너에 정리하는 것에 대한 느낌과 같음).같은 본능이 적용됩니다).

조금 추악해 보이더라도 도움이 될 수 있는 틀이 있습니다.예를 들어 Ninject:

생성자에서 추가 매개 변수를 사용하여 Ninject를 사용하여 인스턴스 만들기

그거에요.NET은 인기가 있고, 아직 그 어느 곳에서도 필요한 만큼 깨끗하지 않지만, 어떤 언어를 사용하든 분명 뭔가가 있을 것입니다.

용기를 주입하는 것은 결국 후회하게 될 지름길이다.

과잉 투입은 문제가 아닙니다.일반적으로 다른 구조적 결함의 증상이며, 특히 우려의 분리가 가장 두드러집니다.이것은 하나의 문제가 아니라 많은 원인이 있을 수 있으며, 이것을 고치는 것이 매우 어려운 것은 때때로 모든 문제를 동시에 처리해야 한다는 것입니다(스파게티를 푸는 것을 생각해 보세요).

여기 주의해야 할 사항의 불완전한 목록이 있습니다.

부실한 도메인 설계(루트 집약 등)

관심사(서비스 구성, 명령어, 쿼리)를 제대로 구분하지 못했습니다. "CQRS 및 이벤트 소싱"을 참조하십시오.

또는 Mappers(주의: 문제가 발생할 수 있습니다)

모델 및 기타 DTO 표시(재사용하지 말고 최소한으로 유지하세요!!!!)

이것이 내가 사용하는 접근법이다.

public class Hero
{

    [Inject]
    private IInventory Inventory { get; set; }

    [Inject]
    private IArmour Armour { get; set; }

    [Inject]
    protected IWeapon Weapon { get; set; }

    [Inject]
    private IAction Jump { get; set; }

    [Inject]
    private IInstanceProvider InstanceProvider { get; set; }


}

다음은 주입을 수행하고 값을 주입한 후 생성자를 실행하는 방법에 대한 대략적인 접근법은 다음과 같습니다.이것은 완전히 기능하는 프로그램입니다.

public class InjectAttribute : Attribute
{

}


public class TestClass
{
    [Inject]
    private SomeDependency sd { get; set; }

    public TestClass()
    {
        Console.WriteLine("ctor");
        Console.WriteLine(sd);
    }
}

public class SomeDependency
{

}


class Program
{
    static void Main(string[] args)
    {
        object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));

        // Get all properties with inject tag
        List<PropertyInfo> pi = typeof(TestClass)
            .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
            .Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();

        // We now happen to know there's only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
        pi[0].SetValue(tc, new SomeDependency(), null);


        // Find the right constructor and Invoke it. 
        ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
        ci.Invoke(tc, null);

    }
}

저는 현재 https://github.com/Jokine/ToolProject/tree/Core과 같은 취미 프로젝트를 진행하고 있습니다.

어떤 의존관계 주입 프레임워크를 사용하고 있습니까?대신 세터 기반 주입을 사용해 본 적이 있습니까?

컨스트럭터 기반 주입의 장점은 DI 프레임워크를 사용하지 않는 Java 프로그래머에게 자연스러운 것처럼 보인다는 것입니다.클래스를 초기화하려면 5가지가 필요하며, 컨스트럭터에는 5개의 인수가 있습니다.단점은 당신이 알고 있는 것, 당신이 많은 의존관계를 가지고 있을 때 다루기 어려워진다는 것입니다.

Spring을 사용하면 필요한 값을 세터로 전달할 수 있으며 @required 주석을 사용하여 주입할 수 있습니다.단점은 초기화 코드를 컨스트럭터에서 다른 메서드로 이동하여 모든 의존관계가 삽입된 후 @PostConstruct로 마킹하여 Spring 호출을 해야 한다는 것입니다.다른 틀은 잘 모르겠지만 비슷한 일을 하는 것 같아요.

어느 쪽이든, 그것은 선호도의 문제이다.

언급URL : https://stackoverflow.com/questions/2420193/how-to-avoid-dependency-injection-constructor-madness

반응형