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