Archive

Articles taggués ‘clean-code’

Retour d’expérience : 2 ans avec Spring-Webflow

Il y a deux ans et demi, lorsque nous avons commencé à travailler sur Kwixo, il a fallu choisir un (des) framework(s) pour gérer la partie MVC. Le cahier des charges était énorme, et Spring MVC paraissait un peu juste pour gérer les enchaînements de pages demandés par la MOA.

Par exemple, le formulaire d’inscription est réparti sur plusieurs pages et implique l’envoi d’un code via SMS sur le téléphone (qui peut-être ignoré). Bref, une cinématique complexe.

Lire la suite…

Share

Clean Android Code : I CAN HAZ INT IDS?

Disclaimer : cet article contient des switch, et pourrait donc heurter la sensibilité d’un public non averti. Je tiens à décliner toute responsabilité en cas de switchite aiguë consécutive à une pratique trop assidue d’Android.

Développeurs Android, avez-vous remarqué à quel point votre code utilise des int comme identifiant à tout bout de champ ?
Il n’est pas rare de devoir écrire le code suivant :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyActivity extends Activity {

    private static final int WARNING_DIALOG = 0;
    private static final int DOWNLOAD_PROGRESS_DIALOG = 1;
    private static final int CONFIRM_LOGOUT_DIALOG = 2;

    public void iCanHasCheezburger() {
        showDialog(WARNING_DIALOG);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case WARNING_DIALOG:
            return createWarningDialog();
        case DOWNLOAD_PROGRESS_DIALOG:
            return createDownloadProgressDialog();
        case CONFIRM_LOGOUT_DIALOG:
            return createConfirmLogoutDialog();
        default:
            return null;
        }
    }
    // [...]
}

Cette manière de faire est d’ailleurs recommandée par le guide de développement Android officiel.

Papy Android réalisé avec Androidify : http://markoi.de/HIy


Lire la suite…

Share

Refactoring par la pratique

Dans cet article, nous allons procéder à un refactoring sur un exemple concret, afin de mettre en exergue quelques bonnes pratiques.

Défi : comprendre du code inélégant*

*Edit : j’avais initialement écrit “imb*table”, mais il paraît que ça ne fait pas très sérieux :-P

Êtes-vous capable de comprendre ce que fait la méthode computeConvexHull() en moins de 3 minutes ?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public List<GeoPoint> computeConvexHull(Set<GeoPoint> points) {

    // Path list
    ArrayList<GeoPoint> path = new ArrayList<GeoPoint>();

    // Pick the min longitude point as first
    GeoPoint first = null;
    for (GeoPoint point : points) {
        if (first == null || point.getLongitude() < first.getLongitude()) {
            first = point;
        }
    }

    if (first != null) {
        path.add(first);
    } else {
        throw new IllegalArgumentException("Can't retrieve the western point");
    }

    boolean right = true;
    boolean loop = true;

    while (loop) {

        GeoPoint current = path.get(path.size() - 1);

        GeoPoint next = null;
        double nextLatitudeDiff = 0;
        double nextLongitudeDiff = 0;
        boolean recordNext = false;
        for (GeoPoint point : points) {
            if (!point.equals(current)) {
                double latitudeDiff = point.getLatitude() - current.getLatitude();
                double longitudeDiff = point.getLongitude() - current.getLongitude();
                if (longitudeDiff == 0 || longitudeDiff > 0 == right) {
                    if (next == null) {
                        recordNext = true;
                    } else {
                        // Compare the 'a' in the linear equation Y = a.X
                        // that correspond to the line slope.
                        boolean negativeSlope = latitudeDiff * nextLongitudeDiff - nextLatitudeDiff * longitudeDiff < 0;

                        if (negativeSlope) {
                            recordNext = true;
                        }
                    }

                    if (recordNext) {
                        recordNext = false;
                        next = point;
                        nextLatitudeDiff = latitudeDiff;
                        nextLongitudeDiff = longitudeDiff;
                    }
                }
            }
        }

        if (next != null) {

            // Check abnormal case
            if (path.size() > points.size() + 1) {
                throw new RuntimeException("Convex hull computation failed");
            }

            path.add(next);

            /*
             * If we are not back to the first point, let's continue finding
             * the path.
             *
             * Otherwise, do nothing => going back from the stack, this is
             * actually the end point of the recursive algorithm.
             */


            if (path.get(0) == next) {
                loop = false;
            }

        } else {
            // We have reached the point on the right, let's go back!
            right = !right;
        }
    }

    return path;
}

Alors ? Checkstyle lui donne une complexité cyclomatique de 16. Plutôt difficile d’y voir clair. Moi en tout cas, je n’en suis pas capable.
Et pourtant, j’en suis l’auteur :-). Critique de code bien ordonnée commence par soi-même ! Lire la suite…

Share

Dé-switcher n’est pas jouer

Amis utilisateurs de Checkstyle, avez-vous remarqué que les switch sont des sources inépuisables de complexité cyclomatique ? Sans parler de leur équivalent pour les objets, les if () {} else if (){} else if (){} else if (){} à répétition.

Parmi les reproches récurrents faits aux switch, il y a le fait qu’ils ne respectent pas le principe Ouvert/Fermé. A chaque ajout de nouvelles valeurs, il faut modifier tous les switch qui les manipulent.

Lire la suite…

Share

HibernateGenericDAO considered harmful

La factorisation de code est une des grandes promesses de la programmation orientée objet. Ainsi, dès que l’on a passé le niveau “novice”, on meurt d’envie de factoriser du code et l’on tombe dans le premier piège de la POO : abuser de l’héritage. La désillusion n’en sera que plus grande dès que l’on passera au niveau “confirmé” :). Mais ce n’est pas le sujet de ce billet. Non, dans ce billet je vous propose de voir un effet plus pervers de ce problème, induit par le mode de fonctionnement d’Hibernate, et la paresse humaine. Lire la suite…

Share