lundi 11 janvier 2010

Initialiser une structure complexe en C# - petite étude sur la lisibilité de code - partie II

Introduction
Suite de l'article "Initialiser une structure complexe en C# - petite étude sur la lisibilité de code - partie I".

Pour rappel, le but de cette série est de pouvoir initialiser une structure complexes en utilisant un code C# facile à lire et à maintenir. Quelque-chose de similaire (dans l'écriture) aux possibilités offertes par Python.
Pour pouvoir suivre cet article il est conseillé de lire la première partie qui contient le code C# de départ.

Deuxième Jet
Afin de rendre l'écriture de l'initialisation un peu plus fluide, il serait idéal de déclarer les propriétés du CD avant les propriétés des tracks.
Bien que l'idée ne me plaise pas au premier abord (1), j'ai modifier la propriété Tracks de la classe CD afin d'autoriser le remplacement de la collection via un "setter".

(1) Remplacement d'une référence de collection interne via une "setter" propriété:
Par essence, il n'est pas opportun ni recommendable de publier une collection interne (ou objet interne ou structure interne) à l'aide d'un "setter", la disponibilité d'un "getter" étant amplement suffisant.
Un "getter" permet d'y accéder pour en modifier le contenu, c'est déjà largement suffisant.
Un "setter" permet de remplacer la référence de l'objet; autrement dit, de référé un autre objet créé par le développeur hors de la classe parente (l'instance de l'objet créé dans la classe devenant obsolète). Cela ouvre la porte a nombreux effets de bord et peu facilement devenir un cauchemar à débugger.

Modification de la classe CD

class CD {
    private List<CDTrack> _tracks;
    ...
    public List<CDTrack> Tracks { 
        get { return _tracks; }
        set {
            if (_tracks != null)
                _tracks = null; // free up current ressource
            _tracks = value;
        }
    }
    ...
}

Code d'initialisation
Avec cette dernière modification, le code d'initialization peut d'écrire comme suit:
List<CD> _CDs = new List<CD>();
_CDs.Add(new CD()
{
    Name = "This is it",
    Artist = "Jackson",
    Type = "Pop",
    Year = 2009,
    Info = "2/2",
    Tracks = new List<CDTrack>() { 
        new CDTrack { Name="I Just can't stop loving you", Seconds=139},
        new CDTrack { Name="Thriller", Seconds=308 },
        new CDTrack { Name="Beat It", Seconds=176}
    } 
});
Conclusion
Bien que le code ait acquis une bien meilleur lisibilité, je ne suis pas encore satisfait.
L'utilisation d'un "setter" pour publier la property "Tracks" ne me convient pas!

Troisième essais
Ce dernier essais se base sur le fait qu'une collection peut également être initialisée à l'aide d'un member initializer (les accolades {}).
Cela permet d'écrire l'initialization de façon encore plus claire sans nécessiter de "setter" pour la propriété Tracks.
De surcroit il n'est plus nécessaire d'ajouter les parenthèses derrière l'appel du constructeur.
Il est aussi possible d'écrite new CDTrack { Name="blabla" } au lieu de new CDTrack() { Name="blabla" }, cela améliore encore un peu plus la lisibilité.

Le nouveau code d'initialization devient donc:
List<CD> _CDs = new List<CD>();
_CDs.Add(new CD {
    Name = "History", Artist = "Jackson", Type = "Pop", Year = 2009, Info = "Continues, 2/2",
    Tracks =  { 
        new CDTrack { Name="Scream (duet with Janet Jackson)", Seconds=114},
        new CDTrack { Name="They don't care about us", Seconds=108 },
        new CDTrack { Name="Smile", Seconds=98},
        new CDTrack { Name="Money", Seconds=95}
    }
});
Conclusion
Cette dernière forme reste la meilleure option et celle que je retiendrais.
Cependant, il y a également les types anonymes (anonymous types apparu avec C# 3.0) dont la déclaration se rapproche le plus du langage Python.
Les types anonymes feront probablement l'objet d'un autre article.

Références

Aucun commentaire: