Différences entre "Objet o = new Objet()" et "Objet.creerObjet()"

Le problème exposé dans ce sujet a été résolu.

Bonjour à toutes et à tous,

Je vous écris aujourd’hui car je rencontre de plus en plus souvent l’écriture de code suivante. C’est peut-être aussi parce que j’y fais plus attention maintenant. Ici en Java, cela donnerait par exemple :

UnObjet.creer(param1, param2);

public class UnObjet
{
    public UnObjet(int param1, int param2)
    {
      // Initialisation de l'objet
    }

    public static UnObjet creer(int param1, int param2)
    {
        return new UnObjet (param1, param2);
    }
}

Les développeurs passent donc par une fonction intermédiaire qui se charge de créer l’objet en question. Ici, ce serait la méthode "creer(…)".

Mais nous pouvons aussi voir que l’objet pourrait très bien être appelé de la manière suivante :

UnObjet o = new Objet(param1, param2);

Ma question, est donc : Pourquoi les développeurs font cette manipulation supplémentaire ? Cela a-t-il des conséquences sur les performances ? La structure du code ?

Je suis désolé pour cette question peut-être bête.

Merci beaucoup pour vos réponses, Cordialement, AlliageSphere

+0 -0

Salut,

Ma question, est donc : Pourquoi les développeurs font cette manipulation supplémentaire ? Cela a-t-il des conséquences sur les performances ? La structure du code ?

AlliageSphere

Tel quel, ça fait surtout mauvaise pratique d’un cours pas à jour. Normalement, toutes les initialisations se font dans le constructeur.

Tu aurais du contexte ou un exemple concret, pour pouvoir t’en dire un peu plus ?

Bonjour ! Merci de ta réponse. :)

Je vais essayer de te trouver un bon exemple. Bon…J’ai un tas d’autres utilisations de ce style. Si cet exemple ne te convient pas, je pourrais toujours en trouver un autre. Je vais chercher en attendant. ;)

public class Codec8
{
    public Codec8(DataInputStream dis) throws Exception
    {
        // Initialisation de l'objet
    }

    public AvlDataCollection decodeAvlDataCollection() throws IOException
    {
        // Récupération de codecId, nbData et data

        return AvlDataCollection.create(codecId, nbData, data); // Ici
    }

    private GpsElement decodeGpsElement() throws IOException
    {
        // Récupération de longitude, latitude, altitude, vitesse, orientation, nbreSatellites

        return GpsElement.creer(longitude, latitude, altitude, vitesse, orientation et nbreSatellites); // Ici
    }
}

// Ou encore
ArrayList result = new ArrayList<IoProperty>();

int x = 10;
for (int i = 0; i < 10; i++)
{
    // Code

    result.add(IoProperty.create(propertyId, value));// Ici
}

Le Java lui même utilise une écriture semblable. Par exemple avec les expressions régluières :

Pattern regex = Pattern.compile("\\d{15}");

Désolé pour tous les exemples. o_O

+0 -0

Salut,

Si ton langage n’accepte qu’un seul constructeur par classe, c’est compréhensible. Ici ce n’est pas le cas, c’est du Java.

Dans ton exemple avec Code8, c’est plutôt bof, le programmeur aurait dû utiliser un constructeur (mais on remarque que ce n’est pas du code très « pro » avec par exemple le mélange de français et d’anglais). C’est d’autant plus vrai que le nom de la méthode ne donne aucune indication sur comment est créé l’objet.

Dans ton deuxième exemple, add ne semble pas créer pas une nouvelle instance de ArrayList, mais juste modifier l’instance qui l’appelle. Donc ce n’est pas la même chose. Néanmoins, c’est le genre de cas où ne pas utiliser un constructeur n’est pas une mauvaise idée. Par exemple pour créer une nouvelle liste qui est la concaténation de deux listes List.concat(l1, l2) est plutôt bien.

+2 -0

Tiens tu peux regarder ce lien qui te donnera déjà quelques explications, même si ce n’est pas la panacée.

Et on peut éventuellement analyser ton exemple de Pattern.compile(...) vis à vis des arguments qu’il donne.

Allons voir dans les sources de Pattern.compile :

    public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

Là il fait donc appel à un constructeur privé :

    private Pattern(String p, int f) {
        pattern = p;
        flags = f;

        // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
        if ((flags & UNICODE_CHARACTER_CLASS) != 0)
            flags |= UNICODE_CASE;

        // Reset group index count
        capturingGroupCount = 1;
        localCount = 0;

        if (pattern.length() > 0) {
            compile();
        } else {
            root = new Start(lastAccept);
            matchRoot = lastAccept;
        }
    }

``` 

Qui lui-même, dans le cas le plus courant (tu lui as donné un vrai pattern, de longueur > 0) fait appel à la méthode `compile` que je ne vais pas copier coller ici car elle est très longue.
Et c'est bien ça qui est important là-dedans, et souligné dans le lien que je t'ai donné plus haut.

La compilation d'un pattern est une opération assez coûteuse. `new Pattern(...)` ne donne pas énormément d'info sur le traitement qui pourrait être fait dans le constructeur. Par contre `Pattern.compile(...)` qui contient le mot "compile" éveille déjà un peu plus l'attention sur le fait que c'est une opération importante.

Autre chose, si on regarde bien ce qui est fait dans la méthode `compile` on voir certes beaucoup d'initialisations de champs ou propriétés du `Pattern`, mais tout plein d'optimisations et de vérifications, là où on préfèrerait conserver un constructeur pour l'initialisation "brute" de champs et propriétés.

Je dirais qu'une "règle de base" (*rule of thumb* ?) serait : 

Si pour instancier ta classe, tu lui passes quelques arguments qui seront bêtement copiés dans les champs de ta classe (même si c'est un constructeur qui "copie" d'un objet à un autre) un constructeur fait l'affaire.

Exemple : 

```java
class Complex {
  public final int real;
  public final int imaginary;

  public Complex(int real, int imaginary) {
    this.real = real;
    this.imaginary;
  }
}

Exemple type d’un cas où une factory statique serait intéressant : admettons que tu veuilles construire un pur réel, ou un pur imaginaire.

public Complex(int ???) { }

Tu vas avoir un conflit, les deux constructeurs "pur réel" et "pur imaginaire" vont avoir la même signature.

Pour lever l’ambiguïté, tu peux écrire deux méthodes statiques dans la class Complex qui distinguent le cas du réel pur et de l’imaginaire pur. C’est le premier argument qui est avancé dans l’article que je t’ai donné.

etc. :)

Lis les autres arguments donnés dans le lien, et si tu n’en comprends pas certains on t’aidera !

+2 -0

@Karnaj Concernant le premier exemple, tu conseilles donc plutôt d’appeler les constructeurs directement. Parce que sinon, il n’y a pas de raisons de le faire. C’est bien ça ?

Pour le second, je comprends. Et puis ce serait aussi plus court du coup.

@Javier Merci beaucoup pour l’explication de l’exemple Pattern ainsi que pour le lien ! De plus, je ne savais pas comment appeler ces deux façons d’écrire. Les 4 points du début semblent assez clairs. Par contre, je ne comprends pas très bien le fonctionnement des singletons expliqués dans la partie 6. Aussi, je viens de découvrir le mot clef "volatile". :o

Cette écriture est donc utile pour la clarté / compréhension du code…C’est super du coup ! :magicien: Donc on peut presque en "abuser" alors. :)

Ton dernier exemple est très bien aussi. Il m’est arrivé dans de rares cas d’avoir ce soucis de constructeurs possédants la même signature. Cette écriture m’aurait permis de résoudre le problème plus facilement.

Et de ce que j’ai pu comprendre dans la conclusion, cela semble être une méthode plutôt fréquente dans les programmes. C’est rassurant. :)

Merci beaucoup à vous !

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte