我正在为树结构重写一个游标API。我有类似的方法:
/**
* Move cursor to parent node of currently selected node.
*
* @return the cursor instance
*/
INodeCursor moveToParent();
/**
* Move cursor to first child node of currently selected node.
*
* @return the cursor instance
*/
INodeCursor moveToFirstChild();
和相应的布尔值hasParent()和hasFirstChild()...方法。到目前为止,moveToX() - 方法还返回了一个布尔值,但我想像:
trx.moveToParent().moveToParent().insertFirstChild(...).moveToRightSibling()
...如果你知道你做了什么就会更有趣。但是我不知道如果它失败该怎么办(返回null以产生一个立即的NPE?)。也许最好返回游标实例,调用者必须知道游标可能根本没有被移动过。
感谢您的任何建议。
编辑:也许这也是Google Guava Optional的一个用例?这样我从两个方面都得到了最好的效果?
trx.moveToRightSibling().get().moveToRightSibling().get().insertTextAsFirstChild("foo")
和
if (trx.moveToRightSibling.isPresent()) && trx.moveToRightSibling.isPresent() && "foo".equals(rtx.getValue) { ... }
加上额外的trx.hasRightSibling()...方法。所以也许是一个自编的简单包装器,具有相同的语义但名称不同。
if (trx.moveToRightSibling().didSucceed() && ...)
和
trx.moveToRightSibling().get().moveToRightSibling().get() ...)
编辑2:例如:
/**
* Determines if the {@link INodeCursor} moved to a node or not. Based on the
* idea of providing a wrapper just like in Google Guava's {@link Optional}
* class.
*
* @author Johannes Lichtenberger
*
* @param <T>
* type parameter, the cursor
*/
public abstract class Move<T extends INodeCursor> {
/**
* Returns a {@link Moved} instance with no contained reference.
*/
@SuppressWarnings("unchecked")
public static <T extends INodeCursor> Move<T> notMoved() {
return (Move<T>) NotMoved.INSTANCE;
}
/**
* Returns a {@code Moved} instance containing the given non-null reference.
*/
public static <T extends INodeCursor> Moved<T> moved(final @Nonnull T pMoved) {
return new Moved<T>(checkNotNull(pMoved));
}
/**
* Determines if the cursor has moved.
*
* @return {@code true} if it has moved, {@code false} otherwise
*/
public abstract boolean hasMoved();
/**
* Get the cursor reference.
*
* @return cursor reference
*/
public abstract T get();
}
编辑3:和我的moveTo(long)-method所有其他moveToX() - 方法基于:
@Override
public Move<? extends INodeCursor> moveTo(final long pNodeKey) {
assertNotClosed();
if (pNodeKey == EFixed.NULL_NODE_KEY.getStandardProperty()) {
return Move.notMoved();
}
// Remember old node and fetch new one.
final INode oldNode = mCurrentNode;
Optional<? extends INodeBase> newNode;
try {
// Immediately return node from item list if node key negative.
if (pNodeKey < 0) {
if (mItemList.size() > 0) {
newNode = mItemList.getItem(pNodeKey);
} else {
newNode = Optional.absent();
}
} else {
final Optional<? extends INodeBase> node = mPageReadTrx.getNode(
pNodeKey, EPage.NODEPAGE);
newNode = node;
}
} catch (final SirixIOException e) {
newNode = Optional.absent();
}
if (newNode.isPresent()) {
mCurrentNode = (INode) newNode.get();
return Move.moved(this);
} else {
mCurrentNode = oldNode;
return Move.notMoved();
}
}
答案 0 :(得分:1)
基于上述评论:
问题归结为: 1)我应该使用布尔返回或实例
无论哪种方式都需要进行错误检查:
if (trx.moveToParent()) {
if (trx.moveToParent()) {
trx.doSomething();
VS
try {
trx.moveToParent().moveToParent().doSomething();
}
catch(NPE ex) {
第一种方法稍微明显一点就是修改了trx,但使用起来有点不方便(例如,错误报告需要else
每个条件[设置一个标志然后再做一个错误报告]基于旗帜])
不更改节点的想法与返回布尔值相同,但更简单的是
trx.noveToParent();
if (trx.didMove()) {
trx.moveToParent();
if (trx.didMove()) {
trx.doSomething();
如果是我,我会返回节点并抛出NPE - 您需要确保您的变量正确更新。即trx.moveToParent()。moveToParent()实际上修改trx两次而不是一次更改为trx,一次更改为某个匿名副本。良好的单元测试将有助于此。
答案 1 :(得分:1)
就个人而言,在这种情况下,我会创建你自己的例外:
public class NoSuchNodeException extends RuntimeException {
...
}
将从您的游标方法抛出此异常。它允许客户端响应有意义的条件(他们已经请求移动到不存在的节点),而不是通用的NPE,这可能意味着什么。是否检查异常取决于您的要求。在这种情况下,我发现未经检查的例外更加友好。
public INodeCursor moveToParent() {
if (currentNode.parent == null) {
throw new NoSuchNodeException("Node has no parent", currentNode);
}
}
答案 2 :(得分:1)
您可以聪明并创建一个NullNode实例。当您移动ToToChild且没有子节点时,返回NullNode并将其父节点设置为您的实际节点。然后,你可以检查isNullNode,如果你得到一个空的,你可以用“printPath”方法把它带回到你从树上掉下来的地方。
如果假设您的分支通常是正确的,那么它会让您的方法更加清晰,因为理想情况下它们很少会失败。
答案 3 :(得分:0)
我建议立即抛出一个自己的异常(可能是NPE的子类),所以它是快速失败的。有时你可能希望通过方法lenient()
返回一个具有所有移动方法的不同类,但没有变异方法。宽松阶级的移动方法永远不会抛出;如果出现错误,则返回失败的实例。通过使用strict()
,您可以返回到原始类,或者在路径某处出现错误时获取异常。
通常,你会做类似
的事情trx.up().right().right().firstChild().addChild(....);
并在问题发生时立即获得异常(是的,我会简化方法名称,“moveToRight”如何比“正确”更好?)。使用“宽大”可能就像
ILenientNodeCursor c1 = trx.lenient().up().right().right().firstChild();
if (c1.failed()) c1 = trx.right().right().firstChild();
if (!c1.failed()) c1.strict().addChild(...);
我不太确定“宽大”,但我很确定正常的行为应该是严格的,尽可能快地投掷。这肯定比其他任何事情都容易出错,而且大部分时间都比较容易使用。