2009-07-20 59 views
1

我有一個基於管道的應用程序,它可以分析不同的語言(比如英語和中文)中的文本。我的目標是有一個系統,可以在兩種語言,透明的方式。 注意:這個問題很長,因爲它有很多簡單的代碼片段。管道系統的體系結構/設計。如何改進此代碼?

管道是由三個部分組成(可以稱它們爲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 
    } 
} 

現在,管道的每個組件都具有特定語言的內容。例如,爲了分析中文文本,我需要一個庫,並且爲了分析英文文本,我需要另一個不同的庫。

此外,還有一些任務可以用一種語言完成,而另一些則無法完成。解決這個問題的一個辦法是將每個管道組件抽象化(實現一些常用方法),然後具體實現具體的語言。與組分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方法中看到的那樣,我首先確定輸入的語言,然後基於此,我需要將管道組件更改爲所標識的語言。因此,而不是直接調用的組件,也許我應該從廠家得到他們,就像這樣:

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

感謝您遠閱讀本。我非常感謝你在這個問題上可以提出的每一個建議。

回答

1

工廠的想法很好,如果可行的話,也可以將A,B,C組件封裝到每種語言的單個類中。我敦促你考慮的一件事是使用Interface繼承,而不是Class繼承。然後你可以加入一個引擎,爲你做runPipeline的過程。這與Builder/Director pattern類似。在此過程中的步驟將是如下:

  1. 獲取輸入
  2. 使用工廠方法來獲取正確的接口(英文/中國)
  3. 通界面到您的發動機
  4. runPipeline並得到結果

關於extends vs implements話題,Allen Holub goes a bit over the top解釋了對Interfaces的偏好。


跟進你的意見:

我在這裏建造模式的應用的解釋是,你有一個Factory,將返回一個PipelineBuilder。在我的設計中的PipelineBuilder是一個包含A,B,& C的設計,但如果您願意,您可以爲每個設計單獨建造。然後,該構建器將被分配給您的PipelineEngine,它使用Builder來生成結果。

由於這是使用Factory來提供Builders,所以您對Factory的上述想法仍然完備,充滿了其緩存機制。

關於您選擇的abstract擴展名,您可以選擇給予您的PipelineEngine重物的所有權。但是,如果確實按abstract的方式執行,請注意您聲明的共享字段爲private,因此您的子類不可用。

+0

感謝您的意見和建議!我已經閱讀了關於Builder模式的一些文章,並且如果我正確理解了它,這個想法應該是有一個`PipelineBuilder`,在給定語言的情況下,可以創建組件A,B和C語言特定版本的方法,然後返回一個方法返回「剛建好」的語言特定的「管道」。然後,我會有一個`PipelineEngine`,它將接收一個`Pipeline`並執行`runPipeline`。現在,我的問題是,我將在運行時切換語言/管道,並且每次創建新管道的成本都很高。我如何緩存它們? – 2009-07-21 01:41:07

1

我喜歡基本的設計。如果這些課程足夠簡單,我可能會考慮將A/B/C工廠合併爲一個班級,因爲看起來可能會有一些人在這個級別分享行爲。儘管如此,我認爲這些確實比它們看起來更復雜,這就是爲什麼這是不可取的。

使用工廠減少部件之間的耦合的基本方法是合理的,伊莫。

0

如果我沒有記錯的話,你在呼喚一個工廠實際上是依賴注入的一個非常好的形式。您正在選擇最能夠滿足參數需求並返回的對象實例。

如果我說得對,你可能想看看DI平臺。他們做你所做的事情(這很簡單,對嗎?),然後他們增加一些你現在可能不需要的能力,但是你可能會發現會在以後幫助你。

我只是建議你看看現在解決了什麼問題。 DI很容易做你自己,你幾乎不需要任何其他工具,但他們可能已經發現你還沒有考慮過的情況。 Google發現很多很棒的鏈接。

從我所見到的DI中,很可能你會想要將整個「管道」的創建移動到工廠中,讓它爲你做鏈接,然後把你需要解決的問題交給你一個特定的問題,但現在我真的達到了 - 我對DI的瞭解比我對代碼的知識稍微好一點(換句話說,我把大部分內容從我的屁股中抽出來)。

+0

感謝您的意見。 DI的問題是我需要在運行時更改管道(和組件)。例如,我以一個句子作爲輸入;我對它做了一些分析以檢測它的語言;然後我需要獲取流水線中特定於語言的組件(我可能需要使Pipeline成爲一個接口,並且具有語言特定版本以簡化「開關」)。從我讀到的DI中,想法是配置外部依賴關係(例如.xml),並將它們「注入」到遠處,這使得在運行時切換不可行。 – 2009-07-21 01:50:04