DulcetAirman

Decorator implementing Comparable

Dec 15th, 2014
288
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package com.example.foo;
  2.  
  3. import static java.lang.String.format;
  4. import static java.util.Arrays.asList;
  5. import static java.util.Objects.requireNonNull;
  6.  
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Proxy;
  9. import java.util.List;
  10.  
  11. /**
  12.  * This is to demponstrate how to implement {@link Comparable} in a decorator class.
  13.  * <p>
  14.  * Blog post: https://humanoidreadable.wordpress.com/2014/12/15/decorator-implementing-comparable/
  15.  * <p>
  16.  * {@link IFoo}: The abstract model.<br>
  17.  * {@link Foo}: Some basic implementation.<br>
  18.  * {@link IFoo}: The decorator.<br>
  19.  * {@link Proxy}: Proxy (reflection).
  20.  *
  21.  * @author Claude Martin
  22.  *
  23.  */
  24. public class SomeClass {
  25.  
  26.   public static void main(String[] args) throws Exception {
  27.     // Classic decoration:
  28.     IFoo foo0 = new Foo(42);
  29.     IFoo foo1 = new FooDecorator(foo0);
  30.     IFoo foo2 = new Foo(1337);
  31.     IFoo foo3 = new FooDecorator(foo2);
  32.     // Decorated decorator: [[1337]]
  33.     IFoo foo4 = new FooDecorator(foo3);
  34.  
  35.     // Decoration by Proxy:
  36.     IFoo.Handler handler = SomeClass::handle;
  37.     IFoo foo5 = IFoo.decorate(foo0, handler);
  38.     IFoo foo6 = IFoo.decorate(foo2, handler);
  39.     IFoo foo7 = IFoo.decorate(foo3, handler);
  40.     IFoo foo8 = IFoo.decorate(foo7, handler);
  41.     // foo9->foo7->foo3->foo2: [{[1337]}]
  42.     IFoo foo9 = new FooDecorator(foo7);
  43.  
  44.     List<IFoo> foos = asList(foo0, foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9);
  45.     for (IFoo a : foos) {
  46.       for (IFoo b : foos) {
  47.         int i = a.compareTo(b);
  48.         System.out.println(format("%s.compareTo(%s) = %d", a, b, i));
  49.         boolean e = a.equals(b);
  50.         System.out.println(format("%s.equals(%s) = %s", a, b, e));
  51.       }
  52.     }
  53.   }
  54.  
  55.   /**
  56.    * Wraps toString in "{?}".
  57.    *
  58.    * @see IFoo#decorate(IFoo, com.example.foo.SomeClass.IFoo.Handler)
  59.    */
  60.   private static Object handle(IFoo foo, Method method, Object[] arguments) throws Throwable {
  61.     // decoration:
  62.     if (method.getName().equals("toString"))
  63.       return "{" + foo.toString() + "}";
  64.     // delegation:
  65.     return method.invoke(foo, arguments);
  66.   }
  67.  
  68.   /**
  69.    * A decorator of an instance of IFoo.
  70.    *
  71.    * This decorates the toString method by wrapping the value in "[?]".
  72.    */
  73.   public static class FooDecorator implements IFoo {
  74.     private final IFoo field;
  75.  
  76.     public FooDecorator(IFoo field) {
  77.       super();
  78.       this.field = requireNonNull(field, "field");
  79.     }
  80.  
  81.     @Override
  82.     public int hashCode() {
  83.       return this.field.hashCode();
  84.     }
  85.  
  86.     @Override
  87.     public boolean equals(Object other) {
  88.       return this == other || this.field.equals(other);
  89.     }
  90.  
  91.     @Override
  92.     public int compareTo(IFoo other) {
  93.       return this.field.compareTo(other);
  94.     }
  95.  
  96.     @Override
  97.     public int getValue() {
  98.       return this.field.getValue();
  99.     }
  100.  
  101.     @Override
  102.     public String toString() {
  103.       return "[" + this.field.toString() + "]";
  104.     }
  105.  
  106.   }
  107.  
  108.   /**
  109.    * Implementation of IFoo.
  110.    *
  111.    * I recommend to make this class final or even immutable.
  112.    */
  113.   public/* final */static class Foo implements IFoo {
  114.     private final int value;
  115.  
  116.     public Foo(int value) {
  117.       this.value = value;
  118.     }
  119.  
  120.     @Override
  121.     public int compareTo(IFoo other) {
  122.       return Integer.compare(this.getValue(), other.getValue());
  123.     }
  124.  
  125.     @Override
  126.     public int getValue() {
  127.       return this.value;
  128.     }
  129.  
  130.     @Override
  131.     public String toString() {
  132.       return Integer.toString(this.value);
  133.     }
  134.  
  135.     @Override
  136.     public int hashCode() {
  137.       return this.value;
  138.     }
  139.  
  140.     @Override
  141.     public boolean equals(Object obj) {
  142.       if (this == obj)
  143.         return true;
  144.       if (obj == null)
  145.         return false;
  146.       if (!(obj instanceof IFoo))
  147.         return false;
  148.       IFoo other = (IFoo) obj;
  149.       if (this.getValue() != other.getValue())
  150.         return false;
  151.       return true;
  152.     }
  153.  
  154.     @SuppressWarnings("static-method")
  155.     public boolean canEqual(Object other) {
  156.       return (other instanceof Foo);
  157.     }
  158.   }
  159.  
  160.   /**
  161.    * The interface which models the abstract idea of the "Foo".
  162.    */
  163.   interface IFoo extends Comparable<IFoo> {
  164.     public int getValue();
  165.  
  166.     /**
  167.      * TODO: This should explain in great detail when two instances of IFoo are equal. In this case
  168.      * it would be equal if both have the same value. But what if some implementation has more
  169.      * fields? cf: http://www.artima.com/lejava/articles/equality.html
  170.      */
  171.     @Override
  172.     public boolean equals(Object obj);
  173.  
  174.     /**
  175.      * TODO: Any type with a special definition of {@link #equals(Object)} should also specify how a
  176.      * hashCode must be calculated. In this case it's just the value.
  177.      */
  178.     @Override
  179.     public int hashCode();
  180.  
  181.     /**
  182.      * TODO: This should explain in great detail how two instances of IFoo are to be compared. In
  183.      * this case it compares the values. But what if some implementation has more fields?
  184.      */
  185.     @Override
  186.     public int compareTo(IFoo o);
  187.  
  188.     /** Decorates an instance of IFoo using a given handler. */
  189.     public static IFoo decorate(final IFoo foo, final Handler handler) {
  190.       requireNonNull(foo, "foo");
  191.       requireNonNull(handler, "handler");
  192.       return (IFoo) Proxy.newProxyInstance(IFoo.class.getClassLoader(), new Class[] { IFoo.class },
  193.           (proxy, method, args) -> {
  194.             return handler.invoke(foo, method, args);
  195.           });
  196.     }
  197.  
  198.     /** Handler of method calls to a decorated instance if IFoo. */
  199.     @FunctionalInterface
  200.     public static interface Handler {
  201.       public Object invoke(IFoo foo, Method method, Object[] args) throws Throwable;
  202.     }
  203.   }
  204.  
  205. }
RAW Paste Data