mercoledì 6 luglio 2011

Anti-If in Java ME

In Java Micro Edition l'Event Handling dei comandi utente sulla GUI viene implementato con due classi: il Command e il suo CommandListener. Il ruolo di ciascuna é ben descritto nel Javadoc della classe Command.

The Command class is a construct that encapsulates the semantic information of an action. The behavior that the command activates is not encapsulated in this object. This means that command contains only information about "command" not the actual action that happens when command is activated. The action is defined in a CommandListener associated with the Displayable.

La scelta architetturale della piattaforma MIDP é stata quindi una separazione fra l'esecuzione di un comando e l'azione che ne deve conseguire.

Se cercate degli esempi di programmazione di questo meccanismo, a cominciare da uno della stessa Oracle ™, il codice appare quasi sempre come segue.

public class LCDUICommandByEqualsMIDlet extends MIDlet implements CommandListener {

    private Command exitCommand = new Command("Exit",Command.EXIT, 1);
    private Command sayYesCommand = new Command("Say Yes", Command.SCREEN, 2);
    private Command sayNoCommand = new Command("Say No", Command.SCREEN, 3);
    private Command sayMaybeCommand = new Command("Say Maybe", Command.SCREEN, 4);

    ...
    
    protected void startApp() throws MIDletStateChangeException {
        Form form = new Form("Command-By-Equals-MIDlet - LCDUI Version");
        
        form.addCommand(exitCommand);
        form.addCommand(sayMaybeCommand);
        form.addCommand(sayNoCommand);
        form.addCommand(sayYesCommand);
        
        form.setCommandListener(this);
        
        Display.getDisplay(this).setCurrent(form);
    }

    ...

    public void commandAction(Command command, Displayable displayable) {
        if(command == exitCommand) {
            notifyDestroyed();
        } else
        if(command == sayYesCommand) {
            ...
        } else
        if(command == sayNoCommand) {
            ...
        } else
        if(command == sayMaybeCommand) {
            ...
        }
    }
}

Sembra evidente, in caso di aggiunta di comandi alla GUI, il pericolo di una proliferazione degli IF, che rendono il codice poco leggibile e complesso.

Adottando la libreria Oracle ™ LWUIT (Lightweight UI Toolkit), oltre a beneficiare di widget grafici piú avanzati di quelli messi a disposizione dal package LCDUI standard, é possibile sbarazzarsi anche degli IF per la determinazione dei comandi invocati. Vediamo come.

Il Command LWUIT, unisce al concetto di comando MIDP, quello di Action di Swing. Espone, infatti, il metodo actionPerformed(ActionEvent evt) che viene invocato in callback quando viene cliccato il relativo bottone sulla GUI. Questo significa che la risoluzione del comando viene realizzata senza doverla esplicitamente programmare.

Sfruttando questo comportamento si puó impostare una superclasse astratta come segue:

public abstract class AntiIfCommand extends Command {

    public AntiIfCommand(String command) {
        super(command);
    }
    
    public void actionPerformed(ActionEvent evt) {
        execute();
    }

    public abstract void execute();
}

Quindi estenderla per implementare i comportamenti voluti all'atto del click dell'utente. Ad esempio, un ipotetico ExitCommand:

public class ExitCommand extends AntiIfCommand {

    private final MIDlet midlet;

    public ExitCommand(MIDlet midlet) {
        super("Exit");
        this.midlet = midlet;
    }

    public void execute() {
           midlet.notifyDestroyed();
    }

}

Oppure, un altro comando:

public class SayYesCommand extends AntiIfCommand {

    public SayYesCommand() {
        super("Say Yes");
    }

    public void execute() {
        sayYes();
    }

    private void sayYes() {
        Dialog.show("Yes Command", "Yes! :)", OK, null);
    }

}

Realizzando i comandi in questa maniera il MIDlet, cambia come segue:

public class LWUITCommandByOopMIDlet extends MIDlet {

    ...

    protected void startApp() throws MIDletStateChangeException {
        Form form = new Form("Command-By-OOP-MIDlet");
        
        form.addCommand(new ExitCommand(this));
        form.addCommand(new SayMaybeCommand());
        form.addCommand(new SayNoCommand());
        form.addCommand(new SayYesCommand());
        
        form.show();
    }

    ...
}

Questa versione del MIDlet, come si puó notare, non implementa alcun listener di eventi; di conseguenza, il codice non viene complicato da nessuna sequenza di IF.

Per chi se lo stesse chiedendo, ho dato una breve occhiata anche ai risultati prodotti da Metrics sulle due versioni del codice. La prima versione del MIDlet presentava degli warning su complessitá ciclomatica, linee di codice per metodo e numero livelli.
Nella versione No-If non ci sono warning sollevati. :)

Potete trovare qui alcuni gist piú completi.

giovedì 14 gennaio 2010

CleanCode/2 - It is not enough for code to work

Chiedo scusa a quanti si risentirono pesantemente quel giorno quando dissi che il codice prodotto di corsa in un mese di overtime non fosse di qualità. Perchè di qualità si trattava, non del fatto che funzionasse o meno, non del fatto che il cliente ne fosse soddisfatto o meno. Non era mia intenzione sminuire il lavoro di nessuno.
Perdonatemi, ma...
It is not enough for code to work. Code that works is often badly broken. Programmers who satisfy themselves with merely working code are behaving unprofessionally. They may fear that they don't have time to improve the structure and design of their code, but I disagree. Nothing has a more profound and long-term degrading effect upon a development project than bad code.
Bad schedules can be redone, bad requirements can be redefined. Bad team dynamics can be repaired. But bad code rots and ferments, becoming an inexorable weight that drags the team down. tiem and time again I have seen teams grind to a crawl because, in their haste, they created a malignant morass of code that forever thereafter dominated their destiny.
Of course bad code can be cleaned up. But it's very expensive. As code rots, the moules insinuate themselves into each other, creating lots of hidden and tangled dependencies is a long and arduous task. If you made a mess in a module in the morning , it is easy to clean it up in the afternoon. Better yet, if you made a mess five minutes ago, it's very easy to clean it up rigt now.
So the solution is to continuously keep code as clean and simple as it can be. Never let the rot get started.
Chapter 14: Successive Refinement - Conclusion - pag. 250

Ricordate quando tante volte abbiamo ripensato al passato e ci siamo chiesti "Ma perchè abbiamo fallito?". Personalmente aggiungo questa qui: non sappiamo scrivere codice pulito.

venerdì 1 gennaio 2010

Il TDD oggi

"It is 2010. I declare that #TDD is no longer controversial."
"@bradwilson #TDD I am not wishing. I am DECLARING. Resistance is futile!"
Uncle Bob - Twitter - 01/01/2010

mercoledì 2 dicembre 2009

Agile & Fear

People fear that agile disciplines will cause delays.  The opposite is true.  We use these disciplines because they help us go faster.
Uncle Bob - Twitter - 02/12/2009

@unclebobmartin and because many are developer disciplines and not management disciplines as some understand  management disciplines.
Tim Ottinger - Twitter - 02/12/2009

venerdì 27 novembre 2009

Libri Tech in Italia

Ho scoperto da poco che moltissimi libri che ho comperato negli anni da Amazon.com, sono disponibili anche direttamente in Italia sul sito di Libreria Universitaria, come il recente "Growing Object-Oriented Software, Guided by Tests".
Benissimo! :)

lunedì 23 novembre 2009

CleanCode/1 - Procedural code vs. OO code

In questi giorni sto leggendo "Clean Code" di Robert "Uncle Bob" Martin e volevo regalarvi alcune perle che mano mano sto trovando.

Nel capitolo 6 - Objects and Data Structures, pagina 97, circa l'antisimmetria fra il codice procedurale e il codice ad oggetti.

Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.

The complement is also true:

Procedural code makes it hard to add new data structures because all the functuions must change. OO code makes it hard to add new functions because all the classes must change.

So, the things that are hard for OO are easy for procedures, and the things that are hard for procedures are easy for OO!

Ma anche "est modus in rebus" ;)

Mature programmer know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them.

Ammetto candidamente di non aver mai pensato alle differenze fra i due paradigmi in questi termini. Anche per quello mi considero in cammino...

giovedì 19 novembre 2009

Agile è come...

Agile è come il sesso adolescenziale
Chi ne parla tanto, per lo più non lo pratica.
Chi lo pratica, non ne parla più di tanto.
E comunque non ce n'è più di tanto in giro! ;)
--
Uberto Barbini - it-milano-java-jug - 19/11/2009