[concurrency-interest] Adding interfaces Sink and Source?

Adam Messinger adam@bea.com
Wed, 6 Oct 2004 10:44:15 -0700


Josh,

We make use of this pattern in our utility classes in the WebLogic
Server product, though we've tended to call a class which like this a
DelegatingFoo, rather than a ForwardingFoo.  We have delegating versions
of all of the collections and associated types including Iterator and
Map, as well as delegating versions of streams and readers and of many
internal interfaces as well.  We've found them to be quite useful.  

I agree that it would be a good addition to your book - maybe for a
later edition?

Cheers!

Adam

> -----Original Message-----
> From: concurrency-interest-admin@cs.oswego.edu 
> [mailto:concurrency-interest-admin@cs.oswego.edu] On Behalf 
> Of Joshua Bloch
> Sent: Sunday, October 03, 2004 10:18 AM
> To: concurrency-interest@altair.cs.oswego.edu
> Subject: Re: [concurrency-interest] Adding interfaces Sink and Source?
> 
> 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
>