这是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 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个输出的元素,然后为每个实例化元素命名,并且如果它具有输入,指定提供输入的元素的名称。
我不清楚的是如何在程序运行时耦合程序的元素。也许要走的路是拥有许多接口,例如AppleConsumer
,ApplePieConsumer
等,因此ApplePieMaker
将实现AppleConsumer
接口(包括方法{{ 1}})和consumeApple()
将实现一个AppleTree
接口,可以在启动时注册消费者,这样每次AppleProducer
生成一个苹果时,它都有一个消费者列表和在每个人身上调用AppleTree
,然后做出正确的事情,而consumeApple()
不必知道他们在用苹果做什么......
有什么建议吗?这种东西有名字吗?我在设计模式方面并不是那么有经验。
编辑:我的最终用户不知道也不关心Java。他们只需要能够设置一个配置文件(我想尽可能简单,所以我可以给他们一些很好的例子)并运行我的程序,它将读取配置文件,构造元素,钩住它们在一起,去吧。所有元素都在我的控制之下,我不需要支持插件,所以我不必非常一般。
答案 0 :(得分: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
的数组。通过兼容,我打算对于每个过滤器,其输出与其跟随者输入的类型相同,第一个过滤器具有匹配的输入您的整体输入类型,最后一个过滤器具有与预期结果类型匹配的输出。在实践中,这很容易验证,因为我们使用的是检查泛型。
另一种解决此限制的方法是以更加繁琐的写作为代价,更改过滤器的定义如下:
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);
答案 1 :(得分:1)
我无法帮助它,我必须为此锻炼一些东西。
所以就是这样。
你已经有了关于苹果生产者/消费者的想法,所以我就是这样做的。
创建三个接口并按如下方式实现:
我们的想法是拥有一个通用产品,由通用生产商生产并由通用消费者使用。
完成后,您可以像描述它一样创建配置文件,并解析它以为每个元素创建一个新实例。
element1 | element2 | element3 <parameters> | element 4
在地图中,您可以创建元素名称并将其映射到将创建新实例的类。
让我们说
map.put( "AppleTree", YouAppleTreeClass.class );
因此,每次读取配置中的元素时,都会创建实例:
for( String item: line ) {
Object o = map.get( item ).newInstance();
}
最后,您必须验证配置的结构,但基本上可能是这样的:
创建和链接所有对象后,即可开始制作。
你需要锻炼一些东西,但它们很容易:
最后的注意事项:下面的代码,只是一个划痕,您可能真的考虑使用依赖注入器来完成这项工作,但当然需要花一点时间来学习它。
配置解析应该手工完成,因为您使用的格式对于最终用户来说是唯一的,并且应该非常简单。你仍然可以在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 ));
}
}
答案 2 :(得分:0)
我相信你想要做的事情可以在Spring框架内完成。它使用依赖注入来说“为了创建X我需要Y,所以找到产生Y的东西,看看你需要什么来创建它”。
我可能错了,但我建议你看看。
答案 3 :(得分:0)
尝试使用Java Beanshell。
BeanShell是一个小型,免费,可嵌入的Java源代码解释器,具有用Java编写的对象脚本语言功能。 BeanShell动态执行标准Java语法,并使用常见的脚本编写方便性扩展它,例如松散类型,命令和方法闭包,如Perl和JavaScript中的那些。
答案 4 :(得分:0)
你需要某种类型的注册表,生产者可以注册(嗨,我是一棵苹果树,我生产苹果),然后消费者可以查找谁生产苹果。这也可以在消费者注册兴趣和生产者抬头的情况下反向完成。我使用JMX做了类似的事情,其中一个Object可以向JMX Server查询产生某种类型Message的Object,然后向该Object注册(Publish / Subscribe)。我现在移植该应用程序以使用具有类似功能的OSGi