Qual é a utilidade de tornar o construtor privado em uma classe?


134

Por que devemos tornar o construtor privado na classe? Como sempre precisamos que o construtor seja público.

Respostas:


129

Algumas razões pelas quais você pode precisar de construtor privado:

  1. O construtor só pode ser acessado a partir do método estático de fábrica dentro da própria classe. Singleton também pode pertencer a esta categoria.
  2. Uma classe de utilitário , que contém apenas métodos estáticos.

9
Eu nem me incomodaria em criar um construtor privado para uma classe de utilidade.
Petesh

16
@ Patesh: Essa é sua decisão. Outro (s) e eu preferimos impedir uma instanciação da classe de utilitário do que deixar de fora o construtor privado de uma linha.
Nanda 14/01

1
em algumas linguagens de programação (nomeadamente Java) também impede herança
dfa

9
@ dfa: para evitar a herança, você deve colocar finalno nível da classe. Colocar o construtor privado é praticamente inútil por esse motivo.
Nanda

3
@ Will: Não se você usar reflexão. Declarar construtores como particulares visa impedir a instanciação (barramento de reflexão), mas impedir a subclassificação é um efeito colateral, não a intenção. A ferramenta apropriada para isso é declarar a classe final. Foi isso que a Sun fez pela classe String e uma parte razoável da API que não deve ser estendida.
precisa saber é o seguinte

96

Ao fornecer um construtor privado, você evita que instâncias de classe sejam criadas em qualquer lugar que não seja essa mesma classe. Existem vários casos de uso para fornecer esse construtor.

A. Suas instâncias de classe são criadas em um staticmétodo. O staticmétodo é então declarado como public.

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

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

B. Sua turma é única . Isso significa que não há mais de uma instância da sua classe no programa.

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

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

C. (Aplica-se apenas ao próximo padrão C ++ 0x) Você tem vários construtores. Alguns deles são declarados public, outros private. Para reduzir o tamanho do código, os construtores públicos 'chamam' construtores privados que, por sua vez, fazem todo o trabalho. Seus publicconstrutores são chamados de delegadores de construção:

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

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

D. Você deseja limitar a cópia do objeto (por exemplo, devido ao uso de um recurso compartilhado):

class MyClass
{
  SharedResource * myResource;

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

E. Sua classe é uma classe de utilidade . Isso significa que ele contém apenas staticmembros. Nesse caso, nenhuma instância do objeto deve ser criada no programa.


3
Na maioria dos casos, você deseja impedir a cópia de objetos. Portanto, você não forneceria uma implementação para o construtor de cópia privada.
frast

do guia de estilo do Google c ++ (outro exemplo que não está na lista, mas é comum) -> se você precisar trabalhar no construtor "considere uma função de fábrica [e torne o construtor privado]" (o texto entre colchetes é escrito por mim )
Trevor Boyd Smith

12

Deixar uma "porta dos fundos" que permita que outra classe / função de amigo construa um objeto de uma maneira proibida pelo usuário. Um exemplo que vem à mente seria um contêiner que constrói um iterador (C ++):

Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
//     and Container is a friend of Iterator.

Isso é diferente o suficiente, Terry? ;)
Emile Cormier

Boa resposta. Por que não mencionar a reflexão, pois esse é um caminho para realizar algo que o usuário não pode?
precisa saber é o seguinte

@ Bob: Você terá que me esclarecer sobre isso, como eu sou principalmente um cara de C ++ e a reflexão não está realmente no vocabulário de C ++. ;)
Emile Cormier

3
Ninguém vai ler isso, mas aqui vai: eu já fui derrotado algumas vezes neste. Não chateado nem nada, mas (por uma questão de aprendizado) eu gostaria de saber por que isso é ruim? De que outra forma um iterador poderia ser construído com os dados necessários para acessar os dados de um contêiner?
Emile Cormier

2
@EmileCormier, acho que você foi injustamente rejeitado porque as pessoas que aprendem C ++ são repetidas vezes: "Evite declarações de amigos". Esse conselho parece ser direcionado a programadores C ++ inexperientes que, de outra forma, poderiam usá- friendlo onde não se justifica - e há muitos casos em que é uma má ideia. Infelizmente, a mensagem foi muito bem recebida e muitos desenvolvedores nunca aprendem a linguagem o suficiente para entender que o uso ocasional de friendnão é apenas aceitável, mas preferível . Seu exemplo foi exatamente esse caso. Um acoplamento forte e deliberado não é crime, é uma decisão de design.
Evadeflow 17/11/16

10

Todo mundo está preso na coisa Singleton, uau.

Outras coisas:

  • Impeça as pessoas de criar sua classe na pilha; faça construtores privados e apenas devolva ponteiros através de um método de fábrica.
  • Impedindo a criação de cópias da classe (construtor de cópia privada)

8

Isso pode ser muito útil para um construtor que contém código comum; construtores privados podem ser chamados por outros construtores, usando o 'this (...);' notação. Ao tornar o código de inicialização comum em um construtor privado (ou protegido), você também deixa explicitamente claro que ele é chamado apenas durante a construção, o que não acontece se fosse simplesmente um 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;
   }
};

3

Existem alguns casos em que você pode não querer usar um construtor público; por exemplo, se você deseja uma classe singleton.

Se você estiver escrevendo uma montagem usada por terceiros, pode haver várias classes internas que você deseja criar apenas pela sua montagem e não serem instanciadas pelos usuários da sua montagem.


3

Isso garante que você (a classe com construtor privado) controle como o contratante é chamado.

Um exemplo: um método de fábrica estático na classe pode retornar objetos à medida que o método de fábrica escolhe alocá-los (como uma fábrica de singleton, por exemplo).


@ Skilldrick: um exemplo seria que você pode forçar uma classe a ser apenas alocada para heap.
Dirk

@dirk Excluí meu comentário, pois ele não é mais relevante.
Skilldrick

3

Também podemos ter construtor privado, para garantir a criação do objeto apenas por uma classe específica (por razões de segurança).

Uma maneira de fazer isso é tendo uma aula de amigos.

Exemplo 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: Somente o ClientClass (por ser amigo do SecureClass) pode chamar o SecureClass's Constructor.



2

quando você não deseja que os usuários criem instâncias dessa classe ou criem uma classe que herda essa classe, como java.lang.mathtodas as funções deste pacote static, todas as funções podem ser chamadas sem criar uma instância de math, portanto, o construtor é anunciado como estático .


1

Se for privado, você não pode chamá-lo ==> você não pode instanciar a classe. Útil em alguns casos, como um singleton.

Há uma discussão e mais alguns exemplos aqui .


1

Vi uma pergunta sua abordando o mesmo problema.

Simplesmente, se você não deseja permitir que os outros criem instâncias, mantenha o construtor dentro de um escopo limitado. A aplicação prática (um exemplo) é o padrão singleton.


1

Você não deve tornar o construtor privado. Período. Torne-o protegido, para que você possa estender a classe, se necessário.

Edit: Eu estou de pé por isso, não importa quantos votos negativos você jogue com isso. Você está cortando o potencial de desenvolvimento futuro do código. Se outros usuários ou programadores estiverem realmente determinados a estender a classe, eles mudarão o construtor para protegido na origem ou no bytecode. Você não terá conseguido nada além de dificultar a vida deles. Inclua um aviso nos comentários do construtor e deixe por isso mesmo.

Se for uma classe de utilidade, a solução mais simples, correta e elegante é marcar toda a classe como "estática final" para impedir a extensão. Não adianta apenas marcar o construtor como privado; um usuário realmente determinado sempre pode usar a reflexão para obter o construtor.

Usos válidos:

  • Um bom uso de um protected construtor é forçar o uso de métodos estáticos de fábrica, que permitem limitar a instanciação ou agrupar e reutilizar recursos caros (conexões de banco de dados, recursos nativos).
  • Singletons (geralmente não é uma boa prática, mas às vezes é necessário)

2
Na minha experiência, não há verdades absolutas, mesmo o goto pode ser usado se a situação exigir. Existem maneiras ruins e más, mas como Marshall Cline coloca, às vezes você precisa escolher entre os menores males. parashift.com/c++-faq-lite/big-picture.html#faq-6.15 Quanto aos construtores particulares, eles são necessários e nem ruins para você. Significa apenas que ninguém, incluindo suas subclasses, deve usá-lo.
precisa saber é o seguinte

Os Gotos têm seu lugar, é verdade, mas marcar um construtor como privado não lhe dá nada além de problemas no caminho. Eu editei minha postagem para explicar mais detalhadamente o porquê.
precisa saber é o seguinte

Não faz sentido copiar construção ou construção padrão de algumas classes. Em C ++, você indica isso declarando um controlador privado sem defini-lo (o que também é comum para o operador =).

A otimização de compiladores como Java JIT e GWT realmente utiliza construtores privados: para limitar o escopo da instanciação e realizar um trabalho melhor ao inserir / remover seu código.
Ajax

1

O construtor é privado para algum propósito, como quando você precisa implementar singleton ou limitar o número de objetos de uma classe. Por exemplo, na implementação de singleton, temos que tornar o construtor 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();
}

Novamente, se você quiser limitar a criação de objetos em até 10, use o seguinte

#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

}

obrigado


1

Você pode ter mais de um construtor. O C ++ fornece um construtor padrão e um construtor de cópia padrão, se você não fornecer um explicitamente. Suponha que você tenha uma classe que só pode ser construída usando algum construtor parametrizado. Talvez tenha inicializado variáveis. Se um usuário usar essa classe sem esse construtor, ele poderá causar problemas sem fim. Uma boa regra geral: se a implementação padrão não for válida, torne o construtor padrão e de cópia privado e não forneça uma implementação:

class C
{
public:
    C(int x);

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

Use o compilador para impedir que os usuários usem o objeto com os construtores padrão que não são válidos.


2
Quando você fornece um construtor não padrão sem especificar um construtor padrão, o construtor padrão não existe. Não há necessidade de criá-lo e torná-lo privado.
TT_

0

Citando em Java Efetivo , você pode ter uma classe com construtor privado para ter uma classe de utilitário que define constantes (como campos finais estáticos).

( EDIT: Conforme o comentário, isso é algo que pode ser aplicável apenas ao Java, não sei se essa construção é aplicável / necessária em outras linguagens OO (por exemplo, C ++))

Um exemplo como abaixo:

public class Constants {
    private Contants():

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

EDIT_1 : Novamente, a explicação abaixo é aplicável em Java: (e referindo-se ao livro, Java efetivo )

Uma instanciação de classe de utilidade como a abaixo, embora não seja prejudicial, não serve a nenhum propósito, pois não foi projetada para ser instanciada.

Por exemplo, digamos que não haja Construtor privado para as constantes da classe. Um pedaço de código como abaixo é válido, mas não transmite melhor a intenção do usuário da classe Constants

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

em contraste com código como

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

Também acho que um construtor privado transmite melhor a intenção do projetista da classe Constants (digamos).

Java fornece um construtor público sem parâmetros padrão, se nenhum construtor for fornecido, e se sua intenção for impedir a instanciação, será necessário um construtor privado.

Não se pode marcar uma classe de nível superior como estática e até uma classe final pode ser instanciada.


2
Mas em C ++ você não precisa de uma classe para isso. Se algo não depender de dados dentro do objeto, escreva-o como funções livres e variáveis ​​livres. Quer encapsulamento? Use um espaço para nome.
Daramarak

@aramarak: obrigado, não tenho experiência com C ++. Eu atualizei a resposta para refletir que este só é aplicável em Java
Sateesh

2
Se o seu objeto está apenas encapsulando variáveis ​​estáticas, por que limitar a instanciação? Por que especificar algo sobre o construtor? Não fará nenhum mal se for instanciado. Parece que você pode obter resultados semelhantes declarando a classe estática e final.
precisa saber é o seguinte

@BobMcGee, obrigado pelo seu comentário. Isso me fez pensar (e me referir) mais sobre o que eu postei. Eu editei minha resposta para adicionar um pouco mais de raciocínio sobre a utilidade do construtor privado.
Sateesh

0

As classes de utilitário podem ter construtores particulares. Os usuários das classes não devem poder instanciar essas classes:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}

1
Por que importa se a classe de utilitário é instanciada? Tudo o que faz é criar um objeto sem campos e consumir alguns bytes de memória.
BobMcGee

Ser capaz de instanciar cria uma API ambígua. Se você projetar uma classe como uma classe de utilitário sem estado e desejar mantê-la dessa maneira. Você pode subclassificar uma classe com um construtor público. Uma subclasse de alguns métodos utilitários é idiota.
gpampara

@BobMcGee: obviamente, projetar uma classe que funcione e projetar uma classe para ser usada por outras pessoas são coisas diferentes. Quem trabalha no desenvolvimento de API (como o pessoal da Sun ou o cara das coleções do Google) matará qualquer um que tentar dizer que o construtor privado em uma classe de utilitário é inútil.
nanda

@gpampara: declarar a sua aula final evita a subclasse de pessoas. Foi isso que a Sun fez pela classe String. @ Nanda: Uma classe de utilitário não precisa de um construtor definido. Se você não deseja que seja extensível, declare que não é usando "final".
precisa saber é o seguinte

1
@BobMcGee: Você parece gostar de ver o exemplo da Sun. Em seguida, verifique esta classe de utilitário Collections da Sun: docjar.com/html/api/java/util/Collections.java.html . É realmente usando construtor privado.
Nanda

0

Você pode impedir que uma classe seja instanciada livremente. Veja o padrão de design singleton como um exemplo. Para garantir a exclusividade, você não pode deixar ninguém criar uma instância dela :-)


0

Um dos usos importantes está na 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
   }
};

Também é adequado, se sua classe tiver apenas métodos estáticos. ou seja, ninguém precisa instanciar sua classe


Note-se que o singleton é considerado por muitos um "antipadrão" e a decisão de aplicá-lo não deve ser tomada de ânimo leve. Uma alternativa comum é a injeção de dependência.
Michael Aaron Safyan

O SingleTon não é um anti-padrão. no entanto, o uso excessivo do padrão (devido à falta de conhecimento de seu uso) não é bom. Note-se que é um dos padrões GOF.
SysAdmin

0

É realmente uma razão óbvia: você deseja construir um objeto, mas não é prático fazê-lo (em termos de interface) dentro do construtor.

O Factoryexemplo é bastante óbvio, deixe-me demonstrar o Named Constructoridioma.

Digamos que eu tenha uma classe Complexque possa representar um número complexo.

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

A questão é: o construtor espera as partes reais e imaginárias ou a norma e o ângulo (coordenadas polares)?

Eu posso mudar a interface para facilitar:

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

Isso é chamado de Named Constructoridioma: a classe só pode ser construída do zero, declarando explicitamente qual construtor queremos usar.

É um caso especial de muitos métodos de construção. Os padrões de projeto fornecem um bom número de maneiras de objeto de construção: Builder, Factory, Abstract Factory, ... e um construtor privado irá garantir que o usuário está devidamente restrito.


0

Além dos usos mais conhecidos…

Para implementar o padrão Object Object , que resumiria como:

"Construtor privado, método estático público"
"Objeto para implementação, função para interface"

Se você deseja implementar uma função usando um objeto, e o objeto não for útil fora de fazer um cálculo único (por uma chamada de método), você terá um Objeto Throwaway . Você pode encapsular a criação do objeto e a chamada do método em um método estático, impedindo esse antipadrão comum:

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

… Substituindo-o por uma chamada de função (com espaço para nome):

z = A.f(x,y);

O chamador nunca precisa saber ou se importar com o uso interno de um objeto, produzindo uma interface mais limpa e evitando o lixo do objeto pendurado ou o uso incorreto do objeto.

Por exemplo, se você quer terminar uma computação através de métodos foo, bare zork, por exemplo para estado share sem ter que passar muitos valores dentro e fora de funções, você pode implementá-lo da seguinte forma:

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 padrão de Objeto de método é fornecido em Smalltalk Best Practice Patterns , Kent Beck, páginas 34–37, onde é a última etapa de um padrão de refatoração, terminando:

  1. Substitua o método original por um que crie uma instância da nova classe, construída com os parâmetros e o receptor do método original, e chame "computar".

Isso difere significativamente dos outros exemplos aqui: a classe é instável (ao contrário de uma classe de utilitário), mas as instâncias são privadas (ao contrário dos métodos de fábrica, incluindo singletons etc.) e podem viver na pilha, pois nunca escapam.

Esse padrão é muito útil no POO de baixo para cima, onde objetos são usados ​​para simplificar a implementação de baixo nível, mas não necessariamente são expostos externamente, e contrasta com o OOP de cima para baixo que geralmente é apresentado e começa com interfaces de alto nível.


0

Às vezes, é útil se você deseja controlar como e quando (e quantas) as instâncias de um objeto são criadas.

Entre outros, usados ​​em padrões:

Singleton pattern
Builder pattern

Como um construtor privado é útil em um objeto imutável? Imutabilidade é impedir alterações em um objeto após a criação , enquanto construtores privados tentam impedir a criação .
Nils von Barth

0

O uso de construtores privados também pode ser aumentar a legibilidade / manutenção em face do design controlado por domínio. Em "Microsoft .NET - Aplicativos de arquitetura para empresas, 2ª edição":

var request = new OrderRequest(1234);

Cite: "Existem dois problemas aqui. Primeiro, ao analisar o código, dificilmente se pode adivinhar o que está acontecendo. Uma instância do OrderRequest está sendo criada, mas por que e usando quais dados? O que é 1234? Isso leva ao segundo problema: você está violando o idioma onipresente do contexto limitado.O idioma provavelmente diz algo assim: um cliente pode emitir uma solicitação de pedido e pode especificar um ID de compra.Se esse for o caso, aqui está uma maneira melhor de obter uma nova instância OrderRequest : "

var request = OrderRequest.CreateForCustomer(1234);

Onde

private OrderRequest() { ... }

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

Não estou defendendo isso para todas as classes, mas para o cenário DDD acima, acho que faz todo sentido evitar a criação direta de um novo objeto.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.