The classifications and typical uses are
| Pattern | Category | When to use it |
|---|---|---|
| Command pattern | Behavioral design pattern | Represent a request as an object, separating its invocation from the receiver. Commands can be queued or logged; undo additionally needs enough information to reverse the particular request. |
| Strategy pattern | Behavioral design pattern | Select among interchangeable algorithms through a common interface, such as different routing or scoring policies. |
| Adapter pattern | Structural design pattern | Wrap an existing component so clients can use it through a different required interface without changing that component. |
| State pattern | Behavioral design pattern | Delegate behavior to an object representing the current state, with transitions selecting another state object instead of scattering conditionals across operations. |
| Visitor pattern | Behavioral design pattern | Add operations across a relatively stable element-class hierarchy, commonly through double dispatch. Adding a new element type then usually requires updating the visitors. |
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.