Freigeben über


ServiceScope class

Das vom SharePoint-Framework verwendete Dienstlocatormuster.

Hinweise

ServiceScope bietet eine formalisierte Möglichkeit für Komponenten, Abhängigkeiten ("Dienste") zu registrieren und zu nutzen und zu ermöglichen, dass verschiedene Implementierungen in verschiedenen Bereichen registriert werden können. Dies verbessert die Modularität, indem Komponenten von ihren Abhängigkeiten auf erweiterbare Weise entkoppelt werden.

Angenommen, verschiedene Komponenten benötigen Zugriff auf eine IPageManager-instance. Wir könnten den PageManager einfach zu einem Singleton (d. h. einer globalen Variablen) machen, aber dies funktioniert nicht, wenn wir z. B. ein Popupdialogfeld erstellen müssen, für das ein zweiter PageManager-instance erforderlich ist. Eine bessere Lösung wäre, den PageManager als Konstruktorparameter für jede Komponente hinzuzufügen, die ihn benötigt. Dann tritt jedoch sofort das Problem auf, dass jeder Code, der diese Konstruktoren aufruft, auch einen PageManager-Parameter benötigt. In einer Anwendung mit vielen dieser Abhängigkeiten würde geschäftslogik, die viele Subsysteme miteinander verbindet, schließlich einen Konstruktorparameter für jede mögliche Abhängigkeit auswählen, was umständlich ist. Eine natürliche Lösung wäre, alle Abhängigkeiten in eine Klasse mit dem Namen "ApplicationContext" zu verschieben und diesen dann als Konstruktorparameter zu übergeben. Dadurch kann der PageManager an Klassen übergeben werden, die ihn benötigen, ohne die zwischengeschalteten Klassen zu überladen, die dies nicht der Fall sind. Es besteht jedoch weiterhin ein Entwurfsproblem, dass "ApplicationContext" hartcodierte Abhängigkeiten von vielen nicht zusammenhängenden Dingen aufweist. Ein flexiblerer Ansatz besteht darin, es zu einem Wörterbuch zu machen, das Elemente für Verbraucher/Anbieter nachschlagen kann, die den richtigen Nachschlageschlüssel (d. h. ServiceKey) kennen. Dies ist das beliebte "Dienstlocator"-Entwurfsmuster, das aus der SPContext-API im klassischen SharePoint bekannt ist.

ServiceScope geht diese Idee auf zwei wichtige Arten weiter: Erstens bietet es einen Bereichsmechanismus, sodass z. B. wenn wir zwei verschiedene Seiten haben, diese jeweils einen eindeutigen PageManager-instance bereitstellen können, während andere gängige Abhängigkeiten weiterhin gemeinsam verwendet werden. Zweitens ermöglicht es, dass ein ServiceKey eine Standardimplementierung der Abhängigkeit bereitstellt. Dies ist wichtig für die API-Stabilität in unserer modularen clientseitigen Umgebung: Angenommen, mit Version 2.0 unserer Anwendung wurde eine neue IDiagnosticTracing-Schnittstelle eingeführt, die von einer Komponente der Version 2.0 erwartet wird. Wenn die Komponente der Version 2.0 von einer älteren 1.0-Anwendung geladen wird, tritt ein Fehler auf. Wir könnten dies beheben, indem jeder Consumer nach fehlenden Abhängigkeiten suchen und diesen Fall behandeln muss, aber dies würde viele Überprüfungen erfordern. Eine bessere Lösung besteht darin, sicherzustellen, dass immer eine Standardimplementierung vorhanden ist, vielleicht nur ein triviales Verhalten, sodass Komponenten davon ausgehen können, dass consume() immer ein Objekt zurückgibt, das den Vertrag implementiert.

Verwendung: ServiceScope-Instanzen werden durch Aufrufen von ServiceScope.startNewRoot() oder ServiceScope.startNewChild() erstellt. Sie befinden sich zunächst in einem "unfinished"-Zustand, in dem provide() aufgerufen werden kann, um Dienstschlüssel zu registrieren, aber consume() ist nicht zulässig. Nachdem ServiceScope.finish() aufgerufen wurde, ist consume() zulässig, und provide() ist jetzt nicht mehr zulässig. Diese Semantik stellt sicher, dass ServiceScope.consume() immer das gleiche Ergebnis für denselben Schlüssel zurückgibt und nicht von der Initialisierungsreihenfolge abhängt. Außerdem können wir zirkuläre Abhängigkeiten unterstützen, ohne sich Gedanken über Endlosschleifen machen zu müssen. (Zirkuläre Abhängigkeiten werden am besten vermieden, dies ist jedoch schwer zu gewährleisten, wenn sie mit Komponenten arbeiten, die von verschiedenen Drittanbietern ohne jegliche Koordination bereitgestellt wurden.) Um Fehler zu vermeiden, ist es am besten, immer consume() innerhalb eines Rückrufs von serviceScope.whenFinished() aufzurufen.

Konstruktoren

(constructor)(parent)

Erstellt eine neue instance der ServiceScope -Klasse

Methoden

consume(serviceKey)

Nutzt einen Dienst aus dem Dienstbereich.

createAndProvide(serviceKey, simpleServiceClass)

Dies ist eine Kurzfunktion, die dem Erstellen einer neuen instance der simpleServiceClass entspricht und dann durch Aufrufen von ServiceScope.provide() registriert wird.

createDefaultAndProvide(serviceKey)

Dies ist eine Abkürzungsfunktion, die die Standardimplementierung des angegebenen ServiceKey erstellt und ihn dann durch Aufrufen von ServiceScope.provide() registriert.

finish()

Schließt die Initialisierungssequenz für einen Dienstbereich ab.

getParent()

Gibt das übergeordnete Element des aktuellen ServiceScope oder undefiniert zurück, wenn dies ein Stammbereich ist.

provide(serviceKey, service)

Fügen Sie einem Dienstbereich einen neuen Dienst hinzu.

startNewChild()

Erstellt einen neuen ServiceScope, der ein untergeordnetes Element des aktuellen Bereichs ist.

startNewRoot()

Erstellt einen neuen ServiceScope auf Stammebene. Nur Bereiche auf Stammebene können standardmäßige Implementierungen von ServiceKeys automatisch erstellen.

whenFinished(callback)

Zurückstellen eines Vorgangs, bis ServiceScope.finish() abgeschlossen ist.

Details zum Konstruktor

(constructor)(parent)

Erstellt eine neue instance der ServiceScope -Klasse

protected constructor(parent: ServiceScope | undefined);

Parameter

parent

ServiceScope | undefined

Details zur Methode

consume(serviceKey)

Nutzt einen Dienst aus dem Dienstbereich.

consume<T>(serviceKey: ServiceKey<T>): T;

Parameter

serviceKey

ServiceKey<T>

Der Schlüssel, der verwendet wurde, als provide() aufgerufen wurde, um den Dienst zu registrieren.

Gibt zurück

T

die Dienstinstanz

Hinweise

Komponenten sollten diese Funktion aufrufen, um eine Abhängigkeit zu "nutzen", d. h. den serviceKey zu suchen und den registrierten Dienst instance zurückzugeben. Wenn die instance nicht gefunden werden kann, wird automatisch eine Standard-instance erstellt und im Stamm-ServiceScope registriert.

createAndProvide(serviceKey, simpleServiceClass)

Dies ist eine Kurzfunktion, die dem Erstellen einer neuen instance der simpleServiceClass entspricht und dann durch Aufrufen von ServiceScope.provide() registriert wird.

createAndProvide<T>(serviceKey: ServiceKey<T>, simpleServiceClass: {
        new (serviceScope: ServiceScope): T;
    }): T;

Parameter

serviceKey

ServiceKey<T>

der Schlüssel, der später verwendet werden kann, um den Dienst nutzen

simpleServiceClass

{ new (serviceScope: ServiceScope): T; }

die zu erstellende TypeScript-Klasse

Gibt zurück

T

eine neu erstellte Instanz von simpleServiceClass

createDefaultAndProvide(serviceKey)

Dies ist eine Abkürzungsfunktion, die die Standardimplementierung des angegebenen ServiceKey erstellt und ihn dann durch Aufrufen von ServiceScope.provide() registriert.

createDefaultAndProvide<T>(serviceKey: ServiceKey<T>): T;

Parameter

serviceKey

ServiceKey<T>

der Schlüssel, der später verwendet werden kann, um den Dienst nutzen

Gibt zurück

T

eine Dienstinstanz, die mit ServiceKey.defaultCreator erstellt wurde

finish()

Schließt die Initialisierungssequenz für einen Dienstbereich ab.

finish(): void;

Gibt zurück

void

Hinweise

Wenn ein ServiceScope zum ersten Mal gestartet wird, befindet er sich in einem "unfinished"-Zustand, in dem provide() zulässig ist, aber consume() nicht zulässig ist. Nach dem Aufruf von finish() ist consume() zulässig, aber provide() ist nicht zulässig.

Dieser Formalismus verhindert eine Reihe komplexer Situationen, die zu Fehlern führen können. Angenommen, Scope2 ist ein untergeordnetes Element von Scope1 und Scope1 stellt instance A1 der Schnittstelle A bereit. Wenn jemand A1 aus Scope2 (über Vererbung) nutzt, bevor Scope2.provide() mit A2 aufgerufen wird, gibt ein nachfolgendes Aufruf von Scope2.consume() möglicherweise ein anderes Ergebnis als der vorherige Aufruf zurück. Dieser Nichtdeterminismus kann zu unvorhersehbaren Ergebnissen führen, die schwer zu diagnostizieren sind.

getParent()

Gibt das übergeordnete Element des aktuellen ServiceScope oder undefiniert zurück, wenn dies ein Stammbereich ist.

getParent(): ServiceScope | undefined;

Gibt zurück

ServiceScope | undefined

der übergeordnete Dienstbereich

provide(serviceKey, service)

Fügen Sie einem Dienstbereich einen neuen Dienst hinzu.

provide<T>(serviceKey: ServiceKey<T>, service: T): T;

Parameter

serviceKey

ServiceKey<T>

Der Schlüssel, der später verwendet wird, um den Dienst zu nutzen.

service

T

Die Dienstinstanz, die registriert wird.

Gibt zurück

T

Das gleiche Objekt, das Dienstparameter übergeben wurde

Hinweise

ServiceScope.provide() wird verwendet, um eine Implementierung des angegebenen serviceKey für den aktuellen Bereich und eine Implementierung eines angegebenen rootServiceKey für den Stammbereich zu registrieren. Es kann nur verwendet werden, wenn sich der ServiceScope in einem "unfinished"-Zustand befindet, d. h. bevor finish() aufgerufen wurde.

startNewChild()

Erstellt einen neuen ServiceScope, der ein untergeordnetes Element des aktuellen Bereichs ist.

startNewChild(): ServiceScope;

Gibt zurück

Den neu erstellten Stamm-ServiceScope

Hinweise

Die Dienstbereiche bilden eine Struktur, sodass bei der Nutzung eines Diensts, wenn der Schlüssel nicht explizit von einem untergeordneten Bereich bereitgestellt wird, die übergeordnete Hierarchie konsultiert wird.

startNewRoot()

Erstellt einen neuen ServiceScope auf Stammebene. Nur Bereiche auf Stammebene können standardmäßige Implementierungen von ServiceKeys automatisch erstellen.

static startNewRoot(): ServiceScope;

Gibt zurück

Den neu erstellten Stamm-ServiceScope

whenFinished(callback)

Zurückstellen eines Vorgangs, bis ServiceScope.finish() abgeschlossen ist.

whenFinished(callback: () => void): void;

Parameter

callback

() => void

Ein Codeblock, der ServiceScope.consume() aufrufen muss

Gibt zurück

void

Hinweise

Das Aufrufen von ServiceScope.consume() vor dem Aufrufen von finish() ist ein Fehler. Die zuverlässigste Möglichkeit zum Schutz Ihrer Komponente vor diesem Fehler besteht darin, die consume()-Aufrufe innerhalb eines whenFinished()-Rückrufs auszuführen. Wenn der Dienstbereich bereits abgeschlossen ist, wird der Rückruf sofort ausgeführt; andernfalls wird er später nach Abschluss des Bereichs ausgeführt.

HINWEIS: Dies ist kein asynchroner Rückruf. Die ServiceScope-Initialisierung ist in der Regel kostengünstig und kurzlebig. Die Ablaufsteuerung durchläuft jedoch häufig zahlreiche Konstruktoren und Basisklassen, die mithilfe von whenFinished() vereinfacht werden können.