webentwicklung-frage-antwort-db.com.de

Vererbung und Abhängigkeitsinjektion

Ich habe eine Reihe von angle2-Komponenten, die alle einen Service erhalten sollten. Mein erster Gedanke war, dass es am besten wäre, eine Superklasse zu schaffen und den Service dort zu injizieren. Jede meiner Komponenten würde dann diese Superklasse erweitern, aber dieser Ansatz funktioniert nicht.

Vereinfachtes Beispiel:

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
  }
}

Ich könnte das lösen, indem ich MyService in jede einzelne Komponente einspeise und dieses Argument für den super() -Aufruf verwende, aber das ist definitiv eine Art absurd.

Wie organisiere ich meine Komponenten richtig, damit sie einen Service von der Superklasse erben?

71
maxhb

Die aktualisierte Lösung verhindert, dass mithilfe des globalen Injektors mehrere Instanzen von myService generiert werden.

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

Dadurch wird sichergestellt, dass MyService in jeder Klasse verwendet werden kann, die AbstractComponent erweitert, ohne dass MyService in jede abgeleitete Klasse eingefügt werden muss.

Diese Lösung hat einige Nachteile (siehe Kommentar von @ Günter Zöchbauer unter meiner ursprünglichen Frage):

  • Das Injizieren des Global Injectors ist nur dann eine Verbesserung, wenn mehrere verschiedene Services an vielen Stellen injiziert werden müssen. Wenn Sie nur einen gemeinsamen Dienst haben, ist es wahrscheinlich besser/einfacher, diesen Dienst in die abgeleiteten Klassen einzufügen.
  • Meine Lösung und seine vorgeschlagene Alternative haben beide den Nachteil, dass sie es schwieriger machen zu sehen, welche Klasse von welchem ​​Dienst abhängt.

Eine sehr gut geschriebene Erklärung zur Abhängigkeitsinjektion in Angular2 finden Sie in diesem Blog-Beitrag, der mir bei der Lösung des Problems sehr geholfen hat: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection- in-angular-2.html

52
maxhb

Ich könnte das lösen, indem ich MyService in jede einzelne Komponente einspeise und dieses Argument für den super () -Aufruf verwende, aber das ist definitiv eine Art absurd.

Das ist nicht absurd. So funktionieren Konstruktoren und Konstruktorinjektionen.

Jede injizierbare Klasse muss die Abhängigkeiten als Konstruktorparameter deklarieren. Wenn die Oberklasse auch Abhängigkeiten aufweist, müssen diese auch im Konstruktor der Unterklasse aufgelistet und mit dem Aufruf super(dep1, dep2) an die Oberklasse übergeben werden.

Die Weitergabe eines Injektors und der Erwerb von Abhängigkeiten hat zwingend schwerwiegende Nachteile.

Abhängigkeiten werden ausgeblendet, was das Lesen von Code erschwert.
Es verletzt die Erwartungen von jemandem, der mit der Funktionsweise von Angular2 DI vertraut ist.
Die Offline-Kompilierung, die statischen Code generiert, um deklarative und imperative DI zu ersetzen, um die Leistung zu verbessern und die Codegröße zu reduzieren, wird unterbrochen.

57

Anstatt alle Dienste manuell zu injizieren, habe ich eine Klasse erstellt, die die Dienste bereitstellt, z. B. werden die Dienste injiziert. Diese Klasse wird dann in die abgeleiteten Klassen injiziert und an die Basisklasse weitergeleitet.

Abgeleitete Klasse:

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

Basisklasse:

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

Service-Providing-Klasse:

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

Anstatt einen Service zu injizieren, der alle anderen Services als Abhängigkeiten enthält, gehen Sie wie folgt vor:

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
    }
}

Ich würde diesen zusätzlichen Schritt überspringen und einfach alle Services in der BaseComponent hinzufügen, wie folgt:

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

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

Diese Technik setzt 2 Dinge voraus:

  1. Ihr Anliegen bezieht sich ausschließlich auf die Vererbung von Komponenten. Der Grund, warum Sie auf diese Frage gestoßen sind, ist höchstwahrscheinlich die überwältigende Menge an nicht trockenem (WET?) Code, den Sie in jeder abgeleiteten Klasse wiederholen müssen. Wenn Sie die Vorteile eines einzigen Einstiegspunkts für alle Ihre Komponenten und Services nutzen möchten, müssen Sie den zusätzlichen Schritt ausführen.

  2. Jede Komponente erweitert das BaseComponent

Es gibt auch einen Nachteil, wenn Sie den Konstruktor einer abgeleiteten Klasse verwenden, da Sie super() aufrufen und alle Abhängigkeiten übergeben müssen. Obwohl ich keinen Anwendungsfall sehe, der die Verwendung von constructor anstelle von ngOnInit erfordert, ist es durchaus möglich, dass ein solcher Anwendungsfall vorliegt.

1
maximedupre

Wenn die übergeordnete Klasse vom Plug-In eines Drittanbieters stammt (und Sie die Quelle nicht ändern können), können Sie dies tun:

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

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

oder besser (bleib nur ein Parameter im Konstruktor):

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);
  }
}
0
dlnsk

Um von der Basisklasse zu erben, müssen Sie sie zunächst instanziieren. Um es zu instanziieren, müssen Sie dem Konstruktor die erforderlichen Parameter übergeben. Sie übergeben sie also von Kind zu Eltern durch einen super () -Aufruf, damit es Sinn macht. Injektor ist natürlich eine andere praktikable Lösung.

0
Ihor Bodnarchuk