3

メソッドに複数の出口点がある場合に、メソッドの戻り値をトレースする最良の方法を見つけようとしています。トレースを追加したいメソッドがたくさんあります。私が試したことを実行します。

最初に、次のような単一の出口点を持つように各メソッドをリファクタリングしてみました。

StatusCode CreateResource(string name, string type)
{
    Trace.LogEvent("BEGIN CreateResource name=" + name + " type=" + type);
    StatusCode status = StatusCode.Ok;
    if (!IsValidResourceName(name))
        status = StatusCode.InvalidName;
    else
    {
        if (!IsValidResourceType(type))
            status = StatusCode.InvalidType;
        else
        {
            if (!SystemOnline())
                status = StatusCode.SystemOffline;
            //continues to nest with more conditions
        }
    }
    Trace.LogEvent("END CreateResource result=" + status);
    return status;
}

しかし、ネストされた if ステートメントは見苦しく、読みにくくなっています。私は初期の終了ポイントをガード ステートメントとして多用しており、それをすべてリファクタリングすると混乱が生じ、デ リファクタリングのように感じます。

私が試したもう1つのことは、戻り値をトレースする別のメソッドで各メソッドをラップすることでした:

StatusCode CreateResource(string name, string type)
{
    Trace.LogEvent("BEGIN CreateResource name=" + name + " type=" + type);
    StatusCode status = CreateResource_DONT_CALL_THIS_METHOD(name, type);
    Trace.LogEvent("END CreateResource result=" + status);
    return status;
}

StatusCode CreateResource_DONT_CALL_THIS_METHOD(string name, string type)
{
    if (!IsValidResourceName(name))
        return StatusCode.InvalidName;
    if (!IsValidResourceType(type))
        return StatusCode.InvalidType;
    if (!SystemOnline())
        return StatusCode.SystemOffline;
    return StatusCode.Ok;
}

問題は、将来、他の開発者 (または私) がラップされたメソッドを呼び出してトレースをバイパスするのを防ぐ方法です。したがって、ラップされたメソッドのばかげた (そして矛盾する) 名前です。内部メソッドの匿名メソッドを定義して呼び出すこともできますが、これはプロジェクト全体で使用するには非常に厄介なパターンです。

私が見つけた最も堅実で読みやすい方法はこれでした。これは一種のOK IMOですが、このforステートメントの使用がコードレビューで飛ぶことになるとは思えません。

StatusCode CreateResource_Internal(string name, string type)
{
    Trace.LogEvent("BEGIN CreateResource name=" + name + " type=" + type);
    StatusCode status = StatusCode.Ok;
    for (int i = 0; i < 1; i++)
    {
        if (!IsValidResourceName(name))
        {
            status = StatusCode.InvalidName;
            break;
        }
        if (!IsValidResourceType(type))
        {
            status = StatusCode.InvalidType;
            break;
        }
        if (!SystemOnline())
        {
            status = StatusCode.SystemOffline;
            break;
        }
    }
    Trace.LogEvent("END CreateResource result=" + status);
    return status;
}

try finally ブロックを使用してバリエーションを試しました。

StatusCode CreateResource_Internal(string name, string type)
{
    Trace.LogEvent("BEGIN CreateResource name=" + name + " type=" + type);
    StatusCode status = StatusCode.Ok;
    try
    {
        if (!IsValidResourceName(name))
        {
            status = StatusCode.InvalidName;
            return status;
        }
        if (!IsValidResourceType(type))
        {
            status = StatusCode.InvalidType;
            return status;
        }
        if (!SystemOnline())
        {
            status = StatusCode.SystemOffline;
            return status;
        }
    }
    finally
    {
        Trace.LogEvent("END CreateResource result=" + status);
    }
    return StatusCode.Ok;
}

ここでの欠点は、戻る前に 'status' 変数を設定するのを忘れる可能性があることです。その場合、トレースは実行されません。

私の質問は、これを行うためのベストプラクティスはありますか? 単一の出口点にリファクタリングする必要がありますか? ラップされたメソッドが他の場所から呼び出されるのを防ぐ方法はありますか? トレースをバイパスできるようにすることの危険性は、単一の出口点へのリファクタリングの乱雑さよりも悪いですか?

ps アスペクト指向プログラミングのような重いものは持ち込みたくありません。

4

4 に答える 4

4

トレース ファイル (またはそのことに関する任意の形式のログ記録) の書き込みは、横断的懸念の典型的な例であり、 A) 読みにくくなり、B) 重複が多くなるため、メソッドでこのタイプのコードを避けるように努める必要があります。コードの。

質問で、アプリケーションに AOP スタイルのプログラミングを追加したくないとおっしゃっていましたが、これには Microsoft Unity を実装することを強くお勧めします。まさにここで解決しようとしているシナリオである傍受をサポートしています。インターフェイスへのコーディングの優れたプログラミング プラクティスに従っている限り、実装がいかに簡単であるかに驚かれることでしょう (そして、かなりクールなこともできます!)。

ちょっと考えさせられます...

于 2013-08-15T12:28:54.603 に答える
2

私は常にtry-finallyスキームを使用してきましたが、メソッド全体がtry-block でラップされています。

このようなもの:

StatusCode CreateResource_Internal(string name, string type)
{
    try
    {
        Trace.LogEvent("BEGIN CreateResource name=" + name + " type=" + type);
        StatusCode status = StatusCode.Ok;

        if (!IsValidResourceName(name))
        {
            status = StatusCode.InvalidName;
            return status;
        }
        if (!IsValidResourceType(type))
        {
            status = StatusCode.InvalidType;
            return status;
        }
        if (!SystemOnline())
        {
            status = StatusCode.SystemOffline;
            return status;
        }
        status = StatusCode.Ok; // A bit silly, but that avoids the problem of status not being set.
        return status;
    }
    finally
    {
        Trace.LogEvent("END CreateResource result=" + status);
    }
}
于 2013-08-15T10:47:39.200 に答える