[concurrency-interest] Adding interfaces Sink and Source?

Ron Bense eqmel@comcast.net
Mon, 04 Oct 2004 20:04:28 -0500


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
  <title></title>
</head>
<body bgcolor="#ffffff" text="#000000">
Check the Decorator pattern:<br>
<br>
Generic reasonable explanation which mentions the oft overlooked I/O
implementation:<br>
<a class="moz-txt-link-freetext" href="http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/DecoratorPattern.htm">http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/DecoratorPattern.htm</a><br>
<br>
and everyone's favorite SWING based examples:<br>
<a class="moz-txt-link-freetext" href="http://www.onjava.com/pub/a/onjava/2003/02/05/decorator.html">http://www.onjava.com/pub/a/onjava/2003/02/05/decorator.html</a><br>
<br>
Ron.<br>
<br>
Joshua Bloch wrote:<br>
<blockquote cite="midb097ac5104100310183fa32263@mail.gmail.com"
 type="cite">
  <pre wrap="">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&lt;E&gt; extends Queue&lt;E&gt; {
    private final Queue&lt;E&gt; queue;

    ForwardingQueue(Queue&lt;E&gt; 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 &lt;E&gt; Queue&lt;E&gt; putOnlyQueue(Queue&lt;? super E&gt; queue) {
        return new ForwardingQueue&lt;E&gt;() {
            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 &lt;E&gt; Queue&lt;E&gt; takeOnlyQueue(Queue&lt;? extends E&gt; queue) {
        return new ForwardingQueue&lt;E&gt;() {
            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 <a class="moz-txt-link-rfc2396E" href="mailto:tim@peierls.net">&lt;tim@peierls.net&gt;</a> wrote:
  </pre>
  <blockquote type="cite">
    <pre wrap="">Doug Lea wrote:
    </pre>
    <blockquote type="cite">
      <pre wrap="">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&lt;E&gt; { void put(E x) throws Exception; }
class MyLinkedQueue&lt;E&gt; extends LinkedQueue&lt;E&gt; implements Puttable&lt;E&gt; {
  ...
}
      </pre>
    </blockquote>
    <pre wrap="">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&lt;E&gt; asPuttable(final BlockingQueue&lt;E&gt; q) {
    return new Puttable&lt;E&gt;() {
      public void put(E e) throws InterruptedException { q.put(e); }
    }
  }
  static Takable&lt;E&gt; asTakable(final BlockingQueue&lt;E&gt; q) {
    return new Takable&lt;E&gt;() {
      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&lt;E&gt; putOnlyBlockingQueue(final BlockingQueue&lt;E&gt; q) {
    return new BlockingQueue&lt;E&gt;() {
      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&lt;E&gt; takeOnlyBlockingQueue(final BlockingQueue&lt;E&gt; q) {
    return new BlockingQueue&lt;E&gt;() {
      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&lt;Foo&gt; putOnlyQueue);
  }

  interface Consumer {
    public void consumeFrom(BlockingQueue&lt;Foo&gt; takeOnlyQueue);
  }

  void start(Collection&lt;Producer&gt; producers,
             Collection&lt;Consumer&gt; consumers,
             BlockingQueue&lt;Foo&gt; 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
<a class="moz-txt-link-abbreviated" href="mailto:Concurrency-interest@altair.cs.oswego.edu">Concurrency-interest@altair.cs.oswego.edu</a>
<a class="moz-txt-link-freetext" href="http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest">http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest</a>

    </pre>
  </blockquote>
  <pre wrap=""><!---->_______________________________________________
Concurrency-interest mailing list
<a class="moz-txt-link-abbreviated" href="mailto:Concurrency-interest@altair.cs.oswego.edu">Concurrency-interest@altair.cs.oswego.edu</a>
<a class="moz-txt-link-freetext" href="http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest">http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest</a>

  </pre>
</blockquote>
<br>
</body>
</html>