首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在运行时配置“管道”

在运行时配置“管道”
EN

Stack Overflow用户
提问于 2009-02-18 19:06:21
回答 5查看 208关注 0票数 3

这是Java中的一个设计模式问题。

我正在设计一个java .jar文件,作为管理和处理某种形式数据的框架。我希望最终用户能够在一定的约束范围内,以某种方式指定“管道”配置。这些部件是生产者和/或消费者,我知道如何实现它们,但是连接让我感到困惑.下面是一个与我的应用程序类似的无稽之谈。

假设我已经实现了以下元素:

  • AppleTree =>生产苹果
  • ApplePieMaker =>吃苹果,生产苹果派
  • ApplePress =>吃苹果,生产苹果酒
  • AppleSave =>将苹果、苹果派或苹果苹果酒存储在一个文件中。
  • AppleLoad =>从AppleSave制作的文件中“重组”苹果、苹果派或苹果苹果酒。
  • ApplePieMonitor =>在屏幕上以图形用户界面格式显示苹果派

现在,我希望用户能够指定如下内容:

  • AppleTree | ApplePress | AppleSave cider1.sav (生产苹果,制作苹果酒,保存到文件中)
  • AppleTree | AppleSave apple1.sav (生产苹果,保存到文件中)
  • AppleLoad apple1.sav | ApplePieMaker | ApplePieMonitor (把保存下来的苹果做成馅饼,在图形用户界面上显示结果)
  • (不确定如何说明这一点,但可以按以下方式指定) AppleTree tree1,ApplePieMaker piemaker1 < tree1,AppleSave apples.sav < tree1,AppleSave @select(*.sav) < piemaker1,ApplePress press1 < tree1,AppleSave cider.sav < press1,tree1<

(生产苹果,将它们制成馅饼和苹果酒,将苹果、馅饼和苹果酒保存到单独的文件中,派在运行时由用户选择的文件,其他的将预先确定文件,并在一个GUI中显示屏幕上的馅饼)

因此,我对如何构建配置文件有一个粗略的想法:即将事物构造为最多有一个输入和最多一个输出的元素,然后对每个实例化的元素命名它,如果它有一个输入,则指定提供输入的元素的名称。

我不清楚的是,如何在程序运行时耦合程序的元素。可能的方法是拥有许多接口,比如AppleConsumerApplePieConsumer等等,这样ApplePieMaker就可以实现AppleConsumer接口(包括AppleConsumer接口)。方法( consumeApple()),AppleTree将实现一个AppleProducer接口,该接口可以在启动时注册消费者,这样,每当AppleTree生产苹果时,它都有其消费者列表并在每个用户上调用consumeApple(),这样做是正确的,而AppleTree不必知道他们对苹果做了什么.

有什么建议吗?这类东西有名字吗?我对设计模式并不是很有经验。

编辑:我的最终用户不知道也不关心Java。他们只需要能够设置一个配置文件(我正在尽量简化,这样我就可以给他们提供一些好的例子)并运行我的程序,它将读取配置文件,构造元素,将它们连接在一起,然后继续运行。所有的元素都在我的控制之下,我不需要支持插件,所以我不需要成为超级通用的。

EN

回答 5

Stack Overflow用户

发布于 2009-02-18 19:32:58

不久前,我遇到了这个问题,在Java中很难清晰地指定,因为我的输入和输出可以是复数的。但是,当您确保有一个输入和输出时(因为一个文件是一种特定的输出,对吗?),您可以尝试使用选中的泛型。

处理步骤实现(我将调用过滤器)有两个检查过的类型参数:输入和输出(例如,假设它们都扩展了一个公共接口,对于您在系统中注入的每一个新类型,您将对该接口进行子类)。

代码语言:javascript
复制
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的数组。通过兼容,我打算让每个过滤器的输出与它的跟随器输入相同类型,第一个过滤器有一个与您的总体输入类型匹配的输入,最后一个过滤器具有一个与预期结果类型匹配的输出。这在实践中很容易验证,因为我们使用的是已检查的泛型。

因此,过滤器链是另一个(复合)过滤器。复合过滤器的兼容性应该在构造函数中检查,而复合过滤器的数组应该是最终的。没有准确的方法来用泛型来表示该构造函数的参数的“链接”属性(兼容性),所以您必须使用裸类型,这是有点不干净的。

另一种克服这一限制的方法是修改过滤器的定义,而代价是编写起来更加繁琐:

代码语言:javascript
复制
public interface Filter<Input extends Type, Output extends Type> {

   public Class<Input> getInputType();

   public Class<Output> getOutputType();

   public Output out process(Input in);

}

然后,我们必须将复合过滤器定义为过滤对的嵌入,从而定义:

代码语言:javascript
复制
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));
  }
}

因此,组合过滤器只是一个写作问题:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2009-02-18 23:25:13

我情不自禁,我得为此做点什么。

所以就是这样了。

你已经有了苹果生产商/消费者的想法,所以我会这样做。

创建三个接口并按以下方式实现:

  • 产品-苹果,AppleCider,ApplePie,
  • 制片人- AppleTree,ApplePress,ApplePieMaker,AppleLoad,
  • 消费者- ApplePress (消费苹果),ApplePieMaker (消费苹果),AppleMonitor,AppleSave。

其想法是拥有一种通用产品,由非专利生产者生产,并由普通消费者消费。

一旦有了它,您就可以按照描述它的方式创建配置文件,并解析它来为每个元素创建一个新的实例。

代码语言:javascript
复制
 element1 | element2 | element3 <parameters> | element 4

在映射中,创建元素名并将其映射到将创建新实例的类。

比方说

代码语言:javascript
复制
map.put( "AppleTree", YouAppleTreeClass.class );

因此,每次在配置中读取元素时,都会创建实例:

代码语言:javascript
复制
for( String item: line )  { 
    Object o = map.get( item ).newInstance();
}

最后,您必须验证配置的结构,但基本上可以如下所示:

  • 第一个元素应该是生产者。
  • 最后一个应该是消费者
  • 任何中间产品都应该是生产者-消费者。
  • 您可以解析所需的参数(例如,保存数据的文件)

一旦创建并链接了所有对象,就会开始生成。

有些东西你必须锻炼,但它们很容易:

  1. 参数传递(将从其中保存/加载它们的文件)
  2. 对象在不同配置中重用(始终使用相同的AppleTree )

最后注意:下面的代码,只是一个划痕,您可能真的会考虑使用依赖注入器来完成这项工作,但是当然需要花一些时间来学习它。

配置解析应该手工完成,因为您所使用的格式对于最终用户来说是唯一的,并且应该非常简单。尽管如此,您仍然可以在jar中交付您想要的复杂程度(使用您需要的任意数量的框架)。

您还可以查看以下设计模式:

  • 复合
  • 观察者
  • 解释器

下面的实现是一种怪物--这三个(我没有编译它,只是抛出一些代码来展示这个想法是什么样子的)

我希望这能帮到你。

代码语言:javascript
复制
/**
 * 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 ));
    }
}
票数 1
EN

Stack Overflow用户

发布于 2009-02-18 19:15:51

我相信您想要做的事情可以在Spring框架内完成。它使用依赖注入来表示“为了创建X,我需要Y,所以找到产生Y的东西,看看您需要什么来创建它”。

我可能错了,但我建议你看看。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/562404

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档