我尝试解决的问题涉及到一些领域知识/业务逻辑,但我会尽可能地将其归结为基础知识。
假设我的接口定义如下:
public interface Stage<I, O> {
StageResult<O> process(StageResult<I> input) throws StageException;
}
这代表了多阶段数据处理管道中的一个阶段,我的想法是将数据处理步骤分解为顺序(非分支)独立步骤(例如从文件读取,解析网络头,解析消息有效负载,转换格式,写入文件)由各个Stage实现表示。理想情况下,我实现了FileInputStage,NetworkHeaderParseStage,ParseMessageStage,FormatStage和FileOutputStage,然后有某种
Stage<A, C> compose(Stage<A, B> stage1, Stage<B, C> stage2);
方法,这样我最终可以将一堆阶段组成一个看起来像FileInput的最后阶段 - &gt; FileOutput。
这是什么东西(特别是compose方法,或者将多个阶段聚合到一个阶段的类似机制)甚至是Java类型系统支持的?我现在正在对它进行攻击,并且我在一个涉及反射和许多未经检查的泛型类型的非常丑陋的地方结束。
我是朝着错误的方向前进,还是尝试用Java做一件合理的事情?非常感谢提前!
答案 0 :(得分:1)
您没有发布足够的实施细节来显示类型安全问题的位置,但这里是我如何解决问题:
首先,不要让整个事情过于通用,让你的特殊情况特定于他们的输入和输出
然后创建一个实现Stage
的复合阶段,并将两个阶段合并为一个最终结果。
这是一个非常简单的实现
public class StageComposit<A, B, C> implements Stage<A, C> {
final Stage<A, B> stage1;
final Stage<B, C> stage2;
public StageComposit(Stage<A, B> stage1, Stage<B, C> stage2) {
this.stage1 = stage1;
this.stage2 = stage2;
}
@Override
public StageResult<C> process(StageResult<A> input) {
return stage2.process(stage1.process(input));
}
}
舞台结果
public class StageResult<O> {
final O result;
public StageResult(O result) {
this.result = result;
}
public O get() {
return result;
}
}
具体阶段示例:
public class EpochInputStage implements Stage<Long, Date> {
@Override
public StageResult<Date> process(StageResult<Long> input) {
return new StageResult<Date>(new Date(input.get()));
}
}
public class DateFormatStage implements Stage<Date, String> {
@Override
public StageResult<String> process(StageResult<Date> input) {
return new StageResult<String>(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(input.get()));
}
}
public class InputSplitStage implements Stage<String, List<String>> {
@Override
public StageResult<List<String>> process(StageResult<String> input) {
return new StageResult<List<String>>(
Arrays.asList(input.get().split("[-:\\s]")));
}
}
最后是一个小测试,演示了如何将所有
组合起来public class StageTest {
@Test
public void process() {
EpochInputStage efis = new EpochInputStage();
DateFormatStage dfs = new DateFormatStage();
InputSplitStage iss = new InputSplitStage();
Stage<Long, String> sc1 =
new StageComposit<Long, Date, String>(efis, dfs);
Stage<Long, List<String>> sc2 =
new StageComposit<Long, String, List<String>>(sc1, iss);
StageResult<List<String>> result =
sc2.process(new StageResult<Long>(System.currentTimeMillis()));
System.out.print(result.get());
}
}
当前时间的输出将是字符串列表
[2015, 06, 24, 16, 27, 55]
如您所见,没有类型安全问题或任何类型的铸件。当您需要处理其他类型的输入和输出或将它们转换为下一阶段的套件时,只需编写一个新的Stage
并将其连接到您的阶段处理链中。
答案 1 :(得分:0)
您可能需要考虑使用复合模式或decorator pattern。对于装饰者,每个阶段将包裹或装饰前一个阶段。要做到这一点,你要让每个阶段都实现接口,因为你正在允许一个阶段包含另一个阶段。
process()方法不再需要接受StageResult参数,因为它可以调用包含的Stage的process()方法本身,获取StageResult并执行自己的处理,返回另一个StageResult。
一个优点是您可以在运行时重新构建管道。
每个可能包含另一个舞台的舞台都可以扩展ComposableStage,作为流程终点的每个舞台都可以扩展LeafStage。请注意,我只是使用这些术语按功能命名类,但您可以创建更具想象力的名称。