Exile on Keyboard St. - Blog sur Linux et Debian

Aller au contenu | Aller au menu | Aller à la recherche

lundi 6 août 2018

Java: Bien utiliser le Builder pattern

Quand on maintient du code Java, on tombe malheureusement assez souvent sur des méthodes aux signatures complexes comme celle-ci:

public int myMethod(String id, String name, Boolean secure, Object obj, Object backupObj, boolean restart)

Et il n'est pas rare d'avoir encore plus de paramètres !

La situation se dégrade encore quand pour "simplifier", on surcharge la méthode précédente en:

public int myMethod(String id, String name, Boolean secure, Object obj, Object backupObj)

quand restart vaut false puis en:

public int myMethod(String id, String name, Boolean secure, Object obj)

quand en plus backupObj est null.

On peut avoir ainsi 3 à 5 signatures différentes pour la même méthode. Et cela devient vite unsupportable quand on veut refactorer ces méthodes, parce qu'elles sont référencées n fois dans le code de production et également dans les tests.

Si le développeur qui a créé la première méthode avait créé un object et l'avait passé en paramètre de sa méthode, on n'en serait pas là !

Imaginons maintenant la méthode suivante qui permet de créer un utilisateur:

public long create(final String firstName, final String name, final int age)

Elle retourne l'identifiant de l'utilisateur créé. Supposons que l'age n'est pas obligatoire et que l'on souhaite anticiper l'ajout de nouveaux champs pour créer un utilisateur.

On va alors plutôt procéder comme suit:

public long create(final User user)

La classe User commencera comme ceci:

public class User
{
    private long id;
    private String firstName;
    private String lastName;
    private int age;
}

Mais pour créer un utilisateur à passer en paramètre de la méthode create, on va créer un Builder pour construire l'objet User.

Pour cela, on peut installer le Plugin Builder Generator. Un autre plugin est également disponible: Inner Builder.

Après redémarrage d'IDEA, un clic-droit puis Generate propose maintenant l'option "Builder" et on a alors la boite de dialogue suivante:

Capture-CreateBuilder.png

Ici on a choisi un "innerBuilder" afin que le Builder soit une classe interne de l'objet lui même.

On choisit ensuite la liste des champs à inclure, on ne prends pas l'id puisqu'il ne sera pas calculé par l'appelant de la méthode create mais bien par celle-ci:

Capture-Select_Fields_to_Be_Available_in_Builder.png

Et on obtient:


    public static final class UserBuilder
    {
        private String firstName;
        private String lastName;
        private int age;

        private UserBuilder()
        {
        }

        public static UserBuilder anUser()
        {
            return new UserBuilder();
        }

        public UserBuilder withFirstName(String firstName)
        {
            this.firstName = firstName;
            return this;
        }

        public UserBuilder withLastName(String lastName)
        {
            this.lastName = lastName;
            return this;
        }

        public UserBuilder withAge(int age)
        {
            this.age = age;
            return this;
        }

        public User build()
        {
            User user = new User();
            user.lastName = this.lastName;
            user.age = this.age;
            user.firstName = this.firstName;
            return user;
        }
    }

On crée ensuite notre User comme suit:

        final User user = UserBuilder
                    .anUser()
                    .withFirstName("Brian")
                    .withLastName("Kernighan")
                    .build();

Cette façon de faire permet:

  • de faire évoluer l'objet User en changeant un minimum de code
  • de valider les différents champs dans la méthode build

Et les signatures des méthodes ne changent pas.

On peut même éviter de dupliquer les champs de la classe User dans la classe UserBuilder en procédant comme ceci:

public static final class UserBuilder
    {
        private User user;

        private UserBuilder()
        {
            user = new User();
        }

        public static UserBuilder anUser()
        {
            return new UserBuilder();
        }

        public UserBuilder withFirstName(String firstName)
        {
            this.user.firstName = firstName;
            return this;
        }

        public UserBuilder withLastName(String lastName)
        {
            this.user.lastName = lastName;
            return this;
        }

        public UserBuilder withAge(int age)
        {
            this.user.age = age;
            return this;
        }

        public User build()
        {
            return user;
        }
    }

Mais notre générateur ne sait pas le faire :-(

jeudi 31 août 2017

Ajouter un Hook de Pre Commit dans IntelliJ IDEA

La boite de dialogue de commit d'IntelliJ IDEA propose entre autres de faire les actions suivantes avant le commit:

  • Reformatter le code
  • Optimiser les imports
  • Chercher les TODO

....

En revanche rien n'est prévu par défaut dans IntelliJ pour ajouter un script utlisateur de pre commit, qui par exemple va vérifier que vos traces ajoutées à des fins de debug ont bien été enlevées avant le commit.

Pour ajouter un Hook de pre commit, il faut installer un plugin, nommé Pre Commit Hook Plugin, qui va se charger d'éxécuter avant commit un script que vous aurez créé à la racine de votre projet et nommé pre-commit-hook.sh.

Le script sera appelé avec en paramètres les fichiers participant au commit. Il vous suffit donc dans le script de renvoyer le code retour 1 si vous avez oublié des traces de debug.

Ainsi vous éviterez les commits supplémentaires avec le message signficatif: "Oopppps: forgot to remove debug traces" !

Un exemple de script de pre commit:

#!/bin/bash

for file in "$@"; do
    grep -H -n DBG_TRACE "$file" && exit 1
done

exit 0

Le Pre Commit Hook Plugin est hébergé sur GitHub.

samedi 26 mars 2016

Configurer Solarized pour un terminal moins fatiguant pour la vue

Sous Gnome, et avec de nombreux environnements graphiques sous Linux, le profile par défaut du terminal a un fond blanc avec un texte en noir.

Ce choix de couleurs qui était préconisé quand j'étais étudiant est au contraire très fatiguant à la longue pour la vue, à cause de la forte luminosité causée par le fond blanc. C'est pourquoi d'autres configurations de couleurs sont apparues, comme par exemple le thème darcula d'IntelliJ.

On va voir dans ce billet comment utiliser le thème solarized pour:

  • La commande ls en configurant un fichier .dircolors
  • Positionner les couleurs du thème Solarized dans notre terminal

Solarized, c'est quoi ?

D'après son créateur Ethan Schoonover:

Solarized is a sixteen color palette (eight monotones, eight accent colors) designed for use with terminal and gui applications

En gros c'est une palette de couleurs, pour moitié lumineuses et pour moitié foncées.

Etape 1: Modifier les couleurs de ls avec dircolors

wget --no-check-certificate https://raw.github.com/seebi/dircolors-solarized/master/dircolors.ansi-dark
mv dircolors.ansi-dark ~/.dircolors
eval `dircolors ~/.dircolors`

Ces dernières commandes créent un fichier ~/.dircolors pris en compte par la commande ls pour coloriser chaque fichier, répertoire ou type de fichier de manière particulière. Au prochain login du système, il n'y a rien de particulier à faire puisque le fichier .bashrc appelle généralement la commande dircolors avec le fichier ~/.dircolors.

Pour en savoir plus sur la commande dircolors, on pourra consulter la doc de lea-linux.

Etape 2: Pour Gnome - Modifier les couleurs utilisées par gnome-terminal

Pour modifier les couleurs utilisées dans le profile du terminal, et donc dans le terminal, on va utiliser un autre repository sur Github:

git clone https://github.com/sigurdga/gnome-terminal-colors-solarized.git
cd gnome-terminal-colors-solarized
./set_dark.sh

Le script nous demande alors de choisir le thème de couleur:

  • dark
  • dark_alternative
  • light

puis le profile utilisé. Quand on a installé Linux en Français, c'est:

  • Par défaut (Default)

Ensuite on confirme et les couleurs sont instantanément modifiées dans le terminal.

Si l'on souhaite conserver le profile "Par défaut" inchangé, on peut commencer par le cloner en l'appelant par exemple "Solarized". C'est alors ce dernier profile que l'on modifiera avec ./set_dark.sh.

Etape 2: Pour Mate - Modifier les couleurs utilisées par mate-terminal

Si vous n'utilisez plus Gnome mais Mate à la place, il faut procéder comme suit:

git clone https://github.com/cledoux/mate-terminal-colors-solarized.git
cd mate-terminal-colors-solarized.git
./install.sh 

Note: Sur Debian Jessie, j'ai dû me déconnecter de l'interface graphique afin que les modifications du profil du terminal soient prises en compte.