DulcetAirman icon

Decorator implementing Comparable

DulcetAirman | PRO | 12/15/14 12:25:32 PM UTC | 0 ⭐ | 1053 👁️ | Never ⏰ | []
Java |

5.65 KB

|

None

|

0 👍

/

0 👎

package com.example.foo;
 
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
 
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
 
/**
 * This is to demponstrate how to implement {@link Comparable} in a decorator class.
 * <p>
 * Blog post: https://humanoidreadable.wordpress.com/2014/12/15/decorator-implementing-comparable/
 * <p>
 * {@link IFoo}: The abstract model.<br>
 * {@link Foo}: Some basic implementation.<br>
 * {@link IFoo}: The decorator.<br>
 * {@link Proxy}: Proxy (reflection).
 * 
 * @author Claude Martin
 *
 */
public class SomeClass {
 
  public static void main(String[] args) throws Exception {
    // Classic decoration:
    IFoo foo0 = new Foo(42);
    IFoo foo1 = new FooDecorator(foo0);
    IFoo foo2 = new Foo(1337);
    IFoo foo3 = new FooDecorator(foo2);
    // Decorated decorator: [[1337]]
    IFoo foo4 = new FooDecorator(foo3);
 
    // Decoration by Proxy:
    IFoo.Handler handler = SomeClass::handle;
    IFoo foo5 = IFoo.decorate(foo0, handler);
    IFoo foo6 = IFoo.decorate(foo2, handler);
    IFoo foo7 = IFoo.decorate(foo3, handler);
    IFoo foo8 = IFoo.decorate(foo7, handler);
    // foo9->foo7->foo3->foo2: [{[1337]}]
    IFoo foo9 = new FooDecorator(foo7);
 
    List<IFoo> foos = asList(foo0, foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9);
    for (IFoo a : foos) {
      for (IFoo b : foos) {
        int i = a.compareTo(b);
        System.out.println(format("%s.compareTo(%s) = %d", a, b, i));
        boolean e = a.equals(b);
        System.out.println(format("%s.equals(%s) = %s", a, b, e));
      }
    }
  }
 
  /**
   * Wraps toString in "{?}".
   * 
   * @see IFoo#decorate(IFoo, com.example.foo.SomeClass.IFoo.Handler)
   */
  private static Object handle(IFoo foo, Method method, Object[] arguments) throws Throwable {
    // decoration:
    if (method.getName().equals("toString"))
      return "{" + foo.toString() + "}";
    // delegation:
    return method.invoke(foo, arguments);
  }
 
  /**
   * A decorator of an instance of IFoo.
   * 
   * This decorates the toString method by wrapping the value in "[?]".
   */
  public static class FooDecorator implements IFoo {
    private final IFoo field;
 
    public FooDecorator(IFoo field) {
      super();
      this.field = requireNonNull(field, "field");
    }
 
    @Override
    public int hashCode() {
      return this.field.hashCode();
    }
 
    @Override
    public boolean equals(Object other) {
      return this == other || this.field.equals(other);
    }
 
    @Override
    public int compareTo(IFoo other) {
      return this.field.compareTo(other);
    }
 
    @Override
    public int getValue() {
      return this.field.getValue();
    }
 
    @Override
    public String toString() {
      return "[" + this.field.toString() + "]";
    }
 
  }
 
  /**
   * Implementation of IFoo.
   * 
   * I recommend to make this class final or even immutable.
   */
  public/* final */static class Foo implements IFoo {
    private final int value;
 
    public Foo(int value) {
      this.value = value;
    }
 
    @Override
    public int compareTo(IFoo other) {
      return Integer.compare(this.getValue(), other.getValue());
    }
 
    @Override
    public int getValue() {
      return this.value;
    }
 
    @Override
    public String toString() {
      return Integer.toString(this.value);
    }
 
    @Override
    public int hashCode() {
      return this.value;
    }
 
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof IFoo))
        return false;
      IFoo other = (IFoo) obj;
      if (this.getValue() != other.getValue())
        return false;
      return true;
    }
 
    @SuppressWarnings("static-method")
    public boolean canEqual(Object other) {
      return (other instanceof Foo);
    }
  }
 
  /**
   * The interface which models the abstract idea of the "Foo".
   */
  interface IFoo extends Comparable<IFoo> {
    public int getValue();
 
    /**
     * TODO: This should explain in great detail when two instances of IFoo are equal. In this case
     * it would be equal if both have the same value. But what if some implementation has more
     * fields? cf: http://www.artima.com/lejava/articles/equality.html
     */
    @Override
    public boolean equals(Object obj);
 
    /**
     * TODO: Any type with a special definition of {@link #equals(Object)} should also specify how a
     * hashCode must be calculated. In this case it's just the value.
     */
    @Override
    public int hashCode();
 
    /**
     * TODO: This should explain in great detail how two instances of IFoo are to be compared. In
     * this case it compares the values. But what if some implementation has more fields?
     */
    @Override
    public int compareTo(IFoo o);
 
    /** Decorates an instance of IFoo using a given handler. */
    public static IFoo decorate(final IFoo foo, final Handler handler) {
      requireNonNull(foo, "foo");
      requireNonNull(handler, "handler");
      return (IFoo) Proxy.newProxyInstance(IFoo.class.getClassLoader(), new Class[] { IFoo.class },
          (proxy, method, args) -> {
            return handler.invoke(foo, method, args);
          });
    }
 
    /** Handler of method calls to a decorated instance if IFoo. */
    @FunctionalInterface
    public static interface Handler {
      public Object invoke(IFoo foo, Method method, Object[] args) throws Throwable;
    }
  }
 
}

Comments