摘要:
我想与两个类(' Item'和' Block')进行交互,它们共享许多类似的功能,就好像它们是从具有这些功能的接口实现的一样,但它们是不,我不能编辑它们。我有什么办法解决这个问题?我是不是写了超级hacky代码?
详细信息:
我正在使用Java编写一个Minecraft mod,使用minecraft的一部分是我无法编辑游戏的基本代码。游戏有两个基本类别; "座"和"项目",这两者共享许多功能,但开发人员没有让它们实现一个接口(我希望他们有)。
为了保持我的代码干净并避免在处理块和项目之间切换很多if语句,我希望将它们保存在通用列表中,最好尽可能少地转换为Block或Item,同时尝试保持可读性。
我目前的解决方案无法满足作为一个好的解决方案,它很难阅读,虽然它清理了一些代码重复,但仍然需要转换返回类型。
更新
根据Eran留下的答案,我已经更新了我对此问题的解决方案,现在我觉得这很令人满意:
我创建了与ItemOrBlockAdapter和扩展Block和Item接口的适配器(在评论中建议使用适配器名称):
public interface ItemOrBlockAdapter {
public String myGetUnlocalizedName();
public ItemOrBlockAdapter mySetCreativeTab(CreativeTabs tab);
}
public class BlockAdapter extends Block implements ItemOrBlockAdapter {
protected BlockAdapter(String uid, Material m) {
super(m);
GameRegistry.registerBlock(this, uid);
}
public String myGetUnlocalizedName()
{
return this.getUnlocalizedName();
}
public ItemOrBlockAdapter mySetCreativeTab(CreativeTabs tab)
{
return (ItemOrBlockAdapter)this.setCreativeTab(tab);
}
}
这比我之前使用的hacky解决方案要好得多(通过Block和Item方法查找每次调用所需的方法)!然而,它并非没有错,我现在必须编写我希望添加适配器三次的每个函数,如果其他东西继承了头适配器,则第四次。
我认为这已经解决了,但我对优秀的解决方案持开放态度,因为这涉及到相当多的代码重复(理解我必须添加很多方法来完成这些,而不仅仅是如上所示的两个)
答案 0 :(得分:6)
您可以创建包装类 - BlockWrapper
和ItemWrapper
。两者都将实现相同的接口(包含Block
和Item
的常用方法)。 BlockWrapper
将包含Block
个实例,ItemWrapper
将包含Item
个实例。
示例:
public interface ItemOrBlock // think of a better name
{
public void func1();
public void func2();
}
public class BlockWrapper implements ItemOrBlock
{
private Block block;
public BlockWrapper (Block block) {
this.block = block;
}
public void func1()
{
block.func1();
}
public void func2()
{
block.func2();
}
}
ItemWrapper
会有类似的实现。
现在,如果您从BlockWrapper
和ItemWrapper
创建Block
和Item
,则可以将它们放入ItemOrBlock
的集合中,并使用该接口调用其常用方法。
答案 1 :(得分:1)
虽然这个问题已经有了接受的答案,但我想提一下创建Dynamic Proxy Classes的选项。
有人可能会说,这只是一种“隐藏”丑陋反射代码的方式,它是一种简洁优雅的解决方案,与手动创建适配器/包装类相比,它对于类似的用例具有一个显着的优势:你可以为许多类创建一个接口的委托,而不必为每个“未实现”接口的类硬编码重复的样板代码。
以下是一个简单的例子,基于上述链接的委托人:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyExample
{
public static void main(String[] args)
{
Block block = new Block();
Item item = new Item();
Entity blockEntity = asEntity(block);
Entity itemEntity = asEntity(item);
blockEntity.setName("Block");
System.out.println(blockEntity.getName()+", "+blockEntity.computeSize());
itemEntity.setName("Item");
System.out.println(itemEntity.getName()+", "+itemEntity.computeSize());
}
private static Entity asEntity(Object object)
{
Class<?>[] ifs = new Class<?>[] { Entity.class };
Entity entity = (Entity) Proxy.newProxyInstance(
Entity.class.getClassLoader(), ifs,
new Delegator(object));
return entity;
}
}
class Delegator implements InvocationHandler
{
private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
static
{
try
{
hashCodeMethod = Object.class.getMethod("hashCode",
(Class<?>[]) null);
equalsMethod = Object.class.getMethod("equals",
new Class[] { Object.class });
toStringMethod = Object.class.getMethod("toString",
(Class<?>[]) null);
}
catch (NoSuchMethodException e)
{
throw new NoSuchMethodError(e.getMessage());
}
}
private final Object delegate;
Delegator(Object delegate)
{
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Class<?> declaringClass = m.getDeclaringClass();
if (declaringClass == Object.class)
{
if (m.equals(hashCodeMethod))
{
return proxyHashCode(proxy);
}
else if (m.equals(equalsMethod))
{
return proxyEquals(proxy, args[0]);
}
else if (m.equals(toStringMethod))
{
return proxyToString(proxy);
}
else
{
throw new InternalError(
"unexpected Object method dispatched: " + m);
}
}
else
{
try
{
Class<? extends Object> delegateClass = delegate.getClass();
Method delegateMethod = delegateClass.getDeclaredMethod(
m.getName(), m.getParameterTypes());
return delegateMethod.invoke(delegate, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
}
}
protected Integer proxyHashCode(Object proxy)
{
return new Integer(System.identityHashCode(proxy));
}
protected Boolean proxyEquals(Object proxy, Object other)
{
return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
}
protected String proxyToString(Object proxy)
{
return proxy.getClass().getName() + '@' +
Integer.toHexString(proxy.hashCode());
}
}
class Item
{
private String name;
void setName(String name)
{
this.name = name;
}
String getName()
{
return name;
}
int computeSize()
{
return 12;
}
}
class Block
{
private String name;
void setName(String name)
{
this.name = name;
}
String getName()
{
return name;
}
int computeSize()
{
return 23;
}
}
interface Entity
{
void setName(String name);
String getName();
int computeSize();
}
(当然,可以插入一些更好的错误处理,但它显示了基本方法)
编辑进一步详细说明,部分回应评论:
正如我所提到的,这可以被认为只是“隐藏”讨厌的反射部分。还有一个仍然需要处理通常的反射问题,比如更难处理的错误处理和缺少编译时错误检查。
但是对于更复杂的设置,它可能是有利的:想象一下,有两个以上的类,以及多个“未实现”的接口。然后以包装器/适配器类的形式手动编码胶水代码可能很麻烦。
但是,我喜欢动态代理类的简单性。它们是在Java中实现像Duck Typing这样的东西的唯一可行方法 - 它们只是神奇地工作。