Ich habe eine Pipeline-basierte Anwendung, die Text in verschiedenen Sprachen (etwa Englisch und Chinesisch) analysiert. Mein Ziel ist, ein System zu haben, das auf beiden Sprachen in einem transparenten Weg funktionieren kann. HINWEIS: Diese Frage ist lang, weil es viele einfache Code-Snippets hat.Architektur/Entwurf eines Pipeline-basierten Systems. Wie kann ich diesen Code verbessern?
Die Pipeline besteht aus drei Komponenten (lassen sie rufen A, B und C), und ich habe sie in der folgenden Art und Weise erstellt, so dass die Komponenten nicht eng gekoppelt:
public class Pipeline {
private A componentA;
private B componentB;
private C componentC;
// I really just need the language attribute of Locale,
// but I use it because it's useful to load language specific ResourceBundles.
public Pipeline(Locale locale) {
componentA = new A();
componentB = new B();
componentC = new C();
}
public Output runPipeline(Input) {
Language lang = LanguageIdentifier.identify(Input);
//
ResultOfA resultA = componentA.doSomething(Input);
ResultOfB resultB = componentB.doSomethingElse(resultA); // uses result of A
return componentC.doFinal(resultA, resultB); // uses result of A and B
}
}
Nun hat jede Komponente der Pipeline etwas, das sprachspezifisch ist. Um beispielsweise chinesischen Text zu analysieren, benötige ich eine Lib, und zum Analysieren von englischem Text brauche ich eine andere Lib.
Darüber hinaus gibt es einige Aufgaben, die in einer Sprache ausgeführt werden können, und auf der anderen nicht ausgeführt werden können. Eine Lösung für dieses Problem besteht darin, jede Pipelinekomponente abstrakt zu machen (um einige gebräuchliche Methoden zu implementieren) und dann eine konkrete sprachspezifische Implementierung zu haben. Als Beispiel für mit der Komponente A, würde ich habe folgende:
public abstract class A {
private CommonClass x; // common to all languages
private AnotherCommonClass y; // common to all languages
abstract SomeTemporaryResult getTemp(input); // language specific
abstract AnotherTemporaryResult getAnotherTemp(input); // language specific
public ResultOfA doSomething(input) {
// template method
SomeTemporaryResult t = getTemp(input); // language specific
AnotherTemporaryResult tt = getAnotherTemp(input); // language specific
return ResultOfA(t, tt, x.get(), y.get());
}
}
public class EnglishA extends A {
private EnglishSpecificClass something;
// implementation of the abstract methods ...
}
Da zusätzlich jede Pipeline-Komponente ist sehr schwer und ich brauche sie wieder zu verwenden, dachte ich an der Schaffung eine Fabrik, das die Komponente-Caches oben für weitere Verwendung, eine Karte, die die Sprache als Schlüssel verwendet, wie so verwendet (die anderen Komponenten auf die gleiche Weise funktionieren würde):
public Enum AFactory {
SINGLETON;
private Map<String, A> cache; // this map will only have one or two keys, is there anything more efficient that I can use, instead of HashMap ?
public A getA(Locale locale) {
// lookup by locale.language, and insert if it doesn't exist, et cetera
return cache.get(locale.getLanguage());
}
}
also, meine Frage ist: Was halten Sie von diesem Design ? Wie kann es sein verbessert? Ich brauche die "Transparenz", weil die Sprache dynamisch geändert werden kann, basierend auf dem Text, der analysiert wird. Wie Sie aus der -Methode sehen können, identifiziere ich zuerst die Sprache der Eingabe, und basierend darauf muss ich die Pipeline-Komponenten in die angegebene Sprache ändern. Also, anstatt direkt die Komponenten aufrufen, vielleicht sollte ich sie von der Fabrik erhalten, etwa so:
public Output runPipeline(Input) {
Language lang = LanguageIdentifier.identify(Input);
ResultOfA resultA = AFactory.getA(lang).doSomething(Input);
ResultOfB resultB = BFactory.getB(lang).doSomethingElse(resultA);
return CFactory.getC(lang).doFinal(resultA, resultB);
}
Danke bis hierher zum Lesen. Ich schätze jeden Vorschlag, den Sie zu dieser Frage machen können.
Danke für die Kommentare und Vorschläge!Ich habe einige Artikel über das Builder-Muster gelesen. Wenn ich es richtig verstanden habe, wäre die Idee, einen PipelineBuilder zu haben, der bei einer Sprache Methoden zur Verfügung stellt, um sprachspezifische Versionen der Komponenten A, B, & C zu erstellen. und dann eine Methode, um die "gerade gebaute" sprachspezifische "Pipeline" zurückzugeben. Dann hätte ich eine 'PipelineEngine', die eine' Pipeline' erhalten und 'runPipeline' ausführen würde. Nun, mein Problem ist, dass ich Sprachen/Pipelines zur Laufzeit wechseln werde und es sehr teuer ist, jedes Mal eine neue Pipeline zu erstellen. Wie kann ich sie zwischenspeichern? –
Zum Thema Extents versus Implements habe ich diesen Artikel auch gelesen, und obwohl es eine nette Lektüre ist, glaube ich, dass die 'Collections' Beispiele irgendwie den Punkt verfehlen, aber ich bekomme das Problem. In meinem speziellen Fall habe ich jedoch einige schwere Objekte, die unter jeder sprachspezifischen Komponente gemeinsam genutzt werden müssen, und einige gebräuchliche Methoden, die auf ihnen arbeiten, daher die "abstract" -Klasse. –