MetaPipes

okram edited this page Jul 28, 2011 · 15 revisions

A metapipe is a pipe that “wraps” another pipe. MetaPipes are useful in that they can make decisions based upon the behavior of the pipe(s) they wrap. There are numerous metapipes and this section will discuss a few of the more interesting cases.

Basic Pattern

In many situations, the basic pattern used when creating a metapipe is to have the constructor of the metapipe take n number of pipes. Then for each incoming S object to the metapipe, it pushes that S object to its n pipes and makes a emit decision based upon how the pipes behave. In many cases, you can think of a metapipe as an “overseer” of its internal pipes. This idea is presented in the typical code pattern found in metapipes.

public E processNextStart() {
  S s = this.starts.next();
  for (Pipe<S, E> pipe : this.pipes) {
    pipe.setStarts(new SingleIterator<S>(s));
    E e = pipe.next();
    // decide about what to do given the output of the internal pipe
  }
  // if all pipes behave as desired, perhaps do:
  return s.getE();
}

SingleIterator<S> is a handy class that wraps an object for a single legal next(). This class is much more efficient than doing Arrays.asList(s).iterator().

Pipelines

A Pipeline is a commonly used pipe. A Pipeline<S,E> implements Pipe<S,E> and as such, a Pipeline is simply a Pipe. A Pipeline takes an ordered list of pipes in its constructor. It connects these pipes, whereby the input of pipe n is connected to the output of pipe n-1. Note that the output type of pipe n-1 must be the same type as the input to pipe n. This idea is elucidated in the diagram above.

The benefit of using a Pipeline is that is greatly reduces the complexity of a process and allows for easy reuse. It is analogous, in many ways, to creating a function to wrap a body of code/instructions. As such, the function serves as a blackbox with input and output.

And/Or Pipes

There are two logical FilterPipe pipes called AndFilterPipe<S> and OrFilterPipe<S>. These pipes take an object of type S and emit an object of type S. However, they only emit the S object if the collection of Pipe<S,Boolean> pipes that they wrap return true. For AndFilterPipe to emit its incoming S object, all of its wrapped pipes must return true that S object. For the OrFilterPipe, only one of its wrapped pipes must return true. If you want to see if a number is greater than or equal to 1 and less than 10, then use the following AndFilterPipe.

Pipe<Integer,Integer> pipeA = new ObjectFilterPipe<Integer>(1, ComparisonFilterPipe.GREATER_THAN_EQUAL);
Pipe<Integer,Integer> pipeB = new ObjectFilterPipe<Integer>(10, ComparisonFilterPipe.LESS_THAN);
Pipe<Integer,Integer> pipe1 = new AndFilterPipe<Integer>(new HasNextPipe<Integer>(pipeA), new HasNextPipe<Integer>(pipeB));
pipe1.setStarts(Arrays.asList(1,22,10,136,7,2,67));
while(pipe1.hasNext()) {
  System.out.print(pipe1.next() + "...");
}

The System.out of the previous code is as follows.

1...7...2...

Note the use of HasNextPipe<S> which implements Pipe<S,Boolean>. If the ObjectFilterPipe pipes that compose the AndFilterPipe filter their incoming S object, then they will return false on a call to hasNext(). Thus, HasNextPipe is useful for determining if a filter pipe has filtered an object.