[inheritance] 상속 및 종속성 주입

모든 서비스가 주입되어야하는 angular2 구성 요소 집합이 있습니다. 첫 번째 생각은 수퍼 클래스를 만들고 거기에 서비스를 주입하는 것이 가장 좋을 것이라고 생각했습니다. 내 구성 요소 중 하나가 해당 수퍼 클래스를 확장하지만이 접근 방식은 작동하지 않습니다.

단순화 된 예 :

export class AbstractComponent {
  constructor(private myservice: MyService) {
    // Inject the service I need for all components
  }
}

export MyComponent extends AbstractComponent {
  constructor(private anotherService: AnotherService) {
    super(); // This gives an error as super constructor needs an argument
  }
}

나는 MyService각각의 모든 구성 요소 내에 주입하여이 문제를 해결할 수 있고 그 인수를 super()호출에 사용할 수 있지만 그것은 분명히 일종의 터무니없는 일입니다.

수퍼 클래스에서 서비스를 상속하도록 구성 요소를 올바르게 구성하는 방법은 무엇입니까?



답변

각각의 모든 구성 요소 내에 MyService를 삽입하고 해당 인수를 super () 호출에 사용하여이 문제를 해결할 수 있지만 이는 분명히 일종의 터무니없는 일입니다.

터무니없는 일이 아닙니다. 이것이 생성자와 생성자 주입이 작동하는 방식입니다.

모든 주입 가능한 클래스는 종속성을 생성자 매개 변수로 선언해야하며 수퍼 클래스에도 종속성이있는 경우 하위 클래스의 생성자에도 나열되어야하며 super(dep1, dep2)호출 과 함께 수퍼 클래스에 전달되어야합니다 .

인젝터를 우회하고 종속성을 획득하는 것은 심각한 단점이 있습니다.

코드를 읽기 어렵게 만드는 종속성을 숨 깁니다.
Angular2 DI 작동 방식에 익숙한 사람의 기대에 어긋납니다.
선언적 및 명령 적 DI를 대체하는 정적 코드를 생성하는 오프라인 컴파일을 중단하여 성능을 개선하고 코드 크기를 줄입니다.


답변

업데이트 된 솔루션은 전역 인젝터를 사용하여 myService의 여러 인스턴스가 생성되는 것을 방지합니다.

import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class AbstractComponent {
  protected myServiceA:MyServiceA;
  protected myServiceB:MyServiceB;
  protected myServiceC:MyServiceC;

  constructor(injector: Injector) {
    this.settingsServiceA = injector.get(MyServiceA);
    this.settingsServiceB = injector.get(MyServiceB);
    this.settingsServiceB = injector.get(MyServiceC);
  }
}

export MyComponent extends AbstractComponent {
  constructor(
    private anotherService: AnotherService,
    injector: Injector
  ) {
    super(injector);

    this.myServiceA.JustCallSomeMethod();
    this.myServiceB.JustCallAnotherMethod();
    this.myServiceC.JustOneMoreMethod();
  }
}

이렇게하면 모든 파생 클래스에 MyService를 주입 할 필요없이 AbstractComponent를 확장하는 모든 클래스 내에서 MyService를 사용할 수 있습니다.

이 솔루션에는 몇 가지 단점이 있습니다 (원래 질문 아래 @ Günter Zöchbauer의 Ccomment 참조).

  • 글로벌 인젝터 주입은 여러 곳에 주입해야하는 여러 서비스가있을 때만 개선됩니다. 공유 서비스가 하나만있는 경우 파생 클래스 내에 해당 서비스를 삽입하는 것이 더 낫거나 더 쉬울 것입니다.
  • 내 솔루션과 제안 된 대안은 어떤 클래스가 어떤 서비스에 의존하는지 확인하기가 더 어렵다는 단점이 있습니다.

: Angular2의 의존성 주입의 매우 잘 쓰여진 설명은 크게 문제를 해결하기 위해 나에게 도움이 블로그 게시물을 참조 http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-을 2.html


답변

모든 서비스를 수동으로 주입하는 대신 서비스를 제공하는 클래스를 만들었습니다. 예를 들어 서비스가 주입되었습니다. 그런 다음이 클래스는 파생 클래스에 주입되고 기본 클래스로 전달됩니다.

파생 클래스 :

@Component({
    ...
    providers: [ProviderService]
})
export class DerivedComponent extends BaseComponent {
    constructor(protected providerService: ProviderService) {
        super(providerService);
    }
}

기본 클래스 :

export class BaseComponent {
    constructor(protected providerService: ProviderService) {
        // do something with providerService
    }
}

서비스 제공 클래스 :

@Injectable()
export class ProviderService {
    constructor(private _apiService: ApiService, private _authService: AuthService) {
    }
}


답변

다음과 같이 다른 모든 서비스를 종속성으로 포함하는 서비스를 삽입하는 대신 :

class ProviderService {
    constructor(private service1: Service1, private service2: Service2) {}
}

class BaseComponent {
    constructor(protected providerService: ProviderService) {}

    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

이 추가 단계를 건너 뛰고 BaseComponent에 모든 서비스를 다음과 같이 삽입하기 만하면됩니다.

class BaseComponent {
    constructor(protected service1: Service1, protected service2: Service2) {}
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        this.service1;
        this.service2;
    }
}

이 기술은 다음 두 가지를 가정합니다.

  1. 귀하의 우려는 전적으로 구성 요소 상속과 관련이 있습니다. 대부분의 경우이 질문에 도달 한 이유는 각 파생 클래스에서 반복해야하는 비 건조 (WET?) 코드의 양이 너무 많기 때문입니다. 모든 구성 요소 및 서비스에 대해 단일 진입 점의 이점 을 얻으려면 추가 단계를 수행해야합니다.

  2. 모든 구성 요소는 BaseComponent

파생 클래스의 생성자를 사용하기로 결정하면 super()모든 종속성 을 호출 하고 전달 해야하므로 단점도 있습니다 . constructor대신을 사용해야하는 사용 사례를 실제로 보지는 못했지만 ngOnInit그러한 사용 사례가 존재할 가능성은 전적으로 가능합니다.


답변

상위 클래스가 타사 플러그인에서 가져온 경우 (소스를 변경할 수 없음) 다음을 수행 할 수 있습니다.

import { Injector } from '@angular/core';

export MyComponent extends AbstractComponent {
  constructor(
    protected injector: Injector,
    private anotherService: AnotherService
  ) {
    super(injector.get(MyService));
  }
}

또는 가장 좋은 방법 (생성자에서 하나의 매개 변수 만 유지) :

import { Injector } from '@angular/core';

export MyComponent extends AbstractComponent {
  private anotherService: AnotherService;

  constructor(
    protected injector: Injector
  ) {
    super(injector.get(MyService));
    this.anotherService = injector.get(AnotherService);
  }
}


답변

기본 클래스에서 상속하려면 먼저 인스턴스화해야합니다. 인스턴스화하려면 생성자 필수 매개 변수를 전달해야합니다. 따라서 super () 호출을 통해 하위에서 상위로 전달하므로 의미가 있습니다. 물론 인젝터는 또 다른 실행 가능한 솔루션입니다.


답변

못생긴 해킹

얼마 전에 내 고객 중 일부가 어제 두 개의 BIG 앵귤러 프로젝트에 참여하기를 원합니다 (앵귤러 v4에서 앵귤러 v8로). Project v4는 각 구성 요소에 대해 BaseView 클래스를 사용 tr(key)하며 번역 방법을 포함합니다 (v8에서는 ng-translate를 사용합니다). 따라서 번역 시스템 전환을 피하고 수백 개의 템플릿 (v4에서)을 편집하거나 2 개의 번역 시스템을 병렬로 설정하기 위해 추악한 해킹 (자랑스럽지 않음)을 사용합니다. AppModule수업 중에 다음 생성자를 추가합니다.

export class AppModule {
    constructor(private injector: Injector) {
        window['UglyHackInjector'] = this.injector;
    }
}

이제 AbstractComponent사용할 수 있습니다

export class AbstractComponent {
  private myservice: MyService = null;

  constructor() {
    this.myservice = window['UglyHackInjector'].get(MyService);
  }
}