架构/设计基于管道的系统。如何改进这段代码?

时间:2009-07-20 04:55:07

标签: java design-patterns oop architecture

我有一个基于管道的应用程序,用于分析不同的语言(例如,英语和中文)中的文本。我的目标是建立一个可以在透明方式下使用这两种语言的系统。 注意:这个问题很长,因为它有很多简单的代码段。

管道由三个组件组成(我们称之为A,B和C),我用以下方式创建它们,因此组件没有紧密耦合:

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

现在,管道的每个组件都有一些特定于语言的内容。例如,为了分析中文文本,我需要一个lib,为了分析英文文本,我需要另一个不同的lib。

此外,有些任务可以用一种语言完成,而另一种语言则无法完成。这个问题的一个解决方案是使每个管道组件都抽象(实现一些常用方法),然后具体具体的语言实现。以组件A为例,我有以下内容:

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

此外,由于每个管道组件都非常繁重,我需要重用它们,所以我想创建一个工厂来缓存组件以供进一步使用,使用使用该语言的地图关键,像这样(其他组件将以相同的方式工作):

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

所以,我的问题是:您对此设计有何看法?如何改进?我需要“透明度”,因为语言可以根据正在分析的文本动态更改。从runPipeline方法可以看出,我首先确定了Input的语言,然后,基于此,我需要将管道组件更改为已识别的语言。因此,不是直接调用组件,也许我应该从工厂获取它们,如下所示:

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

感谢您阅读此内容。我非常感谢你对这个问题提出的每一个建议。

3 个答案:

答案 0 :(得分:1)

我喜欢基本设计。如果这些类很简单,我可能会考虑将A / B / C工厂整合到一个类中,因为在该级别上可能会有一些共享行为。我假设这些实际上比它们看起来更复杂,这就是为什么这是不可取的。

使用工厂来减少组件之间耦合的基本方法是合理的,imo。

答案 1 :(得分:1)

工厂的想法是好的,如果可行的话,将封装A,B和&amp;的想法也是如此。 C组件为每种语言的单个类。我要求您考虑的一件事是使用Interface继承而不是Class继承。然后,您可以合并一个可以为您执行runPipeline过程的引擎。这类似于Builder/Director pattern。这个过程的步骤如下:

  1. 获取输入
  2. 使用工厂方法获取正确的界面(英文/中文)
  3. 将界面传递到您的引擎
  4. runPipeline并获得结果
  5. extends vs implements主题Allen Holub goes a bit over the top上解释Interfaces的偏好。


    跟进你的评论:

    我在这里对Builder模式的应用的解释是你有Factory将返回PipelineBuilder。我设计中的PipelineBuilder是一个包含A,B和A的组合。 C,但如果你愿意,你可以为每个人建立单独的构建器。然后,此构建器将发送给PipelineEngineBuilder使用abstract生成结果。

    由于这会使用工厂来提供构建器,因此上面对工厂的想法仍然存在,充满了其缓存机制。

    关于您选择的PipelineEngine扩展程序,您可以选择对重物提供abstract所有权。但是,如果您采用private方式,请注意您声明的共享字段为{{1}},因此您的子类无法使用。

答案 2 :(得分:0)

如果我没有弄错的话,你所谓的工厂实际上是一种非常好的依赖注入形式。您正在选择最能满足参数需求的对象实例并将其返回。

如果我是对的,你可能想要研究DI平台。他们做你做的事情(这很简单,对吗?)然后他们增加了一些你现在可能不需要的能力,但你可能会发现以后会帮助你。

我只是建议你看看现在解决了什么问题。 DI很容易自己做,你几乎不需要任何其他工具,但他们可能已经找到了你尚未考虑的情况。 Google发现很多很棒的链接。

从我所看到的DI中,你很可能想把你的“管道”的整个创作移到工厂里,让它为你做链接,然后把你需要的东西递给你具体问题,但现在我真正达到了 - 我对DI的了解比我对你的代码的了解要好一些(换句话说,我将大部分内容从我的屁股中拉出来)。