我有n个类,它们堆叠或不堆叠在一起。所有这些类都扩展了相同的类(CellObject)。我知道会在这个列表中添加更多的类,我想创建某种方式,在一个地方很容易操作“可堆叠性”。
我在考虑创建一个矩阵,其中row-index是堆栈底部的类,列索引是堆栈顶部的类。如果您可以在顶部堆叠顶部,则值为true(或1),否则为false(0)。
但是,我的同事建议创建名为canStack的n + 1方法。一个通用的canStack方法会打开一个instanceof语句,将其引导到n个子方法之一。每个子方法都只回答顶部对象是否可以自己堆叠在底部对象上的问题。
我认为我的解决方案更优雅/干净。这是真的?如果是这样,我将如何实现它?
我将对象更改为类
答案 0 :(得分:5)
您的解决方案会更短。但它有一个缺点,如果你添加一个CellObject的sublcass,你可能会忘记改变你的数组。即使你知道这应该发生,其他人可能有一天会在代码上工作。然后,他的解决方案也有同样的问题。
现在,这是一个有点疯狂的想法,但由于你实际上在说一些关于类,所以感觉就像元数据设施一样。你可以做的是定义一个注释,说明哪些类可以堆叠到带注释的类和/或它可以堆叠的类上。
这样的事情:
@interface Stackable {
Class<? extends CellObject>[] stackables(); //Classes that may stack on the annotated one
Class<? extends Cellobject>[] pillars(); //Classes this one can stack on
}
然后,您可以创建使用此元数据的注释处理器。它可以创建一个在编译时读入的配置文件,或者为您生成一些样板代码。您可以生成类JPA这样的元类,因为它的类型安全查询API可以说明类的内容。或者你甚至可以在运行时保留注释,以使用反射来找出可以堆叠什么的东西,特别构建你想要的数组而不必编写代码。
如果使用注释处理器,那么使用具有规范类名的String数组会更安全,因为Class对象在编译时可能还不可用。它的可行性还取决于所有CellObject类是否总是在同一个编译运行中。
使用反射(确保注释具有RetentionType RUNTIME时可能)似乎是一个可行的选项。检查阵列;如果相应的元素为null(可以使用Boolean而不是boolean),请执行反射并填充该元素。下次你可以避免反射开销,根据需要懒洋洋地填充数组。
编辑:忘了提及,我的解决方案并没有强制您保持元数据最新。此外,如果可堆叠性是可传递的,则可以降低复杂性。也就是说,A可以堆叠在B上,B可以堆叠在C上,意味着A可以堆叠在C上。
答案 1 :(得分:1)
矩阵方法将按 O(n 2 )进行缩放。相反,另一种方法可以扩展为 O(n),但维护风险更大。
作为替代方案,请考虑让abstract CellObject
实现合适的Stackable
接口,但将实现推迟到 n 具体子类。编译器将立即识别缺少的实现。另请参阅When an Abstract Class Implements an Interface。
interface Stackable {
boolean canStack(Stackable other);
}
abstract class CellObject implements Stackable {}
class Cell01 extends CellObject {
@Override
public boolean canStack(Stackable other) {
return true; // TODO
}
}
class Cell02 extends CellObject {
@Override
public boolean canStack(Stackable other) {
return true; // TODO
}
}
...
答案 2 :(得分:1)
我认为你的矩阵概念不是实现目标的好方法。你最终会得到一个包含各种可能性的巨大矩阵。显然,从矩阵中提取您想要的信息将相当容易,但从长远来看,维护它可能会因为添加更多CellObject
子类而成为一种痛苦的经历。这同样适用于您的同事建议的n + 1种方法。
在这两种情况下,每次添加CellObject
的子类时,您都必须转到包含矩阵的类,为每个现有行创建新行和新列,并手动指定是否可以在类x上堆叠此新类,或者为每个现有类添加新方法canStackOnNewClassX ()
。在我看来,这两种解决方案都容易出错(您可能很容易忘记更新矩阵,或输入错误的信息,因为代码可能不易读取),有更优雅的方法来处理此类问题。
你可以做的一件事就是在CellObject
超级类中有一个地图,它将保留你的“可堆叠性”信息,并提供填充这个地图的方法,并检查是否可以堆叠A类成员在B班的成员上。这样的事情:
public abstract class CellObject
{
private static Map<Class<? extends CellObject>, Map<Class<? extends CellObject>, Boolean>> fullStackabilityMap =
new HashMap<Class<? extends CellObject>, Map<Class<? extends CellObject>, Boolean>> ();
protected static void addStackableOnObjectInformation (Class<? extends CellObject> baseObjectClass, Class<? extends CellObject> objectToStack, boolean canStackOnObject)
{
Map<Class<? extends CellObject>, Boolean> stackableMapForObject = fullStackabilityMap.get (baseObjectClass);
if (stackableMapForObject == null)
{
stackableMapForObject = new HashMap<Class<? extends CellObject>, Boolean> ();
fullStackabilityMap.put (baseObjectClass, stackableMapForObject);
}
stackableMapForObject.put (objectToStack, canStackOnObject);
}
protected boolean isStackableOnObject (CellObject baseObject)
{
Map<Class<? extends CellObject>, Boolean> stackableMapForObject = CellObject.fullStackabilityMap.get (baseObject.getClass ());
if (stackableMapForObject == null)
{
return false;
}
Boolean canStackOnObject = stackableMapForObject.get (this.getClass ());
return canStackOnObject != null ? canStackOnObject : false; //Assume that the object cannot be stacked if it was not specified
}
}
public class CellObjectA extends CellObject
{
}
public class CellObjectB extends CellObject
{
static
{
addStackableOnObjectInformation (CellObjectB.class, CellObjectA.class, true);
}
}
public class CellObjectC extends CellObject
{
static
{
addStackableOnObjectInformation (CellObjectC.class, CellObjectA.class, true);
addStackableOnObjectInformation (CellObjectC.class, CellObjectB.class, true);
}
}
由于Java在Java 6中缺少菱形运算符,fullStackabilityMap
中CellObject
的创建似乎很复杂,但如果您编写了一个创建地图的实用程序方法或使用Guava,则可以简化它。
因此,在这个例子中,CellObjectC
个实例不能在一些对象上堆叠; CellObjectB
个实例只能堆叠在CellObjectC
个对象上,而CellObjectA
可以堆叠在CellObjectB
或CellObjectC
个对象上。
每次添加新类时,您必须做的唯一工作是更新现有类的静态初始值设定项,以确保考虑这个新类。该解决方案的优点是:
CellObject
子类,这将是一场噩梦。 / LI>