89 votes

Meilleure façon de gérer les constructeurs multiples en Java

Je me suis demandé quelle était la meilleure façon (c'est-à-dire la plus propre, la plus sûre et la plus efficace) de gérer les constructeurs multiples en Java ? En particulier lorsque tous les champs ne sont pas spécifiés dans un ou plusieurs constructeurs :

public class Book
{

    private String title;
    private String isbn;

    public Book()
    {
      //nothing specified!
    }

    public Book(String title)
    {
      //only title!
    }

    ...     

}

Que dois-je faire lorsque les champs ne sont pas spécifiés ? Jusqu'à présent, j'ai utilisé des valeurs par défaut dans la classe afin qu'un champ ne soit jamais nul, mais est-ce une "bonne" façon de procéder ?

7voto

Scott Stanchfield Points 15863

Quelques conseils généraux sur les constructeurs :

  • Essayez de concentrer toute l'initialisation dans un seul constructeur et appelez-le à partir des autres constructeurs.
    • Cela fonctionne bien s'il existe plusieurs constructeurs pour simuler les paramètres par défaut.
  • Ne jamais appeler une méthode non finale à partir d'un constructeur
    • Les méthodes privées sont finales par définition
    • Le polymorphisme peut vous tuer ici ; vous pouvez finir par appeler l'implémentation d'une sous-classe avant que la sous-classe n'ait été initialisée
    • Si vous avez besoin de méthodes d'aide, veillez à les rendre privées ou finales.
  • Soyez explicite dans vos appels à super()
    • Vous seriez surpris du nombre de programmeurs Java qui ne réalisent pas que super() est appelé même si vous ne l'écrivez pas explicitement (en supposant que vous n'ayez pas d'appel à this(...) )
  • Connaître l'ordre des règles d'initialisation des constructeurs. C'est en gros :

    1. this(...) if present ( juste passer à un autre constructeur)
    2. appeler super(...) [si non explicite, appeler super() implicitement].
    3. (construire la superclasse en utilisant ces règles de manière récursive)
    4. initialiser les champs par le biais de leurs déclarations
    5. exécuter le corps du constructeur actuel
    6. retour aux constructeurs précédents (si vous avez rencontré des appels this(...))

En fin de compte, le flux global est le suivant :

  • remonter la hiérarchie des superclasses jusqu'à Object
  • tant que ce n'est pas fait
    • champs d'init
    • Exécuter les corps des constructeurs
    • descendre jusqu'à la sous-classe

Pour un bon exemple du mal, essayez de trouver ce que la commande suivante va imprimer, puis exécutez-la

package com.javadude.sample;

/** THIS IS REALLY EVIL CODE! BEWARE!!! */
class A {
    private int x = 10;
    public A() {
        init();
    }
    protected void init() {
        x = 20;
    }
    public int getX() {
        return x;
    }
}

class B extends A {
    private int y = 42;
    protected void init() {
        y = getX();
    }
    public int getY() {
        return y;
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        System.out.println("x=" + b.getX());
        System.out.println("y=" + b.getY());
    }
}

J'ajouterai des commentaires décrivant les raisons pour lesquelles ce qui précède fonctionne comme il le fait... Certaines de ces raisons peuvent être évidentes, d'autres non...

3voto

Steve Kuo Points 15196

Par ailleurs, si un champ est obligatoire ou si sa portée est limitée, il convient d'effectuer la vérification dans le constructeur :

public Book(String title)
{
    if (title==null)
        throw new IllegalArgumentException("title can't be null");
    this.title = title;
}

0voto

TofuBeer Points 32441

Je ferais ce qui suit :

public class Book
{
    private final String title;
    private final String isbn;

    public Book(final String t, final String i)
    {
        if(t == null)
        {
            throw new IllegalArgumentException("t cannot be null");
        }

        if(i == null)
        {
            throw new IllegalArgumentException("i cannot be null");
        }

        title = t;
        isbn  = i;
    }
}

Je pars du principe que

1) le titre ne changera jamais (le titre est donc définitif) 2) l'isbn ne changera jamais (l'isbn est donc définitif) 3) qu'il n'est pas valable d'avoir un livre sans titre et sans isbn.

Considérons une classe d'étudiants :

public class Student
{
    private final StudentID id;
    private String firstName;
    private String lastName;

    public Student(final StudentID i,
                   final String    first,
                   final String    last)
    {
        if(i == null)
        {
            throw new IllegalArgumentException("i cannot be null"); 
        }

        if(first == null)
        {
            throw new IllegalArgumentException("first cannot be null"); 
        }

        if(last == null)
        {
            throw new IllegalArgumentException("last cannot be null"); 
        }

        id        = i;
        firstName = first;
        lastName  = last;
    }
}

Un étudiant doit être créé avec un identifiant, un prénom et un nom. L'identifiant de l'étudiant ne peut jamais changer, mais le nom et le prénom d'une personne peuvent changer (mariage, changement de nom suite à la perte d'un pari, etc...).

Lorsqu'il s'agit de décider quels constrructeurs utiliser, il faut vraiment réfléchir à ce qu'il est logique d'avoir. Trop souvent, les gens ajoutent des méthodes set/get parce qu'on leur a appris à le faire, mais c'est très souvent une mauvaise idée.

Il est préférable d'avoir des classes immuables (c'est-à-dire des classes avec des variables finales) plutôt que des classes mutables. Ce livre : http://books.google.com/books?id=ZZOiqZQIbRMC&pg=PA97&sig=JgnunNhNb8MYDcx60Kq4IyHUC58#PPP1,M1 (Effective Java) présente une bonne discussion sur l'immutabilité. Regardez les points 12 et 13.

0voto

Craig P. Motlin Points 11814

Plusieurs personnes ont recommandé d'ajouter un contrôle de nullité. C'est parfois la bonne chose à faire, mais pas toujours. Consultez cet excellent article qui explique pourquoi vous ne devriez pas le faire.

http://misko.hevery.com/2009/02/09/to-assert-or-not-to-assert/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X