簡體   English   中英

基於管道的系統的架構/設計。 如何改進這段代碼?

[英]Architecture/Design of a pipeline-based system. How to improve this code?

我有一個基於管道的應用程序,可以分析不同語言(比如英語和中文)的文本。 我的目標是擁有一個可以以透明方式使用兩種語言的系統。 注意:這個問題很長,因為它有很多簡單的代碼片段。

管道由三個組件組成(我們稱它們為 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 ... 
}

此外,由於每個管道組件都非常重,我需要重用它們,所以我想到創建一個工廠來緩存組件以供進一步使用,使用 map 以語言為鍵,就像這樣(其他組件會以相同的方式工作):

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

感謝您閱讀到這里。 我非常感謝你就這個問題提出的每一個建議。

我喜歡基本的設計。 如果這些類足夠簡單,我可能會考慮將 A/B/C 工廠合並到一個 class 中,因為在該級別上似乎可以共享一些行為。 不過,我假設這些實際上比它們看起來更復雜,這就是為什么這是不可取的。

imo,使用工廠來減少組件之間耦合的基本方法是合理的。

工廠的想法很好,如果可行的話,將 A、B 和 C 組件封裝到每種語言的單個類中。 我敦促您考慮的一件事是使用Interface inheritance 而不是Class inheritance。然后您可以合並一個引擎來為您執行runPipeline過程。 這類似於Builder/Director 模式 此過程中的步驟如下:

  1. 獲取輸入
  2. 使用工廠方法獲得正確的界面(英文/中文)
  3. 將接口傳遞給你的引擎
  4. 運行管道並獲得結果

extends vs implements主題上, Allen Holub 有點過頭來解釋對Interfaces的偏好。


跟進你的評論:

我在這里對 Builder 模式應用的解釋是,您有一個返回PipelineBuilderFactory 在我的設計中, PipelineBuilder是包含 A、B 和 C 的一個,但如果您願意,您可以為每個構建器使用單獨的構建器。 然后將該構建器提供給您的PipelineEngine ,它使用該Builder生成您的結果。

由於這使用工廠來提供構建器,因此您上面關於工廠的想法仍然完好無損,充滿了它的緩存機制。

關於您選擇的abstract擴展,您確實可以選擇讓您的PipelineEngine擁有重對象的所有權。 但是,如果您以abstract方式執行 go,請注意您聲明的共享字段是private的,因此您的子類將無法使用。

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

如果我是對的,您可能想研究 DI 平台。 他們做你所做的(這很簡單,對吧?)然后他們添加了一些你現在可能不需要但你以后可能會發現對你有幫助的能力。

我只是建議你看看現在解決了什么問題。 DI 非常容易自己完成,您幾乎不需要任何其他工具,但他們可能已經發現了您尚未考慮的情況。 谷歌立即找到許多漂亮的鏈接。

從我對 DI 的了解來看,您可能希望將“管道”的整個創建移動到工廠中,讓它為您進行鏈接,並只為您提供解決特定問題所需的東西,但現在我真的達到了——我對 DI 的了解只比我對你的代碼的了解好一點(換句話說,我把大部分內容都從我的屁股里拿出來了)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM