Last active
February 24, 2017 10:37
-
-
Save culmat/bee7afc71840a8035c5e5753d44e0de8 to your computer and use it in GitHub Desktop.
if it walks like a duck and talk like a duck ...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package common; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
public class DuckType implements InvocationHandler { | |
public static class Let { | |
private Object object; | |
public Let(Object object) { | |
this.object = object; | |
} | |
private void checkQuackLike(Object object, Class<?> interfaceClass) throws InvocationTargetException { | |
Class<?> candclass = object.getClass(); | |
StringBuilder sb = new StringBuilder(candclass.getName()+" does not implement\n"); | |
int initialLength = sb.length(); | |
for (Method method : interfaceClass.getMethods()) { | |
try { | |
candclass.getMethod(method.getName(), method.getParameterTypes()); | |
} catch (NoSuchMethodException e) { | |
sb.append(" - "+method+"\n"); | |
} | |
} | |
if(sb.length()> initialLength) throw new InvocationTargetException(null, sb.toString()); | |
} | |
public <T> T be(Class<T> interfaceClass) throws InvocationTargetException { | |
checkQuackLike(object, interfaceClass); | |
return implement(object, interfaceClass); | |
} | |
public <T> T wannaBe(Class<T> interfaceClass) { | |
return implement(object, interfaceClass); | |
} | |
private <T> T implement(Object object, Class<T> interfaceToImplement) { | |
return (T) Proxy.newProxyInstance(interfaceToImplement.getClassLoader(), | |
new Class[] { interfaceToImplement}, new DuckType(object)); | |
} | |
} | |
public static class Does { | |
private Class<? extends Object> candclass; | |
public Does(Object object) { | |
candclass = object.getClass(); | |
} | |
public boolean quackLike(Class<?> interfaceClass) { | |
for (Method method : interfaceClass.getMethods()) { | |
try { | |
candclass.getMethod(method.getName(), method.getParameterTypes()); | |
} catch (NoSuchMethodException e) { | |
return false; | |
} | |
} | |
return true; | |
} | |
} | |
public static Let let(Object object) { | |
return new Let(object); | |
} | |
public static Does does(Object object) { | |
return new Does(object); | |
} | |
protected DuckType(Object object) { | |
this.object = object; | |
this.objectClass = object.getClass(); | |
} | |
protected Object object; | |
protected Class objectClass; | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
Method realMethod = objectClass.getMethod(method.getName(), method.getParameterTypes()); | |
if (!realMethod.isAccessible()) { | |
realMethod.setAccessible(true); | |
} | |
return realMethod.invoke(object, args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package common; | |
import static common.DuckType.does; | |
import static common.DuckType.let; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertFalse; | |
import static org.junit.Assert.assertTrue; | |
import java.lang.reflect.InvocationTargetException; | |
import org.junit.Before; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.junit.rules.ExpectedException; | |
public class DuckTypeTest { | |
@Rule | |
public ExpectedException expectedEx = ExpectedException.none(); | |
public interface Duck { | |
String walk(); | |
String talk(); | |
} | |
class Chicken { | |
public String walk() { return "Chicken walks"; } | |
} | |
class Hen { | |
public String walk() { return "Hen walks"; } | |
public String talk() { return "Hen talks"; } | |
} | |
private Chicken chicken; | |
private Hen hen; | |
@Before | |
public void setUp() throws Exception { | |
chicken = new Chicken(); | |
hen = new Hen(); | |
} | |
@Test | |
public void chickenDoesNotQuackLikeDuck() { | |
assertFalse(does(chicken).quackLike(Duck.class)); | |
} | |
@Test | |
public void chickenCannotBeDuck() throws Exception { | |
expectedEx.expect(InvocationTargetException.class); | |
expectedEx.expectMessage(getClass().getName()+"$Chicken does not implement\n" + | |
" - public abstract java.lang.String "+getClass().getName()+"$Duck.talk()"); | |
let(chicken).be(Duck.class); | |
} | |
@Test | |
public void letChickenWannBeDuck() { | |
Duck duck = let(chicken).wannaBe(Duck.class); | |
assertEquals("Chicken walks", duck.walk()); | |
} | |
@Test | |
public void duckTypedHashCode() { | |
assertEquals(chicken.hashCode(), let(chicken).wannaBe(Duck.class).hashCode()); | |
} | |
@Test | |
public void duckTypedEquals() { | |
assertEquals(let(chicken).wannaBe(Duck.class), chicken); | |
} | |
@Test | |
public void henDoesQuackLikeDuck() { | |
assertTrue(does(hen).quackLike(Duck.class)); | |
} | |
@Test | |
public void letHenBeDuck() throws InvocationTargetException { | |
Duck duck = let(hen).be(Duck.class); | |
assertEquals("Hen walks", duck.walk()); | |
assertEquals("Hen talks", duck.talk()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment