web-dev-qa-db-ja.com

ロギングを実行にラップするための設計パターン

前書き

私は抽象Java処理フレームワークのクラス*)を実装しています。関数executeを実装することで、ビジネス論理機能を追加できます。すべての実行関数のすべての実装の開始と終了。特定の処理が行われる場合は、ロギングの合間にも、これを統一したいのですが、フレームワーククラスを独自の新しいクラスに継承すると思いました。ロギングを実装し、特定の部分にロギングをラップするいくつかの新しいexecuteWithLogging関数を提供するクラスです。それが最良のアイデアであるかどうか、および次のような設計パターンを使用できるかどうかはわかりません全体をエレガントにしようと思います。

課題(これらを考慮してください!)

  1. 私の場合の1つの課題は、元のクラスにはexecute関数が1つしかないことですが、複数の部分をログに記録する必要があります。
  2. もう1つは、executeも使用している場合、super.execute()- functionが新しいexecute()の最初の行で呼び出されましたね。
  3. 少なくとも4つのクラスが関係します。フレームワークからのBaseFunctionLoggingFunction extends BaseFunction 私から、 MyBusinessFunction extends LoggingFunction私から、MyBusinessClassを表すMyBusinessFunction
  4. executeの最初と最後だけでなく、途中でもロギングが必要です。
  5. 「ロギング」は単純なJavaロギングではありませんが、実際にはデータベースへのロギングです。これは原則については何も変更しませんが、ロギングは単なる1行のコードではないことを示しています。

多分私がすべてを行う方法の例は、私を動かすためのニースでしょう。

| *ストームトライデント関数。ストームボルトに似ていますが、これは特に重要ではありません。

9
Make42

一般に、特定の実行に関してロギングを実行するには、いくつかの可能な方法があります。まず、ロギングは 懸念の分離 の典型的な例であるため、これを分離することをお勧めします。ロギングをビジネスロジックに直接追加しても、あまり意味がありません。

考えられるデザインパターンについては、頭から上に(決定的ではない)リストを次に示します。

  • Decorator pattern :よく知られている設計パターン。基本的に、非ロギングクラスを同じインターフェイスの別のクラスにラップし、ラッパーはラップされたクラスを呼び出す前または呼び出した後にロギングを行います。

  • 関数構成 :関数型プログラミング言語では、ロギング関数を使用して関数を簡単に作成できます。これはOO言語)のデコレータパターンに似ていますが、合成されたロギング関数は副作用のある実装に基づいているため、実際には好ましい方法ではありません。

  • モナド :モナドも関数型プログラミングの世界からのものであり、実行とロギングを分離することができます。これは基本的に、必要なロギングメッセージと実行メソッドを集約するが、まだ実行していないことを意味します。次に、ビジネスロジックのログ書き込みや実際の実行を行うためにモナドを処理できます。

  • アスペクト指向プログラミング :非ログ動作を行うクラスがログと直接対話することはないため、完全な分離を実現するより洗練されたアプローチ。

  • リレー:他の標準設計パターンも適している場合があります。たとえば、ビジネスロジック部分を実行する別のクラスに呼び出しを転送する前に、Facadeがログエントリポイントとして機能する場合があります。これは、さまざまな抽象化レベルにも適用できます。たとえば、実際にログに記録されないリクエストの処理のために内部URLにリレーする前に、外部から到達可能なサービスURLへのリクエストをログに記録できます。

ロギングを「途中で」実行する必要があるという追加の要件については、これはおそらく奇妙な設計を示しています。なぜあなたの実行はそんなに多くやっているのか、その実行の「中間」のようなものは遠く離れたところにさえあるのですか?本当にたくさんのことが起こっているのなら、それをステージ/フェーズ/何を持っているのかにまとめてみませんか?理論的には、AOPを使用してログを途中で取得することもできますが、設計変更の方がはるかに適切であるように思われます。

11
Frank

絶対に柄を使いたい気がします。デザインパターンの使い方が悪いと、コードの保守性や読み取り性が低下する可能性があることに注意してください。

その蜂は言った...

2種類のロギングを実行したいように思われるため、ケースはトリッキーです。

  1. 論理機能のいくつかのステップのログ(execute内)
  2. 関数呼び出しの周りのロギング(関数の開始/終了)(execute外)

最初のケースでは、関数内で何かをログに記録したい場合は、関数内で実行する必要があります。 テンプレートメソッドパターン を使用して少しきれいにすることもできますが、この場合はoverkillだと思います。

public final class MyBusinessFunction extends BaseFunction {
    @Override
    public final void execute() {
        log.info("Initializing some variables");
        int i = 0;
        double d = 1.0;

        log.info("Starting algorithm");
        for(...) {
            ...
            log.info("One step forward");
            ...
        }
        log.info("Ending algorithm");

        ...
        log.info("Cleaning some mess");
        ...
    }
}

2番目のケースでは、 decorator pattern が適しています。

public final class LoggingFunction extends BaseFunction {
    private final BaseFunction Origin;

    public LoggingFunction(BaseFunction Origin) {
        this.Origin = Origin;
    }

    @Override
    public final void execute() {
        log.info("Entering execute");
        Origin.execute();
        log.info("Exiting execute");
    }
}

そして、BusinessClassで次のように使用できます。

public final BusinessClass {
    private final BaseFunction f1;

    public BusinessClass() {
        this.f1 = new LoggingFunction(new MyBusinessFunction());
    }

    public final void doFunction() {
        f1.execute();
    }
}

doFunctionへの呼び出しはこれを記録します:

Entering execute
Initializing some variables
Starting algorithm
One step forward
One step forward
...
Ending algorithm
Cleaning some mess
Exiting execute
3
Spotted

あなたのアプローチは有効であり、実際には Decorator Pattern の実装です。これを行う方法は他にもあると思いますが、Decoratorは非常にエレガントで理解しやすいパターンであり、問​​題の解決に非常に適しています。

1
JDT

コマンドパターンの使用についてはどうですか?

abstract class MyLoggingCommandPart {

  MyLoggingCommandPart() {
    log.info("Entering execute part");
    executeWithoutLogging();
    log.info("Exiting execute part");
  }

  abstract void executeWithoutLogging();

}

abstract class MyLoggingCommandWhole {

  MyLoggingCommandWhole() {
    log.info("Entering execute whole");
    executeWithoutLogging();
    log.info("Exiting execute whole");
  }

  abstract void executeWithoutLogging();

}

public final class MyBusinessFunction extends BaseFunction {

    @Override
    public final void execute() {

        new MyLoggingCommandWhole(){

            @Override
            executeWithoutLogging(){

                new MyLoggingCommandPart(){
                    @Override
                    executeWithoutLogging(){
                    // ... what I actually wanne do
                    }
                }

                new MyLoggingCommandPart(){
                    @Override
                    executeWithoutLogging(){
                    // ... some more stuff I actually wanne do
                    }
                }
            }
        }
    }
}
0
Make42