¿De qué sirve hacer que el constructor sea privado en una clase?

134

¿Por qué debemos hacer que el constructor sea privado en clase? Como siempre necesitamos que el constructor sea público.

giri
fuente

Respuestas:

129

Algunas razones por las que puede necesitar un constructor privado:

  1. Solo se puede acceder al constructor desde el método estático de fábrica dentro de la propia clase. Singleton también puede pertenecer a esta categoría.
  2. Una clase de utilidad , que solo contiene métodos estáticos.
nanda
fuente
9
Ni siquiera me habría molestado en crear un constructor privado para una clase de utilidad.
Petesh
16
@Patesh: Esa es tu decisión. Otro (s) y yo preferiríamos evitar una instanciación de la clase de utilidad que omitir el constructor privado de una línea.
nanda
1
en algunos lenguajes de programación (especialmente Java) también evita la herencia
dfa
9
@dfa: para evitar la herencia, debes poner finala nivel de clase. Poner un constructor privado es prácticamente inútil por este motivo.
nanda
3
@ Will: no si usas la reflexión. La declaración de constructores privados tiene la intención de evitar la creación de instancias (salvo la reflexión), pero evitar la subclasificación es un efecto secundario, no la intención. La herramienta adecuada para esto es declarar la clase final. Esto es lo que hizo Sun para la clase String, y una porción razonable de la API que no debe extenderse.
BobMcGee
96

Al proporcionar un constructor privado, evita que se creen instancias de clase en cualquier lugar que no sea esta misma clase. Existen varios casos de uso para proporcionar dicho constructor.

A. Sus instancias de clase se crean en un staticmétodo. El staticmétodo se declara como public.

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

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

B. Tu clase es un singleton . Esto significa que no existe más de una instancia de su clase en el programa.

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

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

C. (Solo se aplica al próximo estándar C ++ 0x) Tiene varios constructores. Algunos de ellos están declarados public, otros private. Para reducir el tamaño del código, los constructores públicos 'llaman' a los constructores privados que a su vez hacen todo el trabajo. Sus publicconstructores se denominan así constructores delegadores :

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

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

D. Desea limitar la copia de objetos (por ejemplo, debido al uso de un recurso compartido):

class MyClass
{
  SharedResource * myResource;

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

E. Tu clase es una clase de utilidad . Eso significa que solo contiene staticmiembros. En este caso, nunca se debe crear una instancia de objeto en el programa.

Kerido
fuente
3
En la mayoría de los casos, querrá evitar la copia de objetos. Por lo tanto, no proporcionaría una implementación para el constructor de copia privada.
frast
de la guía de estilo de google c ++ (otro ejemplo no está en la lista pero es común) -> si necesita trabajar en el constructor "considere una función de fábrica [y haga que el constructor sea privado]" (el texto entre corchetes lo escribo yo )
Trevor Boyd Smith
12

Dejar una "puerta trasera" que permita que otra clase / función de amigo construya un objeto de una manera prohibida para el usuario. Un ejemplo que viene a la mente sería un contenedor que construye un iterador (C ++):

Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
//     and Container is a friend of Iterator.
Emile Cormier
fuente
¿Es esto lo suficientemente diferente, Terry? ;)
Emile Cormier
Buena respuesta. ¿Por qué no mencionar la reflexión, ya que esta es una vía para lograr algo que el usuario no puede?
BobMcGee
@Bob: Tendrás que aclararme sobre eso, ya que soy principalmente un chico de C ++ y la reflexión no está realmente en el vocabulario de C ++. ;)
Emile Cormier
3
Nadie va a leer esto, pero aquí va: me han rechazado un par de veces en este caso. No molesto ni nada, pero (por el bien de aprender) me gustaría saber por qué esto es malo? ¿De qué otra forma podría construirse un iterador con los datos que necesita para acceder a los datos de un contenedor?
Emile Cormier el
2
@EmileCormier, creo que te rechazaron injustamente porque a las personas que aprenden C ++ se les dice una y otra vez: "Evita las declaraciones de amigos". Este consejo parece estar dirigido a programadores inexpertos de C ++ que de otro modo podrían usar frienddonde no está garantizado, y hay muchos casos en los que es una mala idea. Lamentablemente, el mensaje ha sido muy bien recibido, y muchos desarrolladores nunca aprenden el idioma lo suficientemente bien como para comprender que el uso ocasional friendno solo es aceptable, sino preferible . Su ejemplo fue solo un caso así. El acoplamiento fuerte deliberado no es delito, es una decisión de diseño.
evadeflow
10

Todos están atrapados en lo de Singleton, wow.

Otras cosas:

  • Evita que la gente cree tu clase en la pila; hacer constructores privados y solo devolver punteros a través de un método de fábrica.
  • Evitar la creación de copias de la clase (constructor de copia privada)
Terry Mahaffey
fuente
8

Esto puede ser muy útil para un constructor que contiene código común; los constructores privados pueden ser llamados por otros constructores, usando 'this (...);' notación. Al hacer el código de inicialización común en un constructor privado (o protegido), también está dejando explícitamente claro que solo se llama durante la construcción, lo que no es así si fuera simplemente un método:

public class Point {
   public Point() {
     this(0,0); // call common constructor
   }
   private Point(int x,int y) {
     m_x = x; m_y = y;
   }
};
Será
fuente
3

Hay algunos casos en los que es posible que no desee utilizar un constructor público; por ejemplo si quieres una clase singleton.

Si está escribiendo un ensamblado utilizado por terceros, podría haber una serie de clases internas que solo desea que su ensamblado cree y que los usuarios de su ensamblado no puedan crear instancias.

Kane
fuente
3

Esto garantiza que usted (la clase con constructor privado) controle cómo se llama al contructor.

Un ejemplo: un método de fábrica estático en la clase podría devolver objetos a medida que el método de fábrica elija asignarlos (como una fábrica de singleton, por ejemplo).

Sangha Preet
fuente
@Skilldrick: un ejemplo sería que puede forzar que una clase solo se asigne en el montón.
Dirk
@dirk He eliminado mi comentario porque ya no es relevante.
Skilldrick
3

También podemos tener un constructor privado, para prevenir la creación del objeto solo por una clase específica (por razones de seguridad).

Una forma de hacerlo es tener una clase de amigos.

Ejemplo de 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.
     }
};

Nota: Nota: Solo ClientClass (ya que es amigo de SecureClass) puede llamar al Constructor de SecureClass.

vijayNidumolu
fuente
2

cuando no desea que los usuarios creen instancias de esta clase o creen una clase que herede esta clase, como java.lang.math, todas las funciones en este paquete son static, todas las funciones se pueden invocar sin crear una instancia math, por lo que el constructor se anuncia como estático .

shinxg
fuente
1

Si es privado, no puede llamarlo ==> no puede crear una instancia de la clase. Útil en algunos casos, como un singleton.

Hay una discusión y algunos ejemplos más aquí .

Seth
fuente
1

Vi una pregunta tuya que abordaba el mismo problema.

Simplemente, si no desea permitir que los demás creen instancias, mantenga el constructor dentro de un alcance limitado. La aplicación práctica (un ejemplo) es el patrón singleton.

Chathuranga Chandrasekara
fuente
1

No deberías hacer que el constructor sea privado. Período. Hazlo protegido, para que puedas extender la clase si lo necesitas.

Editar: Estoy de acuerdo con eso, no importa cuántos votos negativos arrojes a esto. Estás cortando el potencial para el desarrollo futuro del código. Si otros usuarios o programadores están realmente decididos a extender la clase, simplemente cambiarán el constructor a protegido en código fuente o bytecode. No habrá logrado nada más que hacer su vida un poco más difícil. Incluye una advertencia en los comentarios de tu constructor y déjalo así.

Si se trata de una clase de utilidad, la solución más simple, más correcta y más elegante es marcar toda la clase como "estática final" para evitar la extensión. No sirve de nada simplemente marcar el constructor privado; un usuario realmente determinado siempre puede usar la reflexión para obtener el constructor.

Usos válidos:

  • Un buen uso de un protected constructor es forzar el uso de métodos de fábrica estáticos, que le permiten limitar la creación de instancias o agrupar y reutilizar recursos caros (conexiones de base de datos, recursos nativos).
  • Singletons (generalmente no es una buena práctica, pero a veces es necesario)
BobMcGee
fuente
2
En mi experiencia no hay verdades absolutas, incluso goto podría usarse si la situación lo exige. Hay formas malas y malas, pero como dice Marshall Cline, a veces debes elegir entre el menor de los males. parashift.com/c++-faq-lite/big-picture.html#faq-6.15 En cuanto a los constructores privados, son necesarios y ni siquiera malos para usted. Simplemente significa que nadie, incluidas sus subclases, debería usarlo.
daramarak
Los gotos tienen su lugar, es cierto, pero marcar a un constructor como privado no te da más que problemas en el camino. He editado mi publicación para explicar más completamente por qué.
BobMcGee
No tiene sentido copiar construcción o construcción predeterminada de algunas clases. En C ++ usted indica esto declarando un ctor privado sin definirlo (que también es común para operator =).
Los compiladores de optimización como Java JIT y GWT en realidad hacen uso de constructores privados: para limitar el alcance de la creación de instancias y hacer un mejor trabajo al incluir / podar su código.
Ajax
1

Constructor es privado para algún propósito, como cuando necesita implementar singleton o limitar el número de objetos de una clase. Por ejemplo, en la implementación de singleton tenemos que hacer que el constructor sea privado

#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();
}

Nuevamente, si desea limitar la creación de objetos hasta 10, use lo siguiente

#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

}

Gracias

Tunvir Rahman Tusher
fuente
1

Puedes tener más de un constructor. C ++ proporciona un constructor predeterminado y un constructor de copia predeterminado si no proporciona uno explícitamente. Supongamos que tiene una clase que solo se puede construir utilizando algún constructor parametrizado. Quizás inicializó variables. Si un usuario usa esta clase sin ese constructor, puede causar un sinfín de problemas. Una buena regla general: si la implementación predeterminada no es válida, haga que tanto el constructor predeterminado como el de copia sean privados y no proporcione una implementación:

class C
{
public:
    C(int x);

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

Use el compilador para evitar que los usuarios usen el objeto con los constructores predeterminados que no son válidos.

Charles
fuente
2
Cuando proporciona un constructor no predeterminado sin especificar un constructor predeterminado, el constructor predeterminado no existe. No es necesario crearlo y hacerlo privado.
TT_
0

Citando desde Java efectivo , puede tener una clase con un constructor privado para tener una clase de utilidad que defina constantes (como campos finales estáticos).

( EDITAR: según el comentario, esto es algo que podría ser aplicable solo con Java, no sé si esta construcción es aplicable / necesaria en otros lenguajes OO (por ejemplo, C ++))

Un ejemplo como el siguiente:

public class Constants {
    private Contants():

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

EDIT_1 : Nuevamente, la explicación a continuación es aplicable en Java: (y haciendo referencia al libro, Java efectivo )

Una instanciación de la clase de utilidad como la siguiente, aunque no es dañina, no sirve para nada, ya que no están diseñados para ser instanciados.

Por ejemplo, supongamos que no hay un constructor privado para las constantes de clase. Un fragmento de código como el siguiente es válido pero no transmite mejor la intención del usuario de la clase Constantes

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

en contraste con el código como

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

También creo que un constructor privado transmite mejor la intención del diseñador de la clase Constantes (digamos).

Java proporciona un constructor público sin parámetros predeterminado si no se proporciona ningún constructor, y si su intención es evitar la creación de instancias, se necesita un constructor privado.

No se puede marcar una clase de nivel superior estática e incluso se puede instanciar una clase final.

sateesh
fuente
2
Pero en C ++ no necesitas una clase para esto. Si algo no depende de los datos dentro del objeto, escríbalo como funciones libres y variables libres. ¿Quieres encapsulación? Usa un espacio de nombres.
daramarak
@daramarak: gracias, no tengo experiencia con C ++. He actualizado la respuesta para reflejar que esto es aplicable solamente en Java
Sateesh
2
Si su objeto solo encapsula variables estáticas, ¿por qué limitar la creación de instancias? ¿Por qué especificar algo sobre el constructor? No hará ningún daño si se instancia. Parece que podría obtener resultados similares declarando la clase estática y final.
BobMcGee
@BobMcGee, gracias por tu comentario. Esto me hizo pensar (y referirme) más sobre lo que he publicado. Edité mi respuesta para agregar más razonamientos sobre la utilidad del constructor privado.
sateesh
0

Las clases de utilidad podrían tener constructores privados. Los usuarios de las clases no deberían poder crear instancias de estas clases:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}
gpampara
fuente
1
¿Por qué importa si se instancia la clase de utilidad? Todo lo que hace es crear un objeto sin campos y comer algunos bytes de memoria.
BobMcGee
Ser capaz de crear una instancia crea una API ambigua. Si diseña una clase como una clase de utilidad sin estado y desea mantenerla así. Podría subclasificar una clase con un constructor público. Una subclase de algunos métodos de utilidad es idiota.
gpampara
@BobMcGee: obviamente, diseñar una clase que funcione y diseñar una clase para que otras personas la usen son cosas diferentes. Aquellos que trabajan en el desarrollo de API (como Sun people o Google collections guy) matarán a cualquiera que trate de decir que el constructor privado en una clase de utilidad es inútil.
nanda
@gpampara: Declarar el final de tu clase evita que las personas subclasifiquen. Esto es lo que hizo Sun para la clase String. @Nanda: una clase de utilidad no necesita un constructor definido. Si no desea que sea extensible, declare que no lo es utilizando "final".
BobMcGee
1
@BobMcGee: Parece que te encanta ver el ejemplo de Sun. Luego revise esta clase de utilidad Colecciones de Sun: docjar.com/html/api/java/util/Collections.java.html . De hecho, está usando un constructor privado.
nanda
0

Es posible que desee evitar que una clase sea instanciada libremente. Vea el patrón de diseño singleton como ejemplo. Para garantizar la singularidad, no puede dejar que nadie cree una instancia de ella :-)

Seb
fuente
0

Uno de los usos importantes es en la clase 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
   }
};

También es adecuado, si su clase solo tiene métodos estáticos. es decir, nadie necesita crear una instancia de tu clase

SysAdmin
fuente
Cabe señalar que el singleton es considerado por muchos como un "antipatrón" y la decisión de aplicarlo no debe tomarse a la ligera. Una alternativa común es la inyección de dependencia.
Michael Aaron Safyan
SingleTon no es un antipatrón. sin embargo, el uso excesivo del patrón (debido a la falta de conocimiento de su uso) no es bueno. Cabe señalar que es uno de los patrones GOF.
SysAdmin
0

Realmente es una razón obvia: desea construir un objeto, pero no es práctico hacerlo (en términos de interfaz) dentro del constructor.

El Factoryejemplo es bastante obvio, déjame demostrar el Named Constructoridioma.

Digamos que tengo una clase Complexque puede representar un número complejo.

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

La pregunta es: ¿el constructor espera las partes reales e imaginarias, o espera la norma y el ángulo (coordenadas polares)?

Puedo cambiar la interfaz para que sea más fácil:

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

Esto se llama Named Constructormodismo: la clase solo se puede construir desde cero al indicar explícitamente qué constructor queremos usar.

Es un caso especial de muchos métodos de construcción. Los patrones de diseño son un buen número de formas de acumulación de objetos: Builder, Factory, Abstract Factory, ... y un constructor privado se asegurará de que el usuario se ve limitada correctamente.

Matthieu M.
fuente
0

Además de los usos más conocidos ...

Para implementar el patrón Object Object , que resumiría como:

"Constructor privado, método estático público"
"Objeto para implementación, función para interfaz"

Si desea implementar una función usando un objeto, y el objeto no es útil fuera de hacer un cálculo único (mediante una llamada al método), entonces tiene un Objeto desechable . Puede encapsular la creación de objetos y la llamada al método en un método estático, evitando este antipatrón común:

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

... reemplazándolo con una llamada de función (espacio de nombres):

z = A.f(x,y);

La persona que llama nunca necesita saber o preocuparse de que esté utilizando un objeto internamente, produciendo una interfaz más limpia y evitando que la basura del objeto cuelgue o el uso incorrecto del objeto.

Por ejemplo, si desea dividir un cálculo entre métodos foo , bary zork, por ejemplo, para la participación del estado sin tener que pasar muchos valores dentro y fuera de funciones, se podría implementar de la siguiente manera:

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) { /* ... */ };
}

Este patrón de objeto de método se da en Smalltalk Best Practice Patterns , Kent Beck, páginas 34–37, donde es el último paso de un patrón de refactorización, que termina:

  1. Reemplace el método original con uno que cree una instancia de la nueva clase, construida con los parámetros y el receptor del método original, e invoca "calcular".

Esto difiere significativamente de los otros ejemplos aquí: la clase es instanciable (a diferencia de una clase de utilidad), pero las instancias son privadas (a diferencia de los métodos de fábrica, incluidos los singletons, etc.) y pueden vivir en la pila, ya que nunca escapan.

Este patrón es muy útil en la OOP de abajo hacia arriba, donde los objetos se usan para simplificar la implementación de bajo nivel, pero no necesariamente están expuestos externamente, y contrasta con la OOP de arriba hacia abajo que a menudo se presenta y comienza con interfaces de alto nivel.

Nils von Barth
fuente
0

A veces es útil si desea controlar cómo y cuándo se crean (y cuántas) instancias de un objeto.

Entre otros, utilizados en patrones:

Singleton pattern
Builder pattern
ssedano
fuente
¿Cómo es útil un constructor privado en un objeto inmutable? La inmutabilidad se trata de evitar cambios en un objeto después de la creación , mientras que los constructores privados se tratan de evitar la creación .
Nils von Barth
0

El uso de constructores privados también podría ser aumentar la legibilidad / mantenibilidad frente al diseño dirigido por el dominio. De "Microsoft .NET - Aplicaciones de arquitectura para la empresa, 2ª edición":

var request = new OrderRequest(1234);

Cita: "Hay dos problemas aquí. Primero, cuando se mira el código, uno apenas puede adivinar lo que está sucediendo. Se está creando una instancia de OrderRequest, pero ¿por qué y con qué datos? ¿Qué es 1234? Esto lleva al segundo problema: está violando el lenguaje ubicuo del contexto acotado. El lenguaje probablemente dice algo como esto: un cliente puede emitir una solicitud de pedido y se le permite especificar un ID de compra. Si ese es el caso, esta es una mejor manera de obtener una nueva instancia de OrderRequest : "

var request = OrderRequest.CreateForCustomer(1234);

dónde

private OrderRequest() { ... }

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

No estoy abogando por esto para cada clase, pero para el escenario DDD anterior, creo que tiene mucho sentido evitar la creación directa de un nuevo objeto.

Morten Nørgaard
fuente