Pola desain untuk membungkus logging di sekitar eksekusi


11

pengantar

Saya menerapkan kelas Java abstrak dari kerangka kerja pemrosesan *. Dengan menerapkan fungsi ini execute, saya dapat menambahkan fungsionalitas logis bisnis. Saya ingin menambahkan pencatatan pada awal dan akhir setiap implementasi dari setiap fungsi eksekusi saya. Juga di antara beberapa penebangan, jika hal-hal tertentu dilakukan. Namun, saya ingin membuatnya secara seragam. Pikir saya adalah untuk mewarisi kelas kerangka kerja ke kelas baru sendiri yang mengimplementasikan logging dan menyediakan beberapa executeWithLoggingfungsi baru , yang membungkus logging di sekitar bagian-bagian tertentu. Saya tidak yakin apakah itu ide terbaik dan apakah saya dapat menggunakan pola desain yang akan membuat keseluruhan usaha menjadi elegan. Bagaimana saya akan melanjutkan semua ini?

Tantangan (harap pertimbangkan ini!)

  1. Satu tantangan dalam kasus saya adalah bahwa kelas asli hanya memiliki satu executefungsi, namun, saya perlu masuk ke beberapa bagian.
  2. Hal kedua adalah: Menggunakan pola dekorator (adalah ide pertamaku juga) tidak berfungsi, jika saya menggunakan juga a execute, karena super.execute()-fungsi harus dipanggil di baris pertama baru saya execute(), bukan ?
  3. Setidaknya akan ada 4 kelas yang terlibat: BaseFunctiondari kerangka kerja, LoggingFunction extends BaseFunctiondari saya, MyBusinessFunction extends LoggingFunctiondari saya, MyBusinessClassyang instanciates MyBusinessFunction.
  4. Saya tidak hanya perlu login di awal dan akhir execute, tetapi juga di tengah.
  5. "Pencatatan" bukan hanya pencatatan Java sederhana, tetapi sebenarnya pencatatan ke basis data. Ini tidak mengubah apa pun tentang prinsip-prinsip tersebut, tetapi menunjukkan bahwa logging mungkin lebih dari satu baris kode.

Mungkin contoh bagaimana saya melakukan semuanya akan menyenangkan, untuk membuat saya maju.

| * Fungsi Badai Trident, mirip dengan baut Storm, tetapi ini tidak terlalu penting.


1
Super.execute disebut oleh dekorator, bukan dekorator. Kelas utama Anda seharusnya tidak menyadari bahwa ia sedang didekorasi dan Anda hanya perlu berhati-hati dengan panggilan ini, karena mereka tidak dapat menyertakan dekorator (yang bertentangan dengan pendekatan dengan mixin atau ciri-ciri).
Frank

Apakah executedalam BaseFunctionabstrak?
Melihat

@Spotted: Ya, executeabstrak dalam BaseFunction.
Make42

Jawaban:


13

Secara umum ada beberapa cara yang mungkin untuk melakukan logging di sekitar eksekusi tertentu. Pertama-tama, sebaiknya Anda memisahkan ini, karena penebangan adalah contoh khas untuk Pemisahan Masalah . Ini benar-benar tidak masuk akal untuk menambahkan login langsung ke logika bisnis Anda.

Adapun kemungkinan pola desain, berikut adalah daftar (tidak konklusif) dari atas kepala saya:

  • Pola dekorator : Pola desain yang terkenal, di mana Anda pada dasarnya membungkus kelas non-logging ke kelas lain dari antarmuka yang sama dan pembungkus melakukan logging sebelum dan / atau setelah memanggil kelas yang dibungkus.

  • Komposisi fungsi : Dalam bahasa pemrograman fungsional, Anda dapat dengan mudah menyusun fungsi Anda dengan fungsi logging. Ini mirip dengan pola dekorator dalam bahasa OO, tetapi sebenarnya bukan cara yang disukai, karena fungsi logging yang dibuat akan didasarkan pada implementasi efek samping.

  • Monads : Monads juga dari dunia pemrograman fungsional dan memungkinkan Anda untuk memisahkan eksekusi dan pencatatan Anda. Ini pada dasarnya berarti Anda mengagregasi pesan logging dan metode eksekusi yang diperlukan, tetapi belum juga menjalankannya. Anda kemudian dapat memproses monad untuk melakukan penulisan log dan / atau eksekusi aktual logika bisnis.

  • Pemrograman berorientasi aspek : Pendekatan yang lebih canggih, yang mencapai pemisahan penuh, karena kelas yang melakukan perilaku non-logging tidak pernah berinteraksi secara langsung dengan logging.

  • Relai: Pola desain standar lain mungkin cocok juga, misalnya, Facade bisa berfungsi sebagai titik masuk yang dicatat, sebelum meneruskan panggilan ke kelas lain yang melakukan bagian logika bisnis. Ini dapat diterapkan pada berbagai level abstraksi juga. Misalnya, Anda dapat mencatat permintaan yang dibuat ke URL layanan yang dapat dijangkau secara eksternal, sebelum meneruskan ke URL internal untuk pemrosesan permintaan non-log aktual.

Adapun persyaratan tambahan Anda bahwa Anda perlu melakukan logging "di tengah" - itu mungkin menunjuk ke desain yang aneh. Mengapa, bahwa eksekusi Anda melakukan banyak hal, sehingga sesuatu seperti "tengah" dari eksekusi bahkan jauh dari kepentingan? Jika memang ada banyak hal yang terjadi, lalu mengapa tidak mengelompokkannya menjadi beberapa tahap / fase / apa-apa? Anda bisa secara teori mencapai logging di tengah dengan AOP saya kira, tapi saya masih berpendapat bahwa perubahan desain tampaknya jauh lebih tepat.


3

Saya merasa bahwa Anda absolutly ingin menggunakan suatu pola. Ingatlah bahwa penggunaan pola desain yang buruk dapat menyebabkan kode menjadi kurang dapat dikelola / dibaca.

Itu kata ...

Kasing Anda rumit karena sepertinya Anda ingin melakukan 2 jenis pencatatan:

  1. Mencatat beberapa langkah fungsionalitas logis Anda (di dalam execute)
  2. Masuk sekitar panggilan fungsi (masuk / keluar fungsi) (di luar execute)

Untuk kasus pertama, pada saat Anda ingin mencatat beberapa hal di dalam suatu fungsi, Anda harus melakukannya ... di dalam fungsi tersebut. Anda dapat menggunakan pola metode templat untuk membuat hal-hal sedikit lebih cantik tapi saya pikir itu berlebihan dalam hal ini.

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");
        ...
    }
}

Untuk kasus kedua, pola dekorator adalah caranya:

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");
    }
}

Dan Anda bisa menggunakannya seperti ini di BusinessClass:

public final BusinessClass {
    private final BaseFunction f1;

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

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

Panggilan ke doFunctionakan mencatat ini:

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

Apakah akan berhasil menghilangkan atribut asal dan menulis super.execute();alih-alih origin.execute();?
Make42

Solusi Anda tidak lengkap - mungkin karena penjelasan saya yang buruk. Saya akan menambahkan informasi dalam pertanyaan.
Make42

@ user49283 Mengapa Anda ingin memanggil super.execute()metode ini abstract?
Melihat

@ user49283 Saya juga telah memperbarui jawaban saya
Terlihat

Saya belum begitu mengerti pola templat, tapi saya akan mencoba lagi. Sementara itu: Bagaimana dengan menggunakan Pola Perintah? Saya memberikan jawaban menggunakan Pola Perintah - silakan berkomentar!
Make42

1

Pendekatan Anda tampaknya valid dan sebenarnya merupakan implementasi dari Pola Penghias . Ada beberapa cara lain yang bisa Anda lakukan, saya rasa, tetapi Dekorator adalah pola yang sangat elegan dan mudah dipahami yang sangat cocok untuk menyelesaikan masalah Anda.


0

Bagaimana dengan menggunakan Pola Perintah?

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

Saya tidak punya banyak pengalaman dengan pola perintah. Satu-satunya poin negatif yang jelas saya lihat adalah sulit dibaca (perhatikan tingkat indentasi), jadi saya tidak akan menggunakannya karena saya suka merawat orang-orang yang akan membaca kode saya di masa depan.
Melihat

Juga execute()tidak melakukan apa-apa saat ini karena Anda baru saja memulai MyLoggingCommandWhole.
Melihat

@ Spotted: Saya pikir instaciating itu MyLoggingCommandWhole, dijalankan executeWithoutLogging();dari MyLoggingCommandWhole. Apakah bukan ini masalahnya?
Make42

Tidak, bukan itu masalahnya. Anda harus menulis sesuatu seperti:MyLoggingCommandWhole cmd = new MyLoggingCommandWhole() { ... }; cmd.executeWithoutLogging();
Melihat

@Spotted: Itu hampir tidak masuk akal. Jika seperti yang Anda katakan, saya lebih suka harus meletakkan kode dari konstruktor ke dalam fungsi seperti executeWithLogging()dan cmd.executeWithLogging(). Lagipula, itulah yang harus dieksekusi.
Make42
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.