2016-09-18 103 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美分。