[concurrency-interest] Adding interfaces Sink and Source?

Joshua Bloch josh@bloch.us
Mon, 4 Oct 2004 18:58:35 -0700


Yep, I know it's the decorator pattern (and did when I designed the
Collection wrappers in '97).  Neal's observation is that providing
"generic forwarder" classes makes a nice basis for writing custom
decorators.  Another interesting option is linguistic support for
forwarding.

          Josh


----- Original Message -----
From: Ron Bense <eqmel@comcast.net>
Date: Mon, 04 Oct 2004 20:04:28 -0500
Subject: Re: [concurrency-interest] Adding interfaces Sink and Source?
To: josh@bloch.us
Cc: concurrency-interest@altair.cs.oswego.edu

Check the Decorator pattern:

Generic reasonable explanation which mentions the oft overlooked I/O
implementation:
http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/DecoratorPattern.htm

and everyone's favorite SWING based examples:
http://www.onjava.com/pub/a/onjava/2003/02/05/decorator.html

Ron.

Joshua Bloch wrote:



Folks, Vaguely related to this, here's a cool pattern for use when you
want to do several forwarding wrappers of the same interface. I
learned about this pattern from Neal, after I wrote my book. Had I
known about it earlier, it would probably be in the book. The basic
idea is to make a helper wrapper class that does nothing but
forwarding: public class ForwardingQueue<E> extends Queue<E> { private
final Queue<E> queue; ForwardingQueue(Queue<E> queue) { this.queue =
queue; } boolean offer(E o) { return queue.offer(o); } E poll() {
return queue.poll(); } E remove(); { return queue.remove(); } E
peek(); { return queue.peek(); } E element(); { return
queue.element(); } ... // add Collections methods or (much better)
extend forwardingCollection } Then you can make specialized wrappers
by overriding only the methods where you want to do something other
than vanilla forwarding, e.g.: public class Collections { public
static <E> Queue<E> putOnlyQueue(Queue<? super E> queue) { return new
ForwardingQueue<E>() { E poll(); { throw new UnsupportedOperation(); }
E remove(); { throw new UnsupportedOperation(); } E peek(); { throw
new UnsupportedOperation(); } E element(); { throw new
UnsupportedOperation(); } ... // Eliminate some collections ops also
}; } pubic static <E> Queue<E> takeOnlyQueue(Queue<? extends E> queue)
{ return new ForwardingQueue<E>() { boolean offer(E o) { throw new
UnsupportedOperation(); } ... // Eliminate some collections ops also
}; } ... } You get the idea. I have never actually used this pattern
in practice, but that's only because I learned about it too late. I
think it's a good idea. Josh On Sat, 02 Oct 2004 21:38:10 -0400, Tim
Peierls <tim@peierls.net> wrote:
Doug Lea wrote: 
The way we left it, it is still possible for individual developers to
unify usages themselves by adding a bit of glue. For example:
interface Puttable<E> { void put(E x) throws Exception; } class
MyLinkedQueue<E> extends LinkedQueue<E> implements Puttable<E> { ... }
Expanding on this a bit, it might be convenient to use wrapper
factories that convert BlockingQueues to custom Puttable and Takable
interfaces. class QUtil { static Puttable<E> asPuttable(final
BlockingQueue<E> q) { return new Puttable<E>() { public void put(E e)
throws InterruptedException { q.put(e); } } } static Takable<E>
asTakable(final BlockingQueue<E> q) { return new Takable<E>() { public
E take() throws InterruptedException { return q.take(); } } } But if
the only reason you miss Puttable and Takable is the way that they
prevented consumers from accidentally or maliciously accessing
producer methods and vice versa, then a simple way to achieve the same
effect without introducing new interfaces is via a factory that
creates wrapped blocking queues that throw UOE on all but a subset of
the queue methods: class QUtil { static BlockingQueue<E>
putOnlyBlockingQueue(final BlockingQueue<E> q) { return new
BlockingQueue<E>() { public void put(E e) throws InterruptedException
{ q.put(e); } // similarly for all "put"-like methods that you want to
support public E take() { throw new UnsupportedOperationException(); }
// similarly for all "take"-like methods and anything else you //
don't want to support }; } static BlockingQueue<E>
takeOnlyBlockingQueue(final BlockingQueue<E> q) { return new
BlockingQueue<E>() { public E take() throws InterruptedException {
return q.take(); } // similarly for all "take"-like methods that you
want to support public void put(E e) { throw new
UnsupportedOperationException(); } // similarly for all "put"-like
methods and anything else you // don't want to support }; } } Use this
utility to wrap a queue before handing it over to producers and
consumers. For example, interface Producer { public void
produceTo(BlockingQueue<Foo> putOnlyQueue); } interface Consumer {
public void consumeFrom(BlockingQueue<Foo> takeOnlyQueue); } void
start(Collection<Producer> producers, Collection<Consumer> consumers,
BlockingQueue<Foo> fooq) { for (Producer producer : producers)
producer.produceTo(QUtil.putOnlyBlockingQueue(fooq)); for (Consumer
consumer : consumers)
consumer.consumeFrom(QUtil.takeOnlyBlockingQueue(fooq)); } Now
malicious or broken producers and consumers can't touch the "wrong"
end of the queue. This approach trades away compile time type
information in favor of a smaller set of interfaces and run time
checking, roughly analogous to the design tradeoff whose resolution in
the Collections framework resulted in the Collections.unmodifiableXXX
static methods. --tim _______________________________________________
Concurrency-interest mailing list
Concurrency-interest@altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________ Concurrency-interest
mailing list Concurrency-interest@altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________ Concurrency-interest
mailing list Concurrency-interest@altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest