2

現在、ツリー構造用のカーソル 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();

対応する boolean 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: および他のすべての moveToX() メソッドが基づいている私の moveTo(long) メソッド:

@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();
    }
}
4

4 に答える 4

1

上記のコメントに基づいて:

質問は次のように要約されます: 1) ブール値の戻り値またはインスタンスを使用する必要がありますか?

どちらの方法でもエラー チェックが必要です。

if (trx.moveToParent()) {
    if (trx.moveToParent()) {
        trx.doSomething();

try {
    trx.moveToParent().moveToParent().doSomething();
}
catch(NPE ex) {

最初の方法は、trx が変更されていることは少し明白ですが、使用するのは少し不便です (たとえば、エラー報告にはelsefor each 条件 [フラグを設定し、フラグに基づいて単一のエラー報告を行う] が必要です)。

ノードを変更しないという考えは、ブール値を返すのと同じですが、さらに醜いです

trx.noveToParent();
if (trx.didMove()) {
    trx.moveToParent();
    if (trx.didMove()) {
        trx.doSomething();

私だったら、ノードを返して NPE をスローします。変数が正しく更新されていることを確認する必要があります。つまり、trx.moveToParent().moveToParent() は実際には、trx を 1 回変更し、匿名のコピーに 1 回変更するのではなく、trx を 2 回変更します。ここでは、優れた単体テストが役立ちます。

于 2012-09-28T01:59:47.467 に答える
1

個人的には、このような状況では、独自の例外を作成します:

public class NoSuchNodeException extends RuntimeException {
  ...
}

この例外は、カーソル メソッドからスローされます。これにより、クライアントは、意味のある一般的な NPE ではなく、意味のある条件 (存在しないノードへの移動を要求した) に応答できるようになります。例外がチェックされるかどうかは、要件によって異なります。ただし、この場合、未チェックの例外の方がはるかに使いやすいと思います。

public INodeCursor moveToParent() {
   if (currentNode.parent == null) {
       throw new NoSuchNodeException("Node has no parent", currentNode);
   }
}
于 2012-09-28T03:02:19.617 に答える
1

巧妙に NullNode インスタンスを作成できます。moveToChild で子がない場合は、NullNode を返し、その親を実際のノードに設定します。次に、空のものを取得した場合は isNullNode を確認し、「printPath」メソッドなどを使用して、ツリーから落ちた場所に戻すことができます。

ブランチが通常は正しいと仮定すると、理想的にはめったに失敗しないため、メソッドチェーンがよりクリーンになります。

于 2012-09-28T05:21:07.903 に答える
0

すぐに独自の例外(おそらくNPEのサブクラス)をスローすることをお勧めします。そうすれば、フェイルファストになります。lenient()場合によっては、すべての移動メソッドを持ち、変更メソッドを持たない別のクラスを返すメソッドを使用して、作業を簡単にしたい場合があります。寛大なクラスの移動メソッドは決してスローしません。エラーが発生した場合は、失敗したインスタンスを返します。を使用strict()すると、元のクラスに戻るか、パスのどこかにエラーが発生した場合に例外が発生します。

通常、あなたは次のようなことをします

trx.up().right().right().firstChild().addChild(....);

問題が発生するとすぐに例外が発生します(はい、メソッド名を単純化します。「moveToRight」は「right」よりも優れていますか?)。「寛大な」の使用は次のようになります

ILenientNodeCursor c1 = trx.lenient().up().right().right().firstChild();
if (c1.failed()) c1 = trx.right().right().firstChild();
if (!c1.failed()) c1.strict().addChild(...);

「寛大」についてはよくわかりませんが、通常の動作は厳密で、できるだけ速く投げる必要があると確信しています。これは確かに他の何よりもエラーが発生しにくく、ほとんどの場合使いやすいです。

于 2012-09-28T13:04:32.543 に答える