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


11

前書き

処理フレームワークの抽象Javaクラスを実装しています*。関数を実装することでexecute、ビジネスの論理機能を追加することができます。すべての実行関数のすべての実装の最初と最後にロギングを追加したいと思います。また、特定の処理が行われた場合、ロギングの合間にも実行されます。でもこれを一律に作りたい。私の考えは、ロギングを実装executeWithLoggingし、特定の部分にロギングをラップするいくつかの新しい機能を提供する独自の新しいクラスにフレームワーククラスを継承することでした。それが最高のアイデアかどうか、全体がエレガントになるようなデザインパターンを使用できるかどうかはわかりません。全体をどうやって進めますか?

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

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

たぶん、私がすべてを行う方法の例は、私を動かすのにいいでしょう。

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


1
super.executeは、デコレート対象ではなく、デコレータによって呼び出されます。メインクラスはそれが装飾されていることにさえ気付くべきではなく、デコレータを含めることができないため(ミックスインやトレイトによるアプローチとは対照的に)、この呼び出しに注意するだけで十分です。
フランク

あるexecuteBaseFunction抽象?
2015年

@スポット:はい、executeで抽象的ですBaseFunction
Make42

回答:


13

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

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

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

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

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

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

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

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


3

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

その蜂は言った...

2種類のロギングを実行する必要があるように見えるため、ケースはトリッキーです。

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

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

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番目のケースの場合、デコレータパターン適しています。

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

super.execute();代わりにorigin属性を省略して書き込むことはできorigin.execute();ますか?
Make42

あなたのソリューションは完全ではありません-多分私の悪い説明が原因です。質問に情報を追加します。
Make42

@ user49283 super.execute()このメソッドがあるのになぜ呼び出したいのabstractですか?
発見

@ user49283回答も更新しました
発見

テンプレートのパターンはまだよくわかりませんが、すぐにもう一度説明します。それまでは、コマンドパターンの使用についてはどうですか?コマンドパターンを使用して回答しました-コメントしてください!
Make42

1

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


0

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

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
                    }
                }
            }
        }
    }
}

コマンドパターンの経験はあまりありません。私が目にする唯一の明らかな否定的な点は、それがほとんど読めない(インデントのレベルに注意)ことです。そのため、将来私のコードを読む人を気遣うのが好きなので、使用しません。
2015年

またexecute()、インスタンス化しているだけなので、現時点では何もしませんMyLoggingCommandWhole
発見

@Spotted:私は、不満を抱くMyLoggingCommandWholeexecuteWithoutLogging();から実行さ れると思いましたMyLoggingCommandWhole。そうではありませんか?
Make42 10/10

いいえ、そうではありません。:あなたのような書き込みに何かしなければならないMyLoggingCommandWhole cmd = new MyLoggingCommandWhole() { ... }; cmd.executeWithoutLogging();
斑点

@スポット:それはほとんど意味がありません。それがあなたの言うとおりである場合は、コンストラクタのコードをexecuteWithLogging()andのような関数に配置する必要がありcmd.executeWithLogging()ます。結局、それは実行されるべきものです。
Make42、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.