Aller au contenu

Patrons de conception/Type fantôme

Un livre de Wikilivres.
Patron de conception
Catégorie : Autre
Nom français : Type fantôme
Nom anglais : Phantom type
Utiliser un type pour détecter des erreurs plutôt que pour ses valeurs possibles


En général, un type de données est créé afin de pouvoir utiliser ses valeurs possibles.

Un type fantôme n'est utilisé que pour ajouter une contrainte qui puisse être vérifiée à la compilation. Cette fonctionnalité est utilisable avec la programmation fonctionnelle et également la programmation orientée objet en utilisant les templates ou types génériques.

Cet exemple utilise une classe dont les instances peuvent être en lecture seule ou écriture seule.

public class PName<V>
{
    // Types fantômes utilisés pour marquer les méthodes
    // dont les appels seront vérifiés à la compilation.
    public static interface ReadOnly { }
    public static interface WriteOnly { }
    public static interface ReadWrite extends ReadOnly, WriteOnly { }
    // Des interfaces sont utilisées au lieu de classes afin que
    // ReadWrite hérite des deux types précédents.

    private String name;

    private PName(String s) { this.name = s; }

    public static PName<ReadWrite> pname(String s)     { return new PName<ReadWrite>(s); }
    public static PName<ReadOnly>  readOnly(String s)  { return new PName<ReadOnly>(s); }
    public static PName<WriteOnly> readWrite(String s) { return new PName<WriteOnly>(s); }

    // Opérations de conversion
    @SuppressWarnings("unchecked")
    public static PName<ReadOnly> readable(PName<? extends ReadOnly> pname)
    { return (PName<ReadOnly>) pname; }
    @SuppressWarnings("unchecked")
    public static PName<WriteOnly> writeable(PName<? extends WriteOnly> pname)
    { return (PName<WriteOnly>) pname; }

    // Opérations de lecture et d'écriture
    public static int get(PName<? extends ReadOnly> pname)
    { return pname.name; }
    public static void set(PName<? extends WriteOnly> pname, String v)
    { pname.name = v; }

    public static void main(String[] args) // Test
    {
        // Nom en lecture et écriture
        PName<ReadWrite> rw = pname("Test"); 
        System.out.println(get(rw));       // Lecture OK
        set(rw,"Exemple");                 // Écriture OK
        System.out.println(get(rw));       // Lecture OK

        // Version en lecture seule
        PName<ReadOnly> ro = readable(rw);
        System.out.println(get(ro));       // Lecture OK
        //set(ro,"Autre");                 // Écriture --> erreur à la compilation

        // Version en écriture seule
        PName<WriteOnly> wo = writeable(rw);
        set(wo,"Modification");            // Écriture OK
        //get(wo);                         // Lecture --> erreur à la compilation
    }
}

Les types ReadOnly, WriteOnly et ReadWrite sont des types fantômes (aucune instance existante) utilisés uniquement pour marquer le type PName et que le compilateur puisse effectuer les vérifications.