Question

Je lisais le guide de langage de programmation Java efficace de Joshua Bloch . ;.
Il explique que les méthodes d'usine statiques pourraient être utilisées pour éviter les objets en double inutiles .
Je n'ai pas bien compris cela.
Quelqu'un pourrait-il expliquer?

Était-ce utile?

La solution

Un exemple concret:

Java prend en charge les types primitif et objet pour représenter un octet. Lorsque vous convertissez une primitive en objet, vous pouvez faire quelque chose comme:

Byte b = new Byte( (byte) 65);

Cela créerait une nouvelle instance pour chaque appel. Au lieu de cela, vous faites:

Byte b = Byte.valueOf( (byte) 65);

À chaque appel, la méthode valueOf () renvoie la même instance d'un objet Byte représentant la valeur d'octet 65.

Après 10000 appels, le premier exemple aurait créé 10000 objets, alors que le second seulement, car la classe Byte possède un cache interne d'objets Byte représentant tous les nombres compris entre -128 et 127.

Autres conseils

Toutes les réponses concernant la non-duplication semblent se concentrer sur le modèle singleton, qui est un bon exemple de non-duplication, mais un modèle incorrect à utiliser dans le cas général. À mon avis, une application donnée devrait contenir zéro à un singletons, avec une préférence pour zéro. Cependant, cela n’a pas grand-chose à voir avec la nécessité de ne pas créer d’objets inutiles.

Considérez plutôt une application qui doit créer beaucoup d'objets Date. Il crée tellement d'objets Date que la construction des objets Date a un impact négatif sur les performances. Donc, au lieu d'appeler le constructeur de l'objet Date, le code est refactoré pour ne créer que des dates via une méthode de fabrique. Dans cette méthode d'usine, une carte est vérifiée pour voir si la date demandée a déjà été créée. Si c'était le cas, le même objet est renvoyé de la carte. Sinon, un nouveau est créé, inséré dans la carte et renvoyé.

Ce qui semble vous dérouter, c’est comment, en appelant la méthode factory, vous empêchez la création d’un objet en double. Le simple fait d'appeler une méthode factory ne change rien. L’appel de l’usine permet au code de prendre le relais et de prendre une décision concernant la création de l’objet. Lorsqu’il appelle à nouveau, aucune décision de ce type ne peut être prise.

Voir aussi cette question pour d'autres aperçu du motif et de son utilité.

Lorsque vous appelez un constructeur, il retournera toujours un nouvel objet (à moins qu'une exception ne soit levée). Les méthodes d'usine statiques, ou n'importe quel type d'usine, ne doivent pas toujours renvoyer un nouvel objet. Par exemple, la méthode getInstance () du modèle de conception Singleton traditionnel est une méthode d'usine qui renvoie toujours le même objet. Il y a des cas où parfois vous voulez faire ce genre de chose, que ce soit pour imposer un objet ne peut être instancié qu'une seule fois, ou pour créer une sorte de pool d'objets, etc. En général, je pense que c'est une raison marginale d'utiliser méthodes d'usine statiques. L’objectif principal est de créer des pseudo-constructeurs bien nommés.

Voici un exemple (quelque peu idiot) d'utilisation de méthodes fabriques statiques pour créer des pseudo-constructeurs bien nommés. Considérez cette classe:

class Person {

   public Person(Role role) {
      setRole(role);
   }

   ...
}

Sans les méthodes d'usine statiques, vous pourriez faire quelque chose comme ceci:

Person employee = new Person(Role.EMPLOYEE);
Person manager = new Person(Role.MANAGER);

À la place, vous pouvez créer des méthodes fabriques statiques:

class Person {

   public static Person newEmployee() {
      return new Person(Role.EMPLOYEE);
   }

   public static Person newManager() {
      return new Person(Role.MANAGER);
   }

   private Person(Role role) {
      setRole(role);
   }

   ...
}

et vous pourriez plutôt faire quelque chose comme ceci:

Person employee = Person.newEmployee();
Person manager = Person.newManager();

Cela peut ne pas être un bon exemple, mais considérons un constructeur plus complexe ou avec un paramètre moins descriptif. Parfois, le chemin de la méthode factory rend le code plus clair. Il y a bien sûr des inconvénients ...

En ce qui concerne la création d'objets, considérez une contrainte étrange, comme il ne peut jamais y avoir plus d'un PDG:

class Person {

   private static Person singletonCEO = new Person(Role.CEO);

   public static Person newCEO() {
      return singletonCEO;
   }

   ...
}

et comment il serait créé:

Person ceo1 = Person.newCEO();
Person ceo2 = Person.newCEO();

assertThat(ceo1, is(ceo2)); // JUnit 4.x

J'espère que ces exemples vous aideront.

Le modèle de méthode d'usine peut être utile en cas de besoin inutile de créer un nouvelle instance d'un objet afin d'effectuer une action.

Voici quelques cas généraux auxquels je peux penser où une méthode de fabrique statique qui retourne le même objet peut s'avérer utile:

  1. La création d'un objet est coûteuse : il y a beaucoup de traitement lorsque l'objet est instancié. Par conséquent, instancier l'objet plusieurs fois n'est pas souhaitable. (Ceci est également lié au modèle Singleton .)

  2. L'objet ne conserve aucun état : s'il n'y a pas de différence d'état entre les instances, il n'est pas utile de créer un nouvel objet à chaque fois.

La page Wikipedia sur le modèle de méthode d'usine contient davantage d'informations à ce sujet.

Regardons un exemple concret.

Le DateFormat La classe utilise le < code> getInstance méthode statique pour renvoyer une instance DateFormat , qui peut être utilisée pour formater une Date en une mise en forme prédéfinie en fonction de l'environnement local de la machine.

Etant donné que DateFormat renvoyé utilise la même mise en forme pour chaque action de mise en forme de date, il n'y a aucune raison réelle de créer une nouvelle instance DateFormat à chaque fois.

En général, cela est mis en œuvre pour créer une instance si une instance n'existe pas déjà, puis conserver une référence à cette instance. Si l'instance est à nouveau nécessaire, la référence est renvoyée. (C’est généralement ainsi que le motif Singleton est mis en œuvre.)

Par exemple:

class MySingleInstanceObject {

  private MySingleInstanceObject instance;

  private MySingleInstanceObject() {
    // Initialize the object.
    // This may be expensive.
  }

  public MySingleInstanceObject getInstance() {
    if (instance == null) {
      instance = new MySingleInstanceObject();
    }

    return instance;
  }
}

(Pour info, le code ci-dessus est un exemple de singleton. De plus, il n'est pas thread-safe.)

Si je me souviens bien, il donne également un exemple dans le livre. Considérons décimal . Zéro est assez souvent utilisé. Donc, si vous appelez la méthode de fabrique statique Decimal.valueOf ("0") (vous ne savez pas s'il s'agit de l'API réelle, mais cela n'a pas d'importance pour cet exemple) il vous renverra une instance du nombre décimal représentant 0 et ce sera la même instance pour tout appel. La mise en oeuvre ressemblerait à ceci:

public class Decimal {
    private static Decimal zero = new Decimal(0);

    public static Decimal valueOf(String s) {
        if (s.equals("0")) {
            return zero;
        } else {
            return new Decimal(parse(s)); // or whatever
        }

    // rest of the class
}

Notez qu'il n'y a qu'une seule instance de zéro, alors que pour tout autre nombre, un nouvel objet est créé. En outre, cela fonctionne avec les méthodes d'usine et vous ne pouvez pas le faire avec les constructeurs. C’est ce que Bloch essayait de souligner, comme un avantage pour les anciens.

Et, comme Yishai l’a mentionné, ce n’est pas si étroitement lié à Singleton. Comme vous pouvez le constater, de nombreux objets décimaux sont disponibles. Au lieu de cela, vous pouvez utiliser des méthodes d'usine pour avoir un contrôle total sur le nombre d'instances que vous créez. C’est pourquoi cela s’appelle une usine.

scroll top