这是 Java 中的设计模式问题。
我正在设计一个 java .jar 文件作为管理和处理某种形式数据的框架。我希望最终用户能够在特定限制条件下以特定方式指定“管道”配置。这些部分是生产者和/或消费者,我知道如何实现它们,但它们之间的联系让我感到困惑……这是一个与我的应用程序类似的废话示例。
假设我已经实现了这些元素:
AppleTree
=> 生产苹果ApplePieMaker
=> 吃苹果, 生产苹果派ApplePress
=> 吃苹果,生产苹果酒AppleSave
=> 存储苹果, 苹果派或苹果酒 文件AppleLoad
=> 从AppleSave
生成的文件中“重构”苹果、苹果派或苹果酒
ApplePieMonitor
=> 在制作苹果派时以 GUI 格式在屏幕上显示它们
现在我希望用户能够指定如下内容:
苹果树 |苹果出版社 | AppleSave cider1.sav
(生产苹果,制成苹果酒,保存到文件中)苹果树 | AppleSave apple1.sav
(生产苹果,保存到文件中)AppleLoad apple1.sav |苹果派制作者 | ApplePieMonitor
(获取保存的苹果,将它们做成馅饼,在 GUI 的屏幕上显示结果)(不确定如何说明这一点,但可以指定如下)
AppleTree tree1, ApplePieMaker piemaker1 < tree1, AppleSave apples.sav < tree1, AppleSave @select(*.sav) < piemaker1, ApplePress press1 < tree1, AppleSave cider.sav < press1, ApplePieMonitor piemon1 < piemaker1
(生产苹果,做成馅饼和苹果酒,将苹果、馅饼和苹果酒保存到单独的文件中,馅饼到运行时用户选择的文件,其他到预先确定的文件,同时显示GUI 中屏幕上的馅饼)
所以我对如何构建配置文件有一个粗略的想法:即将事物构造成最多具有 1 个输入和最多 1 个输出的元素,然后为每个实例化的元素命名,如果它有输入,指定提供输入的元素的名称。
我不清楚的是如何在程序运行时耦合程序的元素。也许要走的路是拥有多个接口(interface),如 AppleConsumer
、ApplePieConsumer
等,因此 ApplePieMaker
将实现 AppleConsumer
接口(interface)(包括方法 consumeApple()
)和一个 AppleTree
将实现一个可以在启动时注册消费者的 AppleProducer
接口(interface),所以每次 AppleTree
产生一个苹果时,它都有一个消费者列表,并在每个消费者上调用 consumeApple()
,然后做正确的事情而不需要AppleTree
必须知道他们在用苹果做什么....
有什么建议吗?这种东西有名字吗?我在设计模式方面并不是很有经验。
编辑:我的最终用户不知道也不关心 Java。他们只需要能够设置一个配置文件(我试图尽可能简单,以便我可以给他们一些很好的例子)并运行我的程序,该程序将读取配置文件,构造元素, Hook 它们一起,走。所有的元素都在我的掌控之中,我不需要支持插件,所以我不必超通用。
最佳答案
我不久前遇到过这个问题,在 Java 中明确指定有点困难,因为我的输入和输出可能是复数。但是,当您确定只有一个输入和输出时(因为文件是一种特定类型的输出,对吗?),您可以尝试使用已检查的泛型。
一个处理步骤的实现(我称之为过滤器)有两个检查过的类型参数:输入和输出(假设它们都扩展了一个公共(public)接口(interface),并且对于你注入(inject)的每个新类型在系统中,您将子类化该接口(interface))。
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
数组。通过兼容,我打算对于每个过滤器,它的 Output 与其跟随者 Input 的类型相同,第一个过滤器有一个 Input 匹配您的整体输入类型,最后一个过滤器有一个与预期结果类型相匹配的输出。这在实践中很容易验证,因为我们使用的是经过检查的泛型。
过滤器链因此是另一个(复合)过滤器。应在构造函数中检查复合过滤器的兼容性,并最终确定复合过滤器的数组。没有准确的方法来表达构造函数参数的“链接”属性(兼容性)与泛型,因此您将不得不使用裸类型来表达,这有点不干净。
以更繁琐的编写为代价的另一种绕过此限制的方法是像这样更改过滤器的定义:
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);
关于java - 在运行时配置 "plumbing",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/562404/