J'ai une application basée sur pipeline qui analyse le texte en différentes langues (disons, anglais et chinois). Mon objectif est d'avoir un système qui peut fonctionner dans les deux langues, de manière transparente. NOTE: Cette question est longue car elle contient de nombreux extraits de code simples.Architecture/Conception d'un système basé sur un pipeline. Comment améliorer ce code?
Le pipeline se compose de trois composants (permet de les appeler A, B et C), et je les ai créés de la manière suivante, de sorte que les composants ne sont pas étroitement couplés:
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
}
}
Maintenant, chaque composant du pipeline a quelque chose à l'intérieur qui est spécifique à la langue. Par exemple, pour analyser le texte chinois, j'ai besoin d'une lib, et pour l'analyse du texte anglais, j'ai besoin d'une autre lib.
En outre, certaines tâches peuvent être effectuées dans une langue et ne peuvent pas être effectuées de l'autre. Une solution à ce problème consiste à rendre chaque composant de pipeline abstrait (pour implémenter des méthodes communes), puis avoir une implémentation concrète spécifique au langage. Illustrant avec le composant A, je donne les résultats suivants:
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 ...
}
En outre, étant donné que chaque composant de pipeline est très lourd et j'ai besoin de les réutiliser, je pensais que la création d'une usine qui met en cache le composant pour une utilisation ultérieure, en utilisant une carte qui utilise la langue comme la clé, comme si (les autres composants fonctionnerait de la même manière):
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());
}
}
alors, ma question est: que pensez-vous de cette conception ? Comment peut-il être amélioré? J'ai besoin de la "transparence" parce que le langage peut être changé dynamiquement, sur la base du texte qui est analysé. Comme vous pouvez le voir à partir de la méthode runPipeline
, j'identifie d'abord la langue de l'Input, puis, sur cette base, je dois changer les composants du pipeline dans la langue identifiée. Ainsi, au lieu d'invoquer les composants directement, peut-être que je devrais les faire de l'usine, comme ceci:
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);
}
Merci d'avoir lu jusqu'ici. J'apprécie énormément chaque suggestion que vous pouvez faire sur cette question.
Merci pour vos commentaires et suggestions!J'ai lu quelques articles sur le pattern Builder et si j'ai bien compris, l'idée serait d'avoir un PipelineBuilder qui, avec un langage, aurait des méthodes pour créer des versions spécifiques au langage des composants A, B et C, puis une méthode pour renvoyer le "Pipeline" spécifique au langage "juste construit". Ensuite, j'aurais un 'PipelineEngine' qui recevrait un' Pipeline' et exécuterait 'runPipeline'. Maintenant, mon problème est que je vais changer de langages/pipelines en cours d'exécution, et il est très coûteux de créer un nouveau pipeline à chaque fois. Comment puis-je les mettre en cache? –
En ce qui concerne le problème des extensions par rapport aux implémentations, j'ai également lu cet article, et bien que ce soit une bonne lecture, je pense que les exemples 'Collections' manquent quelque peu le point, mais j'ai le problème. Cependant, dans mon cas particulier, j'ai des objets lourds qui doivent être partagés entre chaque composant spécifique de la langue, et quelques méthodes communes qui fonctionnent sur eux, d'où la classe '' abstract''. –