The pipe object interface supports command-query separation: consuming an input changes state, while querying the result observes the current output without feeding another input. A client can inspect the result repeatedly, including an initial result when the concrete pipe defines one. That is useful for accumulated statistics.
The channel object interface instead packages an input update and its associated output into one call. For request-and-result processing this is simpler: the caller cannot forget which update precedes a query. A pipe caller must respect the intended order of operations, and interleaving other callers between consume and result can change the observation. A single channel object method is not automatically thread-safe either; synchronization is a separate property.
Neither object interface is universally better. The choice depends on whether clients need independent observation of state or a combined transformation step. The adapters below can reconcile their protocols, provided the initial-result policy is made explicit.
Use the adapter pattern, keeping the existing component software object in both directions:
class PipeAsChannel(pipe: Pipe) extends Channel {
  override def apply(n: Int): Int = {
    pipe.consume(n)
    pipe.result
  }
}

class ChannelAsPipe(channel: Channel, initialResult: Int) extends Pipe {
  private var last = initialResult
  override def consume(n: Int): Unit = {
    last = channel(n)
  }
  override def result: Int = last
}
The first adapter feeds the input before reading the pipe's output. The second invokes the channel exactly once per consume and caches its returned output; subsequent result queries do not advance it. Its initial result must be supplied explicitly, because a general channel object interface exposes no initial-output query. Calling the channel on a dummy input during construction would mutate arbitrary state and would invent behavior the object interface does not promise.
These adapters preserve the sequential update/output behavior after each real input, assuming no interfering access to the wrapped component. They do not claim a perfect inverse for every aspect of the original object interfaces: pipe initialization is additional observable state, whereas a channel alone does not specify that observation.

Articles by others on the same topic (0)

There are currently no matching articles.