2009-02-18 71 views
3

這是Java中的一種設計模式問題。在運行時配置「管道」

我正在設計一個java .jar文件來充當管理和處理某種形式的數據的框架。我希望最終用戶能夠以某種方式在一定的限制範圍內指定「管道」配置。這些作品是生產者和/或消費者,我知道如何實現它們,但是這些關係讓我感到困惑......這是一個與我的應用程序類似的無意義的例子。

假設我已經實現這些元素:

  • AppleTree =>產生蘋果
  • ApplePieMaker =>消耗蘋果, 產生蘋果派
  • ApplePress => 消耗蘋果,產生蘋果酒
  • AppleSave =>將蘋果店, 蘋果派或蘋果酒加入 文件
  • AppleLoad =>「重新構成」蘋果,蘋果餡餅,或蘋果酒從由AppleSave
  • ApplePieMonitor產生的文件=>顯示在GUI格式在屏幕上蘋果餡餅,因爲它們被製造

現在,我希望用戶能夠指定喜歡的事物:

  • AppleTree | ApplePress | AppleSave cider1.sav(生產蘋果,使他們成爲蘋果酒,將它們保存到一個文件)
  • AppleTree | AppleSave apple1.sav(生產蘋果,將它們保存到一個文件)
  • AppleLoad apple1.sav | ApplePieMaker | ApplePieMonitor(以保存蘋果,使他們成爲餡餅,顯示在GUI屏幕上的結果)
  • (不知道如何來說明這一點,但可能被規定如下)

    APPLETREE樹1,ApplePieMaker piemaker1 <樹1,AppleSave apples.sav <樹1,AppleSave @select(* SAV)< piemaker1,ApplePress按1 <樹1,AppleSave cider.sav <按1,ApplePieMonitor piemon1 < piemaker1

(生產蘋果,將它們製成餡餅和蘋果酒,保存蘋果,餡餅和蘋果酒以分開文件,餡餅會在運行時轉到用戶選擇的文件,其他人將預先確定文件,以及在GUI中顯示屏幕上的餅圖)

所以我對如何構建配置文件有一個粗略的想法:即將結構組織成最多有1個輸入和最多1個輸出的元素,然後爲每個實例化的元素,將其命名,如果它具有輸入,則指定提供輸入的元素的名稱。

我不清楚的是如何在程序運行時耦合這些元素。也許要走的路線是有許多接口,如AppleConsumerApplePieConsumer等,因此ApplePieMaker將實現AppleConsumer接口(包括接口)。方法consumeApple()),並且AppleTree將實現AppleProducer接口,該接口可以在啓動時註冊消費者,以便每當AppleTree產生蘋果時,它都具有它的消費者列表並且在它們中的每一個上調用consumeApple(),然後做正確的事情沒有AppleTree必須知道他們在做什麼與蘋果....

有什麼建議嗎?這種事有名嗎?我在設計模式方面並不是那麼有經驗。

編輯:我的最終用戶不知道也不關心Java。他們只需要能夠建立一個配置文件(我試圖儘可能簡化,以便我可以給他們一些很好的例子),並運行我的程序,它將讀取配置文件,構建元素,鉤住它們一起去吧。所有的元素都在我的控制之下,我不需要支持插件,所以我不必是超一般的。

+0

如何配置流量?通過GUI或通過文本文件?或者一個網頁。我問的是,最終用戶如何配置流量? – OscarRyz 2009-02-18 21:29:09

+0

什麼是用戶使用和我實現的最簡單的組合。所以可能通過一個非常簡單的文本或XML文件。 – 2009-02-19 13:58:03

回答

0

我相信你在做什麼可以在Spring框架內完成。它使用依賴注入來說「爲了創建X我需要Y,所以找到產生Y的東西,看看你需要什麼來創建它」。

我可能是錯的,但我建議你看看。

+0

呵呵......我會再看看;我只是看着它:配置文件,但它讓我感到困惑。我不需要推斷哪些元素是需要的,最終用戶必須明確指定它們。 – 2009-02-18 19:22:35

+0

依賴注入通常通過外部文件配置,不一定通過代碼(儘管我認爲它可以通過代碼)。我不確定春天是怎麼做到的。 – 2009-02-18 22:39:23

0

嘗試使用Java Beanshell

BeanShell是一個小型的,免費的,可嵌入的Java源代碼解釋器,具有對象腳本語言功能,用Java編寫。 BeanShell動態執行標準的Java語法,並通過常見的腳本方便擴展它,比如鬆散類型,命令和方法閉包,如Perl和JavaScript中的那些。

+0

雖然這個鏈接可能回答這個問題,但最好在這裏包含答案的重要部分,並提供供參考的鏈接。如果鏈接頁面更改,則僅鏈接答案可能會失效。 – 2012-08-31 02:17:21

1

前段時間我有這個問題,而且在Java中乾淨地指定有點困難,因爲我的輸入和輸出可能是複數。但是,如果您確定只有一個輸入和輸出(因爲文件是特定類型的輸出,對嗎?),您可以嘗試使用選中的泛型。

的處理步驟的實施(我會打電話給一個過濾)有兩個檢查類型參數:輸入和輸出(讓例如假設它們都擴展了一個公共接口,併爲每一個新的類型你注入系統,你將繼承該接口)。

public interface Filter<Input extends Type, Output extends Type> { 

    public Class<Input> getInputType(); 

    public Class<Output> getOutputType(); 

    public void process(Input in, Output out); 

} 

過濾器鏈只是一個(兼容)Filter s的陣列。通過兼容的,我打算爲每個過濾器,其輸出相同類型作爲其追隨者輸入,第一濾波器具有輸入符合您整體的輸入類型,最後一個濾波器具有輸出那匹配預期的結果類型。這在實踐中很容易驗證,因爲我們正在使用選中的泛型。

過濾器鏈因此是其他(複合)過濾器。應該在構造函數中檢查複合過濾器的兼容性,並確定最終的化合物陣列。沒有準確的方式來表達構造函數參數與泛型的「鏈接」屬性(兼容性),因此您必須使用裸類型來完成該操作,這些類型有點不清楚。

的其他辦法做到這一點是能夠規避這種限制,在更麻煩寫作的成本,就是要改變這樣的過濾器的定義:

public interface Filter<Input extends Type, Output extends Type> { 

    public Class<Input> getInputType(); 

    public Class<Output> getOutputType(); 

    public Output out process(Input in); 

} 

然後,我們將必須定義一個複合濾波器作爲濾波器對一個疊瓦,這樣定義:

public class CompoundFilter<Input extends Type, Output extends Type> 
     implements Filter<Input extends Type, Output extends Type> { 

    private final Filter<Input extends Type, ? extends Type> l; 
    private final Filter<Input extends Type, ? extends Type> r; 

    public <Median extends Type> CompoundFilter(
     Filter<Input, Median> r, 
     Filter<Median, Output> l 
    ) { 
     this.l = l; 
     this.r = r; 
    } 

    @SuppressWarnings("unchecked") 
    public Output out process(Input in) { 
     // Compute l(r(in)) = (l o r) (in) 
     return ((Output<Input,Type>) l).process(r.process(in)); 
    } 
} 

因此,構成濾波器僅僅是一個書寫的問題:

Filter<A,B> f1 = new FilterImpl<A,B>;; 
Filter<B,C> f2 = new FilterImpl<B,C>; 
// this is mathematically f2 o f1 
Filter<A,C> comp = new CompoundFilter<A,C>(f1,f2); 
0

您需要某種註冊表,生產者可以註冊(嗨,我是蘋果樹,我生產蘋果),然後消費者可以查找誰生產蘋果。這也可以在消費者註冊興趣和生產者查找的情況下逆向進行。我使用JMX做了類似的事情,其中​​對象可以向JMX服務器查詢產生特定類型消息的對象,然後註冊該對象(發佈/訂閱)。我現在將該應用程序移植到使用具有類似功能的OSGi中

1

我無法幫助它,因此我必須爲此做一些工作。

所以它就是這樣。

你已經有了關於蘋果生產者/消費者的想法,所以這就是我要做的。

創建三個接口,並實現如下:

  • 產品 - 無論是蘋果,AppleCider,ApplePie,
  • 生產者 - 蘋果樹,ApplePress,ApplePieMaker,AppleLoad,
  • 消費者 - ApplePress(消耗蘋果) ,ApplePieMaker(消費蘋果),AppleMonitor,AppleSave。

這個想法是有一個通用的產品,由通用生產者生產和消費的通用消費者。

一旦你有了,你可以創建配置文件,就像你描述它的方式,並解析它來爲每個元素創建一個新的實例。

element1 | element2 | element3 <parameters> | element 4 

在映射中,創建元素名稱並將其映射到將創建新實例的類。

比方說

map.put("AppleTree", YouAppleTreeClass.class); 

所以每次讀取配置的元素時,你創建實例:

for(String item: line) { 
    Object o = map.get(item).newInstance(); 
} 

最後你要確認您的配置的結構,但基本上能是這樣的:

  • 第一個元素應該是生產者
  • 最後應該是消費者
  • 任何中間應該是生產者 - 消費者
  • 您可以分析需要(文件保存爲實例數據)一旦你創建和鏈接所有對象

參數,你開始製作。

有一些事情你必須鍛鍊,但他們很容易:

  1. 參數傳遞(在那裏他們將被保存在文件/從加載)
  2. 對象重複使用在不同的配置(總是使用相同的蘋果樹)

最後說明:下面的代碼,只是一個從頭開始,你可能真的考慮依賴注入來完成這項工作,當然它會帶你一點時間去學習它。

配置解析應該手動完成,因爲您使用的格式對最終用戶來說是唯一的,而且應該非常簡單。儘管如此,你仍然可以在你的jar中提供儘可能多的複雜性(使用你需要的任意數量的框架)。

你也可以看看下面的設計模式:

下實施,是一種怪物的這三個(我沒有編譯它,只是拋出一些代碼來展示這個想法會是怎樣的)

我希望這有助於。

/** 
* Anything. An apple, cider, pie, whatever. 
*/ 
interface Product{} 

// The kinds of products. 
class Apple  implements Product{} 
class ApplePies implements Product{} 
class AppleCider implements Product{} 

/** 
* This indicates the class will do something. 
**/ 
interface Producer { 
    // adds a consumer to the list. 
    public void addConsumer(Consumer c); 
    // removes the consumer from the list. 
    public void removeConsumer(Consumer c); 
    // let know eveytone a product has been created. 
    public void notifyProductCreation(Product someProduct); 
    // You're producer? Produce then... 
    public void startProduction(); 
} 

// To avoid copy/paste all around 
class AbstractProducer implements Producer { 
    private List<Consumer> consumers = new ArrayList<Consumer>(); 
    // adds a consumer to the list. 
    public void addConsumer(Consumer c) { 
     consumers.add(c); 
    } 
    // removes the consumer from the list. 
    public void removeConsumer(Consumer c) { 
     consumers.remove(c); 
    } 
    public void notifyProductCreation(Product someProduct) { 
     for(Consumer c : list) { 
      c.productCreated(someProduct); 
     } 
    } 
} 

interface Consumer { 
    // Callback to know a product was created 
    public void productCreated(Product p); 
} 


class AppleTree extends AbstractProducer { 
    public void startProduction() { 
     // do something with earh, sun, water.. 
     // and from time to time: 
     Product ofThisNewApple = new Apple(); 
     notifyProductCreation(ofThisNewApple); 
    } 

}  
class ApplePieMaker extends AbstractProducer implements Consumer { 

    // Ok, a product was created, but 
    // is it the product I care? 
    // check first and consume after. 
    public void productCreated(Product p){ 
     // Is this the kind of product I can handle.. 
     // well do handle 
     if(p instanceof Apple) { 
      /// start producing pies.. 
     } 
    } 
    public void startProduction() { 
     // collect the needed number of apples and then... 
     Product ofPie = new ApplePie(); 
     notifyProductCreation(ofPie); 
    } 

} 
class ApplePress extends AbstractProducer implements Consumer { 
    // Yeap, something gots produced. 
    // Just handle if it is an apple 
    public void productCreated(Product p) { 
     if(p instanceof Apple) { 
      // start producing cider 
     } 
    } 


    public void startProduction() { 
     // collect the needed number of apples and then... 
     Product ofCiderBottle = new AppleCider(); 
     notifyProductCreation(ofCiderBottle); 
    } 


} 
class AppleSave implements Consumer { 
    public void productCreated(Product p) { 
     file.append(p);// any one will do. 
    } 
} 

class AppleLoad extends AbstractProducer { 
    public void startProduction() { 
     readFromFile(); 
    } 
    private readFromFile() { 
     for(Product p : file) { 
      notifyProductCreation(p); 
     } 
    } 
} 


class Main { 
    public static void main(String [] args) { 
     Configuration conf = new Configuration(); 
     List<Producer> producers conf.read(); 
     for(Producer p : producers) { 
      // fasten your seat belts.... 
      p.startProduction(); 
     } 
    } 
} 

/// Ahhh, pretty ugly code below this line. 
// the idea is: 
// Read the configuration file 
// for each line split in the "|" 
// for each element create a new instance 
// and chain it with the next. 
// producer | consumer | etc... 
// Becomes.... 
// new Producer().addConsumer(new Consumer()); 
// Return the list of create producers. 
class Configuration { 
    List<Producer> producers 
    // read the file 
    // create the instances 
    // let them run. 
    public List<Producer> read() { 
     File file = new File(.... 
     // The format is: 
     // producer | consumer-producer <params> | consumer 
     String line = uniqueLineFrom(file); 

     String [] parts = line.split("|"); 

     if(parts.length == 1) { 
      System.err.println("Invalid configuration. use element | element | etc. Only one element was...."); 
      System.exit(1); 
     } 



     int length = parts.length; 
     for(int i = 0 ; i < parts.length ; i++) { 
      Object theInstance = implementationMap.get(parts[i]).newInstance(); 
      validatePosition(i, length, theInstance , parts[i]); 
     } 

     List<Producer> producers = new ArrayList<Producer>(); 
     for(int i = 0 ; i < parts.length ; i++) { 
      Object theInstance = getInstance(parts[i]); 
      if(not(isLast(i, length) && isProducer(theInstance)) { 
       // the next is its consumer 
       Producer producer = (Producer) theInstance; 
       producer.addConsumer((Consumer) getInstance(parts[i+1])); 
       producers.add(producer); 
      } 
     } 
     return producers; 

    } 
    // creates a new instance from the implementation map. 
    private Object getInstance(String key) { 
     return implementationMap.get(part[i]).newInstance();   
    } 
    // validates if an element at the given position is valid or not. 
    // if not, prints the message and exit. 
    // the first element most be a producer 
    // the last one a consumer 
    // all the middle elements producer-consumer 
    // 
    private void validatePosition(int i, int length, Object theInstance, String element ) { 
     if(isFirst(i) && not(isProducer((theInstance)))) { 
      System.err.println("Invalid configuration: " + element + " most be a producer (either Ap..."); 
      System.exit(2); 
     } else if (isLast(i, length) && not(isConsumer(theInstance ))) { 
      System.err.println("Invalid configuration: " + element + " most be a consumer (either Ap..."); 
      System.exit(3); 
     } else if (isMiddleAndInvalid(i, length , instance)) { 
      System.err.println("Invalid configuration: " + element + " most be a producer-consumer (either Ap..."); 
      System.exit(4); 
     } 
    } 
    private static Map<String,Class> implementationMap = new HashMap<String,Class>() static { 
     implementationMap.put("AppleTree", AppleTree.class); 
     implementationMap.put("ApplePieMaker ", ApplePieMaker .class); 
     implementationMap.put("ApplePress", ApplePress.class); 
     implementationMap.put("AppleSave", AppleSave.class); 
     implementationMap.put("AppleLoad", AppleLoad.class); 
     implementationMap.put("ApplePieMonitor", ApplePieMonitor.class); 
    }; 

    // Utility methods to read better (hopefully) the statements 
    // If you could read the validations above you may ignore these functions. 


    private boolean not(boolean value) { 
     return !value; 
    } 
    private boolean isFirst(int i ) { 
     return i == 0; 
    } 
    private boolean isLast(int i, int l) { 
     return i == l -1 ; 
    } 
    private boolean isProducer(Object o) { 
     return o instanceof Producer; 
    } 
    private boolean isConsumer(Object o) { 
     return o instanceof Consumer; 
    } 
    private boolean isMiddleAndInvalid(int index, int length, Object instance) { 
     return not(isFirst(index)) && not(isLast(index, length)) && not(isProducer(instance) && isConsumer(instance)); 
    } 
}