2016-09-18 101 views
1

我想設計讀取一個大的文本文件中的API,提取相關信息,並返回美孚對象的列表如下:Java服務架構

interface FooService { 
    Optional<Foo> getFoo(Bar bar); 
} 

文本文件和方式,它的格式被解析總是一樣的。唯一可以變化的是文件的位置,即它可以是本地系統上的文件或URL。所以,我創建了一個AbstractFooService:

class AbstractFooService implements FooService { 

    Map<Bar, Foo> registry; 

    AbstractFooService(InputStream is) { 
     try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { 
      registry = reader.lines() 
       .map(l -> l.split(';')) 
       .map(a -> new Foo(a[0]), a[1])) 
       .collect(Collectors.groupingBy(...)); 
     } catch (IOException e) { 
      throw new UncheckedIOException(e); 
     } 
    } 

    Optional<Foo> getFoo(Bar bar) { 
     return Optional.ofNullable(registry.get(bar)); 
    } 
} 

具體的實現只是調用超級構造與一個InputStream:

class UrlFooService extends AbstractFooService { 
    UrlFooService(String url) { 
     super(createStream(url)); 
    } 

    private static InputStream createStream(final String url) { 
     try { 
      return new URL(string).openStream(); 

     } catch (IOException e) { 
      throw new UncheckedIOException(e); 
     } 
    } 
} 

那是一個完善的API設計或者是有一個「好」的方式來實現我的目標?即使用InputStream調用超級構造函數是否明智?或者需要一個單獨的load()方法來在需要時打開數據流?

+3

有關工作代碼,請轉到codereview.stackexchange.com – GhostCat

+0

在構造函數中執行工作通常是一個糟糕的主意。它使得難以測試代碼,並可能導致繼承層次結構中的非顯而易見的行爲。 – sisyphus

回答

2

我不明白爲什麼你需要那個抽象基類。比繼承更喜歡構圖;我認爲更合理的解決辦法是有:

public class FooServiceImpl implements FooService { 
... 

然後客戶端,如

public class UrlFooService implements FooService { 
    private final FooService delegatee; 

public UrlFooService(URL url) { 
    delegate = new FooServiceImpl(url.openStream()) 
... 
@Override 
Optional<Foo> getFoo(Bar bar) { return delegatee.getFoo(bar); } 

繼承夫婦的具體服務類與父類的;我寧願避免,通過使用這個簡單的「委託人」機制。

請注意:我還更改了UrlSerivce的URL以獲取URL。你已經有了這些類型,那麼爲什麼還要自己打電話給自己呢?這隻意味着你的UrlService將不得不處理所有可能出錯的事情!

0

在這裏有幾個挑戰與你的問題,我會開始以不同的方式分解問題。

@sisyphus說,注意你在構造函數中做的事情。一個構造函數確實應該只關注於創建一個「有效的對象」,而沒有其他的東西。 @GhostCat也提供了許多很好的點子。

而是認爲如下建模的問題:

創建一個代表爲您服務的API接口。在這種情況下,如果你希望它是「getFoo()」,那麼很好。考慮你想傳遞什麼(它是真正的文件還是URL或路徑)。既然你說這是一個大文件,那麼在內存中實例化一個大對象可能不是一個好主意,你將要再次解析爲有用的格式。你一定會在垃圾回收中付出代價。

接下來,您應該考慮分離出文件的「發現」 - 或者用解析邏輯打開流。當試圖打開文件時會發生很多異常情況 - 從未找到文件,到沒有權限,打開文件太多(ulimit)。

說到解析,我建議你考慮清楚你正在解析什麼和解析什麼。如果這是一個現實世界的問題,那麼「穩定格式」總是會發生變化 - 特別是在容忍'無效'格式時 - 例如其他非可打印字符的存在或意外的EOF。處理事情會有很多要求,也需要理解分析的內容,錯誤以及如何處理這些內容。

我的2美分。