Quando você deve usar uma classe privada / interna?

21

Para esclarecer, o que estou perguntando é

public class A{
    private/*or public*/ B b;
}

vs.

public class A{
    private/*or public*/ class B{
        ....
    }
}

Definitivamente, posso pensar em algumas razões para usar uma ou outra, mas o que realmente gostaria de ver são exemplos convincentes que mostram que os prós e os contras não são apenas acadêmicos.

EpsilonVector
fonte
2
A resposta dependeria dos recursos fornecidos pelas bibliotecas de idiomas e principais. Assumindo a linguagem C #, tente executá-las pelo StyleCop. Tenho certeza de que você receberá um aviso sobre a separação dessa classe em seu próprio arquivo. Isso significa que muitas pessoas no MSFT pensam que você não precisa usar nenhum dos exemplos que você usou.
Job
Qual seria um exemplo convincente se os exemplos acadêmicos não fossem bons o suficiente?
O @Job StyleCop emitirá um aviso apenas se as classes internas forem visíveis do lado de fora. Se for privado, está completamente ok e, na verdade, tem muitos usos.
julealgon

Respostas:

20

Eles geralmente são usados ​​quando uma classe é um detalhe de implementação interna de outra classe, e não parte de sua interface externa. Eu os vi principalmente como classes somente de dados que tornam as estruturas de dados internas mais limpas em idiomas que não possuem uma sintaxe separada para estruturas no estilo c. Às vezes, também são úteis para objetos derivados de um método altamente personalizados, como manipuladores de eventos ou threads de trabalho.

Karl Bielefeldt
fonte
2
É assim que eu os uso. Se uma classe é, do ponto de vista funcional, nada além de um detalhe de implementação privada de outra classe, ela deve ser declarada dessa maneira, assim como seria um método ou campo privado. Não há razão para poluir o espaço para nome com lixo que nunca será usado em outro lugar.
precisa saber é o seguinte
9

Suponha que você esteja construindo uma árvore, uma lista, um gráfico e assim por diante. Por que você deve expor os detalhes internos de um nó ou uma célula ao mundo externo?

Qualquer pessoa que utilize um gráfico ou uma lista deve confiar apenas em sua interface, não em sua implementação, pois você poderá alterá-lo um dia no futuro (por exemplo, de uma implementação baseada em array para outra baseada em ponteiro) e nos clientes que usam seu a estrutura de dados ( cada uma delas ) precisaria modificar seu código para adequá-lo à nova implementação.

Em vez disso, encapsular a implementação de um nó ou célula em uma classe interna privada oferece a liberdade de modificar a implementação a qualquer momento, sem que os clientes sejam obrigados a ajustar o código consequentemente, desde que a interface da estrutura de dados permaneça intocado.

Ocultar os detalhes da implementação da sua estrutura de dados também traz vantagens de segurança, porque se você quiser distribuir sua classe, disponibilizará apenas o arquivo de interface junto com o arquivo de implementação compilado e ninguém saberá se você está realmente usando matrizes ou ponteiros. para sua implementação, protegendo seu aplicativo de algum tipo de exploração ou, pelo menos, de conhecimento devido à impossibilidade de usar ou inspecionar seu código. Além de questões práticas, não subestime o fato de que é uma solução extremamente elegante nesses casos.

Nicola Lamonaca
fonte
5

Na minha opinião, é um bom policial usar classes definidas internamente para classes usadas brevemente em uma classe para permitir uma tarefa específica.

Por exemplo, se você precisar vincular uma lista de dados que consiste em duas classes não relacionadas:

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

Se sua classe interna é usada por outra classe, você deve separá-la. Se sua classe interna contiver alguma lógica, você deve colocá-la em separado.

Basicamente, acho que eles são ótimos para tapar buracos em seus objetos de dados, mas são difíceis de manter se precisarem conter lógica, pois você precisará saber onde procurá-los se precisar alterá-los.

Ed James
fonte
2
Supondo C #, você não pode simplesmente usar tuplas aqui?
Job
3
@ Job Claro, mas às vezes tuple.ItemXo código não é claro.
Lasse Espeholt
2
@ Sim, lasseespeholt acertou em cheio. Prefiro ver uma classe interna de {string Title; string Descrição; } do que uma Tupla <string, string>.
Ed James
nesse ponto, não faria sentido colocar essa classe em seu próprio arquivo?
Job
Não, não se você realmente estiver usando esses dados para unir brevemente alguns dados, a fim de realizar uma tarefa que não vai além da competência da classe pai. Pelo menos, não na minha opinião!
Ed James
4
  • Classes internas em alguma linguagem (Java, por exemplo) têm um vínculo com um objeto da classe que os contém e podem usar seus membros sem qualificá-los. Quando disponível, é mais claro do que reimplementá-los usando outros recursos de idioma.

  • Classes aninhadas em outra linguagem (C ++, por exemplo) não têm esse vínculo. Você está simplesmente usando o controle de escopo e acessibilidade fornecido pela classe.

AProgrammer
fonte
3
Na verdade, o Java tem os dois .
Joachim Sauer
3

Evito classes internas em Java porque a troca a quente não funciona na presença de classes internas. Eu odeio isso porque realmente odeio comprometer o código por essas considerações, mas essas reinicializações do Tomcat se somam.

Kevin Cline
fonte
esse problema está documentado em algum lugar?
gnat
Downvoter: minha afirmação está incorreta?
Kevin cline
1
Fiz voto negativo porque sua resposta não tem detalhes, não porque está incorreta. A falta de detalhes dificulta a verificação da sua afirmação. Se você adicionar mais sobre esse assunto, eu provavelmente voltaria a votar, porque suas informações parecem bastante interessantes e podem ser úteis #
31512
1
@gnat: Eu fiz uma pequena pesquisa e, embora isso pareça uma verdade conhecida, não encontrei uma referência definitiva sobre as limitações do Java HotSwap.
Kevin cline
nós não estamos no Skeptics.SE :) apenas algumas referências fariam, não precisa ser definitivo #
3012
2

Um dos usos para a classe interna estática privada é o padrão Memo. Você coloca todos os dados que precisam ser lembrados na classe privada e os retorna de alguma função como Object. Ninguém do lado de fora não pode (sem reflexão, desserialização, inspeção de memória ...) investigá-lo / modificá-lo, mas ele pode devolvê-lo à sua classe e restaurar seu estado.

user470365
fonte
2

Um motivo para usar uma classe interna privada pode ser porque uma API que você está usando requer herança de uma classe específica, mas você não deseja exportar o conhecimento dessa classe para usuários da sua classe externa. Por exemplo:

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

Nisso, o do_my_async_op exige que um objeto do tipo retorno de chamada seja passado para ele. Esse retorno de chamada tem um público assinatura de função de membro que a API usa.

Quando do_op () é chamado de outer_class, ele usa uma instância da classe interna privada que herda da classe de retorno de chamada necessária em vez de um ponteiro para si mesmo. A classe externa tem uma referência à classe externa com o objetivo simples de desviar o retorno de chamada para o privado da classe externa função de membro do_callback .

O benefício disso é que você tem certeza de que ninguém mais pode chamar a função de membro pública "fire ()".

Kaz Dragon
fonte
2

De acordo com Jon Skeet, em C # in Depth, o uso de uma classe aninhada era a única maneira de implementar um singleton totalmente seguro para encadeamentos preguiçosos (consulte a Quinta Versão) (até o .NET 4).

Scott Whitlock
fonte
1
é o idioma Inicializador On Demand Holder , em Java também requer classe interna estática privada. É recomendado em JMM FAQ , por Brian Goetz em JCiP 16,4 e por Joshua Bloch no JavaOne 2008 Mais Effective Java (pdf) "para alto desempenho em um campo estático ..."
mosquito
1

Classes privada e interna são usadas para aumentar os níveis de encapsulamento e ocultar os detalhes da implementação.

No C ++, além de uma classe privada, o mesmo conceito pode ser alcançado implementando o espaço de nome anônimo de uma classe cpp. Isso serve para ocultar / privatizar um detalhe de implementação.

É a mesma idéia que uma classe interna ou privada, mas com um nível ainda maior de encapsulamento, pois é completamente invisível fora da unidade de compilação do arquivo. Nada disso aparece no arquivo de cabeçalho ou é visível externamente na declaração da classe.

hiwaylon
fonte
Algum feedback construtivo sobre o -1?
hiwaylon 11/09/12
1
Não diminuí o voto, mas acho que você não está realmente respondendo à pergunta: você não está dando nenhum motivo para usar a classe privada / interna. Você está apenas descrevendo como ele funciona e como fazer uma coisa semelhante em c ++
Simon Bergot
De fato. Eu pensei que era óbvio pelo meu e pelas outras respostas (ocultando detalhes de implementação, aumento dos níveis de encapsulamento, etc.), mas tentarei esclarecer algumas. Obrigado @Simon.
hiwaylon 11/09/12
1
Boa edição. E por favor, não se ofenda por causa desse tipo de reação. A rede SO está tentando aplicar políticas para melhorar a relação sinal / ruído. Algumas pessoas são muito rigorosos sobre o escopo de pergunta / resposta, e às vezes se esqueça de ser agradável (comentando sua downvote por exemplo)
Simon Bergot
@ Simon Obrigado. É decepcionante por causa da baixa qualidade de comunicação que exibe. Talvez seja um pouco fácil demais ser "rigoroso" por trás do véu anônimo da web? Oh, bem, nada de novo, eu acho. Mais uma vez obrigado pelo feedback positivo.
hiwaylon 13/09/12