À quoi sert de rendre le constructeur privé dans une classe?

134

Pourquoi devrions-nous rendre le constructeur privé en classe? Comme nous avons toujours besoin que le constructeur soit public.

giri
la source

Réponses:

129

Quelques raisons pour lesquelles vous pourriez avoir besoin d'un constructeur privé:

  1. Le constructeur n'est accessible qu'à partir de la méthode de fabrique statique à l'intérieur de la classe elle-même. Singleton peut également appartenir à cette catégorie.
  2. Une classe utilitaire , qui ne contient que des méthodes statiques.
nanda
la source
9
Je n'aurais même pas pris la peine de créer un constructeur privé pour une classe utilitaire.
Petesh le
16
@Patesh: C'est votre décision. D'autres et moi préférerions éviter une instanciation de classe d'utilité plutôt que de laisser de côté le constructeur privé d'une ligne.
nanda
1
dans certains langages de programmation (notamment Java), il empêche également l'héritage
dfa
9
@dfa: pour éviter l'héritage, il faut mettre finalau niveau de la classe. Mettre un constructeur privé est pratiquement inutile pour cette raison.
nanda
3
@Will: Pas si vous utilisez la réflexion. La déclaration des constructeurs privés vise à empêcher l'instanciation (interdiction de la réflexion), mais empêcher le sous-classement est un effet secondaire, pas l'intention. L'outil approprié pour cela est de déclarer la classe final. C'est ce que Sun a fait pour la classe String et une partie raisonnable de l'API qui ne devrait pas être étendue.
BobMcGee
96

En fournissant un constructeur privé, vous empêchez la création d'instances de classe à un endroit autre que cette classe. Il existe plusieurs cas d'utilisation pour fournir un tel constructeur.

A. Vos instances de classe sont créées dans une staticméthode. La staticméthode est ensuite déclarée comme public.

class MyClass()
{
private:
  MyClass() { }

public:
  static MyClass * CreateInstance() { return new MyClass(); }
};

B. Votre classe est un singleton . Cela signifie qu'il n'existe pas plus d'une instance de votre classe dans le programme.

class MyClass()
{
private:
  MyClass() { }

public:
  MyClass & Instance()
  {
    static MyClass * aGlobalInst = new MyClass();
    return *aGlobalInst;
  }
};

C. (S'applique uniquement au prochain standard C ++ 0x) Vous avez plusieurs constructeurs. Certains d'entre eux sont déclarés public, d'autres private. Pour réduire la taille du code, les constructeurs publics «appellent» des constructeurs privés qui à leur tour font tout le travail. Vos publicconstructeurs sont donc appelés constructeurs délégués :

class MyClass
{
public:
  MyClass() : MyClass(2010, 1, 1) { }

private:
  MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};

D. Vous souhaitez limiter la copie d'objets (par exemple, en raison de l'utilisation d'une ressource partagée):

class MyClass
{
  SharedResource * myResource;

private:
  MyClass(const MyClass & theOriginal) { }
};

E. Votre classe est une classe utilitaire . Cela signifie qu'il ne contient que des staticmembres. Dans ce cas, aucune instance d'objet ne doit jamais être créée dans le programme.

Kerido
la source
3
Dans la plupart des cas, vous souhaitez empêcher la copie d'objets. Vous ne fourniriez donc pas d'implémentation pour le constructeur de copie privée.
vendredi
du guide de style google c ++ (un autre exemple pas sur la liste mais courant) -> si vous avez besoin de travailler dans le constructeur "considérez une fonction d'usine [et rendez le constructeur privé]" (le texte entre accolades est écrit par moi )
Trevor Boyd Smith
12

Laisser une "porte dérobée" qui permet à une autre classe / fonction amie de construire un objet d'une manière interdite à l'utilisateur. Un exemple qui me vient à l'esprit serait un conteneur construisant un itérateur (C ++):

Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
//     and Container is a friend of Iterator.
Émile Cormier
la source
Est-ce assez différent, Terry? ;)
Emile Cormier
Bonne réponse. Pourquoi ne pas mentionner la réflexion, car c'est une voie pour accomplir quelque chose que l'utilisateur ne peut pas?
BobMcGee
@Bob: Vous devrez m'éclairer là-dessus, car je suis principalement un gars C ++ et la réflexion n'est pas vraiment dans le vocabulaire C ++. ;)
Emile Cormier
3
Personne ne lira ceci, mais voilà: j'ai été critiqué à plusieurs reprises sur celui-ci. Pas contrarié ou quoi que ce soit, mais (pour apprendre) j'aimerais savoir pourquoi c'est mauvais? Sinon, comment un itérateur pourrait-il être construit avec les données dont il a besoin pour accéder aux données d'un conteneur?
Emile Cormier
2
@EmileCormier, je pense que vous avez été injustement critiqué parce que les gens qui apprennent le C ++ se font répéter sans cesse: "Évitez les déclarations d'amis". Ce conseil semble être destiné aux programmeurs C ++ inexpérimentés qui pourraient autrement utiliser friendlà où cela n'est pas justifié - et il y a de nombreux cas où c'est une mauvaise idée. Malheureusement, le message a été trop bien reçu et de nombreux développeurs n'apprennent jamais assez bien la langue pour comprendre que l'utilisation occasionnelle de friendest non seulement acceptable, mais préférable . Votre exemple était exactement un tel cas. Un couplage fort délibéré n'est pas un crime, c'est une décision de conception.
evadeflow
10

Tout le monde est coincé sur le truc Singleton, wow.

Autres choses:

  • Empêchez les gens de créer votre classe sur la pile; créer des constructeurs privés et ne rendre les pointeurs que via une méthode de fabrique.
  • Empêcher la création de copies de la classe (constructeur de copie privée)
Terry Mahaffey
la source
8

Cela peut être très utile pour un constructeur qui contient du code commun; les constructeurs privés peuvent être appelés par d'autres constructeurs, en utilisant le 'this (...);' notation. En rendant le code d'initialisation commun dans un constructeur privé (ou protégé), vous indiquez également explicitement qu'il n'est appelé que pendant la construction, ce qui n'est pas le cas s'il s'agissait simplement d'une méthode:

public class Point {
   public Point() {
     this(0,0); // call common constructor
   }
   private Point(int x,int y) {
     m_x = x; m_y = y;
   }
};
Volonté
la source
3

Dans certains cas, vous ne souhaiterez peut-être pas utiliser de constructeur public; par exemple si vous voulez une classe singleton.

Si vous écrivez un assembly utilisé par des tiers, il peut y avoir un certain nombre de classes internes que vous souhaitez uniquement créer par votre assembly et ne pas être instanciées par les utilisateurs de votre assembly.

Kane
la source
3

Cela garantit que vous (la classe avec le constructeur privé) contrôlez la façon dont le constructeur est appelé.

Un exemple: une méthode de fabrique statique sur la classe peut renvoyer des objets lorsque la méthode de fabrique choisit de les allouer (comme une fabrique de singleton par exemple).

Preet Sangha
la source
@Skilldrick: un exemple serait que vous pouvez forcer une classe à être uniquement allouée au tas.
Dirk
@dirk J'ai supprimé mon commentaire car il n'est plus pertinent.
Skilldrick
3

On peut aussi avoir un constructeur privé, pour éviter la création de l'objet par une classe spécifique uniquement (pour des raisons de sécurité).

Une façon de le faire est d'avoir une classe d'amis.

Exemple C ++:

class ClientClass;
class SecureClass 
{
  private:
    SecureClass();   // Constructor is private.
    friend class ClientClass;  // All methods in 
                               //ClientClass have access to private
                               // &   protected methods of SecureClass.
};

class ClientClass
{
public:
    ClientClass();
    SecureClass* CreateSecureClass()
     { 
           return (new SecureClass());  // we can access 
                                        // constructor of 
                                        // SecureClass as 
                                        // ClientClass is friend 
                                        // of SecureClass.
     }
};

Remarque: Remarque: seul ClientClass (puisqu'il est ami de SecureClass) peut appeler le constructeur de SecureClass.

vijayNidumolu
la source
2

lorsque vous ne voulez pas que les utilisateurs créent des instances de cette classe ou créent une classe qui hérite de cette classe, comme le java.lang.math, toutes les fonctions de ce package sont static, toutes les fonctions peuvent être appelées sans créer d'instance de math, donc le constructeur est annoncé comme statique .

shinxg
la source
1

Si c'est privé, vous ne pouvez pas l'appeler ==> vous ne pouvez pas instancier la classe. Utile dans certains cas, comme un singleton.

Il y a une discussion et quelques autres exemples ici .

Seth
la source
1

J'ai vu une question de votre part concernant le même problème.

Simplement si vous ne voulez pas permettre aux autres de créer des instances, gardez le constructeur dans une portée limitée. L'application pratique (un exemple) est le modèle singleton.

Chathuranga Chandrasekara
la source
1

Vous ne devez pas rendre le constructeur privé. Période. Rendez-le protégé afin de pouvoir étendre la classe si vous en avez besoin.

Edit: Je reste fidèle à cela, quel que soit le nombre de votes négatifs que vous lancez. Vous coupez le potentiel de développement futur du code. Si d'autres utilisateurs ou programmeurs sont vraiment déterminés à étendre la classe, ils changeront simplement le constructeur en protégé en source ou en bytecode. Vous n'aurez rien accompli d'autre que pour rendre leur vie un peu plus difficile. Incluez un avertissement dans les commentaires de votre constructeur et restez-en là.

S'il s'agit d'une classe utilitaire, la solution la plus simple, la plus correcte et la plus élégante est de marquer la classe entière comme "statique final" pour empêcher l'extension. Cela ne sert à rien de simplement marquer le constructeur comme privé; un utilisateur vraiment déterminé peut toujours utiliser la réflexion pour obtenir le constructeur.

Utilisations valides:

  • Une bonne utilisation d'un protected constructeur est de forcer l'utilisation de méthodes de fabrique statiques, qui vous permettent de limiter l'instanciation ou de regrouper et de réutiliser des ressources coûteuses (connexions DB, ressources natives).
  • Singletons (généralement pas une bonne pratique, mais parfois nécessaire)
BobMcGee
la source
2
D'après mon expérience, il n'y a pas de vérités absolues, même goto pourrait être utilisé si la situation l'exige. Il existe des moyens mauvais et mauvais, mais comme le dit Marshall Cline, vous devez parfois choisir entre le moindre des maux. parashift.com/c++-faq-lite/big-picture.html#faq-6.15 En ce qui concerne les constructeurs privés, ils sont nécessaires et même pas mauvais pour vous. Cela signifie simplement que personne, y compris vos sous-classes, ne doit l'utiliser.
daramarak le
Les Gotos ont leur place, c'est vrai, mais marquer un constructeur privé ne vous rapporte rien d'autre que des ennuis sur la route. J'ai édité mon message pour expliquer plus en détail pourquoi.
BobMcGee
Cela n'a pas de sens de copier la construction ou la construction par défaut de certaines classes. En C ++, vous l'indiquez en déclarant un ctor privé sans le définir (ce qui est également courant pour operator =).
Les compilateurs d'optimisation comme Java JIT et GWT utilisent en fait des constructeurs privés: pour se limiter à la portée de l'instanciation et faire un meilleur travail en insérant / élaguant votre code.
Ajax
1

Le constructeur est privé dans un certain but, comme lorsque vous devez implémenter un singleton ou limiter le nombre d'objets d'une classe. Par exemple, dans l'implémentation singleton, nous devons rendre le constructeur privé

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i==0)
        {

            instance =new singletonClass;
            i=1;

        }

        return instance;

    }
    void test()
    {

        cout<<"successfully created instance";
    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//////return instance!!!
    temp->test();
}

Encore une fois, si vous souhaitez limiter la création d'objets jusqu'à 10, utilisez ce qui suit

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i<10)
        {

            instance =new singletonClass;
            i++;
            cout<<"created";

        }

        return instance;

    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//return an instance
    singletonClass *temp1=singletonClass::createInstance();///return another instance

}

Merci

Tunvir Rahman Tusher
la source
1

Vous pouvez avoir plus d'un constructeur. C ++ fournit un constructeur par défaut et un constructeur de copie par défaut si vous n'en fournissez pas un explicitement. Supposons que vous ayez une classe qui ne peut être construite qu'à l'aide d'un constructeur paramétré. Peut-être qu'il a initialisé des variables. Si un utilisateur utilise ensuite cette classe sans ce constructeur, il ne peut causer aucun problème. Une bonne règle générale: si l'implémentation par défaut n'est pas valide, rendez privé le constructeur par défaut et le constructeur de copie et ne fournissez pas d'implémentation:

class C
{
public:
    C(int x);

private:
    C();
    C(const C &);
};

Utilisez le compilateur pour empêcher les utilisateurs d'utiliser l'objet avec les constructeurs par défaut qui ne sont pas valides.

Charles
la source
2
Lorsque vous fournissez un constructeur non par défaut sans spécifier de constructeur par défaut, le constructeur par défaut n'existe pas. Pas besoin de le créer et de le rendre privé.
TT_
0

Citant Effective Java , vous pouvez avoir une classe avec un constructeur privé pour avoir une classe utilitaire qui définit des constantes (comme champs finaux statiques).

( EDIT: selon le commentaire, c'est quelque chose qui pourrait être applicable uniquement avec Java, je ne sais pas si cette construction est applicable / nécessaire dans d'autres langages OO (disons C ++))

Un exemple comme ci-dessous:

public class Constants {
    private Contants():

    public static final int ADDRESS_UNIT = 32;
    ...
}

EDIT_1 : Encore une fois, l'explication ci-dessous est applicable en Java: (et faisant référence au livre, Effective Java )

Une instanciation de classe d'utilité comme celle ci-dessous, bien que non nuisible, ne sert à rien puisqu'elle n'est pas conçue pour être instanciée.

Par exemple, disons qu'il n'y a pas de constructeur privé pour les constantes de classe. Un bloc de code comme ci-dessous est valide mais ne transmet pas mieux l'intention de l'utilisateur de la classe Constants

unit = (this.length)/new Constants().ADDRESS_UNIT;

contrairement au code comme

unit = (this.length)/Constants.ADDRESS_UNIT;

De plus, je pense qu'un constructeur privé exprime mieux l'intention du concepteur de la classe Constants (disons).

Java fournit un constructeur public sans paramètre par défaut si aucun constructeur n'est fourni, et si votre intention est d'empêcher l'instanciation, un constructeur privé est nécessaire.

On ne peut pas marquer une classe statique de niveau supérieur et même une classe finale peut être instanciée.

satin
la source
2
Mais en C ++, vous n'avez pas besoin d'une classe pour cela. Si quelque chose ne dépend pas des données à l'intérieur de l'objet, écrivez-le sous forme de fonctions libres et de variables libres. Envie d'encapsulation? Utilisez un espace de noms.
daramarak le
@daramarak: merci, je n'ai aucune expérience avec C ++. J'ai mis à jour la réponse pour indiquer que cela n'est applicable qu'en Java
sateesh
2
Si votre objet encapsule simplement des variables statiques, pourquoi limiter l'instanciation? Pourquoi spécifier quelque chose sur le constructeur? Il ne fera aucun mal s'il est instancié. On dirait que vous pourriez obtenir des résultats similaires en déclarant la classe statique et finale.
BobMcGee
@BobMcGee, merci pour votre commentaire. Cela m'a fait réfléchir (et me référer) davantage à ce que j'ai publié. J'ai édité ma réponse pour ajouter un raisonnement supplémentaire sur l'utilité du constructeur privé.
sateesh le
0

Les classes d'utilité peuvent avoir des constructeurs privés. Les utilisateurs des classes ne doivent pas pouvoir instancier ces classes:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}
gpampara
la source
1
Pourquoi est-ce important que la classe utilitaire soit instanciée? Tout ce qu'il fait, c'est créer un objet sans champ et consommer quelques octets de mémoire.
BobMcGee du
Être capable de l'instancier crée une API ambiguë. Si vous concevez une classe comme une classe utilitaire sans état et que vous souhaitez la conserver ainsi. Vous pouvez sous-classer une classe avec un constructeur public. Une sous-classe de certaines méthodes utilitaires est idiote.
gpampara le
@BobMcGee: évidemment, concevoir une classe qui fonctionne et concevoir une classe à utiliser par d'autres personnes sont des choses différentes. Ceux qui travaillent dans le développement d'API (comme les gens de Sun ou les gars des collections de Google) tueront quiconque essaiera de dire que le constructeur privé dans une classe utilitaire est inutile.
nanda le
@gpampara: déclarer votre classe finale empêche les gens de sous-classer. C'est ce que Sun a fait pour la classe String. @Nanda: Une classe utilitaire n'a pas besoin d' un constructeur défini. Si vous ne voulez pas qu'il soit extensible, déclarez qu'il ne l'est pas en utilisant "final".
BobMcGee
1
@BobMcGee: Vous semblez aimer voir l'exemple de Sun. Vérifiez ensuite cette classe d'utilitaire Collections de Sun: docjar.com/html/api/java/util/Collections.java.html . Il utilise en effet un constructeur privé.
nanda
0

Vous souhaiterez peut-être empêcher une classe d'être instanciée librement. Voir le modèle de conception singleton à titre d'exemple. Afin de garantir l'unicité, vous ne pouvez laisser personne en créer une instance :-)

Seb
la source
0

L'une des utilisations importantes est en classe SingleTon

class Person
{
   private Person()
   {
      //Its private, Hense cannot be Instantiated
   }

   public static Person GetInstance()
   {
       //return new instance of Person
       // In here I will be able to access private constructor
   }
};

C'est également approprié, si votre classe n'a que des méthodes statiques. c'est-à-dire que personne n'a besoin d'instancier votre classe

SysAdmin
la source
Il convient de noter que le singleton est considéré par beaucoup comme un "anti-pattern" et la décision de l'appliquer ne doit pas être prise à la légère. Une alternative courante est l'injection de dépendances.
Michael Aaron Safyan
SingleTon n'est pas un anti-pattern. cependant, la surutilisation du modèle (en raison du manque de connaissance de son utilisation) n'est pas bonne. Il convient de noter que c'est l'un des modèles GOF.
SysAdmin
0

C'est vraiment une raison évidente: vous voulez construire un objet, mais ce n'est pas pratique de le faire (en terme d'interface) dans le constructeur.

L' Factoryexemple est assez évident, permettez-moi de démontrer l' Named Constructoridiome.

Disons que j'ai une classe Complexqui peut représenter un nombre complexe.

class Complex { public: Complex(double,double); .... };

La question est: le constructeur attend-il les parties réelles et imaginaires, ou attend-il la norme et l'angle (coordonnées polaires)?

Je peux changer l'interface pour le rendre plus facile:

class Complex
{
public:
  static Complex Regular(double, double = 0.0f);
  static Complex Polar(double, double = 0.0f);
private:
  Complex(double, double);
}; // class Complex

C'est ce qu'on appelle l' Named Constructoridiome: la classe ne peut être construite à partir de rien en indiquant explicitement quel constructeur nous souhaitons utiliser.

C'est un cas particulier de nombreuses méthodes de construction. Les modèles de conception offrent un bon nombre de façons de construire l' objet: Builder, Factory, Abstract Factory, ... et un constructeur privé veillera à ce que l'utilisateur est bien limitée.

Matthieu M.
la source
0

En plus des utilisations les plus connues…

Pour implémenter le modèle d' objet de méthode , que je résumerais comme suit:

"Constructeur privé, méthode statique publique"
"Objet pour l'implémentation, fonction pour l'interface"

Si vous souhaitez implémenter une fonction à l'aide d'un objet, et que l'objet n'est pas utile en dehors d'un calcul ponctuel (par un appel de méthode), alors vous avez un objet Throwaway . Vous pouvez encapsuler la création d'objet et l'appel de méthode dans une méthode statique, évitant ainsi cet anti-pattern commun:

z = new A(x,y).call();

… En le remplaçant par un appel de fonction (avec un espace de nom):

z = A.f(x,y);

L'appelant n'a jamais besoin de savoir ou de se soucier que vous utilisez un objet en interne, ce qui donne une interface plus propre et empêche les déchets de l'objet qui traînent ou une utilisation incorrecte de l'objet.

Par exemple, si vous souhaitez fractionner un calcul entre plusieurs méthodes foo, baret zork, par exemple, partager un état sans avoir à passer de nombreuses valeurs dans et hors de fonctions, vous pouvez l'implémenter comme suit:

class A {
  public static Z f(x, y) {
    A a = new A(x, y);
    a.foo();
    a.bar();
    return a.zork();
  }

  private A(X x, Y y) { /* ... */ };
}

Ce modèle d'objet de méthode est donné dans Smalltalk Best Practice Patterns , Kent Beck, pages 34–37, où il est la dernière étape d'un modèle de refactoring, se terminant:

  1. Remplacez la méthode d'origine par une qui crée une instance de la nouvelle classe, construite avec les paramètres et le récepteur de la méthode d'origine, et invoque «compute».

Cela diffère considérablement des autres exemples ici: la classe est instanciable (contrairement à une classe utilitaire), mais les instances sont privées (contrairement aux méthodes d'usine, y compris les singletons, etc.), et peuvent vivre sur la pile, car elles ne s'échappent jamais.

Ce modèle est très utile dans la POO ascendante, où les objets sont utilisés pour simplifier l'implémentation de bas niveau, mais ne sont pas nécessairement exposés à l'extérieur, et contraste avec la POO descendante qui est souvent présentée et commence par des interfaces de haut niveau.

Nils von Barth
la source
0

C'est parfois utile si vous souhaitez contrôler comment et quand (et combien) d'instances d'un objet sont créées.

Entre autres, utilisé dans les modèles:

Singleton pattern
Builder pattern
ssedano
la source
Comment un constructeur privé est-il utile dans un objet immuable? L'immuabilité consiste à empêcher les modifications d'un objet après sa création , tandis que les constructeurs privés visent à empêcher la création .
Nils von Barth
0

L'utilisation de constructeurs privés pourrait également être d'augmenter la lisibilité / maintenabilité face à la conception axée sur le domaine. À partir de «Microsoft .NET - Applications d'architecture pour l'entreprise, 2e édition»:

var request = new OrderRequest(1234);

Citation: "Il y a deux problèmes ici. Premièrement, quand on regarde le code, on peut difficilement deviner ce qui se passe. Une instance de OrderRequest est en cours de création, mais pourquoi et en utilisant quelles données? Qu'est-ce que 1234? Cela conduit au deuxième problème: vous ne respectez pas le langage omniprésent du contexte borné. Le langage dit probablement quelque chose comme ceci: un client peut émettre une demande de commande et est autorisé à spécifier un ID d'achat. Si tel est le cas, voici un meilleur moyen d'obtenir une nouvelle instance OrderRequest : "

var request = OrderRequest.CreateForCustomer(1234);

private OrderRequest() { ... }

public OrderRequest CreateForCustomer (int customerId)
{
    var request = new OrderRequest();
    ...
    return request;
}

Je ne préconise pas cela pour chaque classe, mais pour le scénario DDD ci-dessus, je pense qu'il est parfaitement logique d'empêcher la création directe d'un nouvel objet.

Morten Nørgaard
la source