Design pattern per il wrapping della registrazione attorno all'esecuzione

8

Introduzione

Sto implementando una classe Java astratta di un framework di elaborazione *. Implementando la funzione execute , sono in grado di aggiungere funzionalità logiche di business. Vorrei aggiungere la registrazione all'inizio e alla fine di ogni implementazione di tutte le mie funzioni di esecuzione. Anche tra alcuni logging, se vengono fatte cose specifiche. Tuttavia, vorrei farlo in modo uniforme. Il mio pensiero era di ereditare la classe framework in una nuova classe che implementa la registrazione e fornisce alcune nuove funzioni executeWithLogging , che stanno avvolgendo la registrazione intorno alle parti specifiche. Non sono sicuro che sia la migliore idea e se sia in grado di utilizzare un modello di progettazione che renderebbe l'intero sforzo elegante. Come andrei su tutto il resto?

Sfide (ti preghiamo di prendere in considerazione queste!)

  1. Una sfida nel mio caso è che la classe originale ha solo una funzione execute , tuttavia, avrei bisogno di accedere a più parti.
  2. La seconda cosa è: usare un pattern decoratore (era anche la mia prima idea) non funziona, se sto usando anche un execute , perché la funzione super.execute() dovrebbe essere chiamata nel primo linea del mio nuovo execute() , non è vero?
  3. Ci sarebbero almeno 4 classi coinvolte: BaseFunction dal framework, LoggingFunction extends BaseFunction da me, MyBusinessFunction extends LoggingFunction da me, MyBusinessClass che instanciates MyBusinessFunction .
  4. Non solo ho bisogno di loggarmi all'inizio e alla fine di execute , ma anche nel mezzo.
  5. Il "logging" non è solo una semplice registrazione Java, ma in realtà si collega a un database. Questo non cambia nulla sui principi, ma dimostra che la registrazione potrebbe essere più di una sola riga di codice.

Forse un esempio di come farei il tutto sarebbe bello, per farmi andare.

| * Una funzione Trident tempesta, simile a un bullone Tempesta, ma questo non è particolarmente importante.

    
posta Make42 01.10.2015 - 14:14
fonte

4 risposte

10

Esistono in genere diversi modi per eseguire il logging di una determinata esecuzione. Prima di tutto, è bene che tu voglia separarlo, poiché la registrazione è un tipico esempio di Separazione delle preoccupazioni . Non ha molto senso aggiungere il logging direttamente nella tua logica aziendale.

Per quanto riguarda i possibili schemi di progettazione, ecco una lista (non conclusiva) dalla cima della mia testa:

  • Pattern di decorazione : un modello di progettazione ben noto, in cui fondamentalmente si avvolge la classe non di logging in un'altra classe della stessa interfaccia e il wrapper esegue la registrazione prima e / o dopo aver chiamato la classe wrapper.

  • Composizione delle funzioni : in un linguaggio di programmazione funzionale, puoi semplicemente comporre la tua funzione con una funzione di registrazione. Questo è simile al pattern di decoratore nei linguaggi OO, ma non è un metodo preferito in realtà, poiché la funzione di registrazione composta sarebbe basata su un'implementazione di effetti collaterali.

  • Monade : le Monade sono anche dal mondo della programmazione funzionale e ti permettono di disaccoppiare la tua esecuzione e registrazione. Ciò significa fondamentalmente che si aggregano i messaggi di registrazione e i metodi di esecuzione necessari, ma non ancora eseguiti. È quindi possibile elaborare la monade per eseguire la scrittura dei log e / o l'effettiva esecuzione della logica di business.

  • Programmazione orientata agli aspetti : approccio più sofisticato, che consente la separazione completa, come la classe che il comportamento non di logging non interagisce mai direttamente con la registrazione.

  • Inoltro: altri modelli di progettazione standard possono essere adatti, ad esempio, una facciata può servire come un punto di accesso registrato, prima di inoltrare la chiamata a un'altra classe che esegue la parte della business logic. Questo può essere applicato anche a diversi livelli di astrazione. Ad esempio, è possibile registrare le richieste inoltrate a un URL di servizio esternamente raggiungibile, prima di inoltrare a un URL interno per l'elaborazione effettiva delle richieste non registrate.

Per quanto riguarda il requisito aggiuntivo che è necessario eseguire la registrazione "nel mezzo", probabilmente sta indicando uno strano design. Perché è che il tuo execute sta facendo così tanto che qualcosa come un "mezzo" della sua esecuzione è anche lontanamente interessante? Se c'è davvero un sacco di cose in corso, allora perché non raggrupparlo in fasi / fasi / cosa-hai-te? In teoria, potresti teoricamente ottenere il logging nel mezzo con AOP, ma continuerei a sostenere che un cambiamento di design sembra essere molto più appropriato.

    
risposta data 01.10.2015 - 14:33
fonte
3

Ho la sensazione che tu voglia assolutamente usare un pattern. Ricorda che un cattivo utilizzo dei modelli di progettazione può portare a un codice meno gestibile / leggibile.

Ciò detto ...

Il tuo caso è complicato in quanto sembra che tu voglia fare 2 tipi di registrazione:

  1. Registrazione di alcuni passaggi della tua funzionalità logica (all'interno di execute )
  2. Effettuare il log delle chiamate di funzione (entrare / uscire da una funzione) (al di fuori di execute )

Per il primo caso, nel momento in cui vuoi registrare alcune cose all'interno di una funzione, devi farlo ... all'interno della funzione. Puoi utilizzare il modello di metodo del modello per rendere le cose un po 'più carine, ma penso che sia eccessivo in questo caso.

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

Per il secondo caso, il schema di decorazione è la strada da percorrere:

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

E potresti usarlo in questo modo in BusinessClass :

public final BusinessClass {
    private final BaseFunction f1;

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

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

Una chiamata a doFunction registrerà questo:

Entering execute
Initializing some variables
Starting algorithm
One step forward
One step forward
...
Ending algorithm
Cleaning some mess
Exiting execute
    
risposta data 01.10.2015 - 15:47
fonte
1

Il tuo approccio sembra valido ed è in realtà un'implementazione del pattern Decorator . Ci sono altri modi in cui potresti farlo, suppongo, ma Decorator è un modello molto elegante e facile da capire che è molto adatto a risolvere il tuo problema.

    
risposta data 01.10.2015 - 14:27
fonte
0

Che ne dici di usare il modello di comando?

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
                    }
                }
            }
        }
    }
}
    
risposta data 03.10.2015 - 14:05
fonte

Leggi altre domande sui tag