Pergunta

Essa pergunta já tem resposta aqui:

Allen Holub escreveu o seguinte,

Você não pode ter um programa sem algum acoplamento.No entanto, você pode minimizar consideravelmente o acoplamento seguindo servilmente os preceitos OO (orientado a objetos) (o mais importante é que a implementação de um objeto deve ser completamente ocultada dos objetos que o utilizam).Por exemplo, as variáveis ​​de instância de um objeto (campos membros que não são constantes) devem ser sempre privadas.Período.Sem exceções.Sempre.Quero dizer.(Ocasionalmente, você pode usar métodos protegidos de maneira eficaz, mas variáveis ​​de instância protegidas são uma abominação.)

O que parece razoável, mas ele continua dizendo:

Você deve nunca use funções get/set pela mesma razão - são apenas maneiras excessivamente complicadas de tornar um campo público (embora as funções de acesso que retornam objetos completos em vez de um valor do tipo básico sejam razoáveis ​​​​em situações em que a classe do objeto retornado é uma abstração chave no design ).

O que, francamente, parece loucura para mim.

Eu entendo o princípio de ocultação de informações, mas sem acessadores e modificadores você não poderia usar Java beans.Não sei como você seguiria um design MVC sem acessadores no modelo, já que o modelo não pode será responsável por renderizar a visualização.

Porém, sou um programador mais jovem e aprendo mais sobre Design Orientado a Objetos todos os dias.Talvez alguém com mais experiência possa opinar sobre esse assunto.

Artigos de Allen Holub para referência


Perguntas relacionadas:

Foi útil?

Solução

Não tenho nenhum problema com Holub dizendo que você geralmente deve evitar alterar o estado de um objeto, mas em vez disso recorrem a métodos integrados (execução de comportamentos) para atingir esse fim.Como Corletk aponta, é sensato pensar muito sobre o mais alto nível de abstração e não apenas programar impensadamente com getters/setters que apenas permitem que você execute um encapsulamento final.

No entanto, tenho muitos problemas com quem diz que você "nunca" deve usar setters ou "nunca" acessar tipos primitivos.Na verdade, o esforço necessário para manter este nível de pureza em todos casos podem e acabarão causando mais complexidade em seu código do que usar propriedades implementadas adequadamente.Você apenas precisa ter bom senso para saber quando está contornando as regras do ganho de curto prazo em detrimento do sofrimento de longo prazo.

Holub não confia em você para saber a diferença.Acho que saber a diferença é o que faz de você um profissional.

Outras dicas

Leia esse artigo com atenção.Holub está enfatizando que getters e setters são um malvado “antipadrão padrão”, um mau hábito que adquirimos ao projetar um sistema;porque nós podemos.

O processo de pensamento deve seguir as linhas;O que esse objeto fazer?Quais são as suas responsabilidades?Quais são os seus comportamentos?O que isso sabe?Pensar muito nessas questões leva você naturalmente ao design de classes que expõem a interface do mais alto nível possível.

Um carro é um bom exemplo.Ele expõe uma interface de alto nível padronizada e bem definida.Eu não me preocupo com setSpeed(60)...isso é MPH ou km/h?Eu apenas acelero, navego, desacelero.Não preciso pensar nos detalhes setSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5)), Eu acabei de turn(-1.5), e os detalhes são cuidados sob o capô.

Tudo se resume a "Você pode e deve descobrir para que cada classe será usada, o que ela faz, o que representa e expor a interface de mais alto nível possível que atenda a esses requisitos.Getters e setters geralmente são uma desculpa, quando o programador tem preguiça de fazer a análise necessária para determinar exatamente o que cada classe é e o que não é, e assim seguimos o caminho de "ela pode fazer qualquer coisa".Getters e setters são maus!

Às vezes, os requisitos reais de uma classe não podem ser conhecidos com antecedência.Isso é legal, apenas evite e use o antipadrão getter/setter por enquanto, mas quando você souber, por experiência, para que a classe está sendo usada, provavelmente desejará voltar e limpar a interface suja de baixo nível.Refatorar com base em "coisas que você gostaria de saber quando escreve o idiota em primeiro lugar" é normal.Você não precisa saber tudo para começar, apenas quanto mais você sabe, menos retrabalho será necessário no caminho.

Essa é a mentalidade que ele está promovendo.Getters e setters são uma armadilha fácil de cair.

Sim, os beans basicamente requerem getters e setters, mas para mim um bean é um caso especial.Os feijões representam substantivos, coisas, objetos tangíveis identificáveis ​​(se não físicos).Na verdade, poucos objetos têm comportamentos automáticos;na maioria das vezes as coisas são manipuladas por forças externas, incluindo humanos, para torná-las produtivas.

daisy.setColor(Color.PINK) faz todo o sentido.O que mais você pode fazer?Talvez uma fusão mental vulcana, para fazer a flor querer ser rosa?Hmmm?

Getters e setters têm seu “mal”lugar.É que, como todas as coisas OO realmente boas, tendemos a usá-las em excesso, porque são seguras e familiares, para não mencionar simples, e, portanto, seria melhor se os novatos não vissem ou ouvissem falar delas, pelo menos até que eles ' Eu dominei a coisa da fusão mental.

Acho que o que Allen Holub tentou dizer, reformulado em esse artigo, é o seguinte.

Getters e setters podem ser úteis para variáveis ​​que você deseja encapsular especificamente, mas não é necessário usá-los para todas as variáveis.Na verdade, usá-los para todas as variáveis ​​é um cheiro desagradável de código.

O problema que os programadores têm, e Allen Holub estava certo ao apontar isso, é que eles às vezes usam getters/setters para todas as variáveis.E o propósito do encapsulamento está perdido.

(observe que estou abordando isso de um ângulo de "propriedade" do .NET)

Bem, simplesmente – não concordo com ele;ele faz um grande alarido sobre o tipo de retorno das propriedades ser uma coisa ruim porque pode quebrar seu código de chamada - mas exatamente o mesmo argumento se aplicaria aos argumentos do método.E se você também não pode usar métodos?

OK, os argumentos do método podem ser alterados conforme conversões ampliadas, mas....só por que...Além disso, observe que em C# o var palavra-chave poderia mitigar grande parte dessa dor percebida.

Os acessadores são não um detalhe de implementação;eles são a API/contrato público.Sim, se você quebrar o contrato, terá problemas.Quando isso se tornou uma surpresa?Da mesma forma, não é incomum que os acessadores não sejam triviais - ou seja,eles fazem mais do que apenas envolver campos;eles realizam cálculos, verificações lógicas, notificações, etc.E eles permitem abstrações de estado baseadas em interface.Ah, e polimorfismo - etc.

Quanto à natureza detalhada dos acessadores (p3?4?) - em C#: public int Foo {get; private set;} - tarefa concluída.

Em última análise, todo código é um meio de expressar nossa intenção ao compilador.As propriedades me permitem fazer isso de maneira segura, baseada em contrato, verificável, extensível e polimórfica - obrigado.Por que preciso "consertar" isso?

Getters e setters são usados ​​como pouco mais que uma máscara para tornar pública uma variável privada.

Não faz sentido repetir o que Holub já disse, mas o ponto crucial é que as classes devem representar comportamento e não apenas estado.

Pesquisei mais no Google sobre Allen Holub, parece que ele tem sua cota de oponentes na comunidade Java.

O último é particularmente apontado.O texto do comentarista está em itálico.

Embora getIdentity comece com “get”, não é um acessador porque não retorna apenas um campo.Ele retorna um objeto complexo que possui comportamento razoável

Ah, mas espere...então não há problema em usar acessadores, desde que você retorne objetos em vez de tipos primitivos?Agora, essa é uma história diferente, mas é igualmente idiota para mim.Às vezes você precisa de um objeto, às vezes de um tipo primitivo.

Além disso, noto que Allen suavizou radicalmente sua posição desde sua coluna anterior sobre o mesmo tema, onde o mantra “Nunca use acessadores” não sofreu uma única exceção.Talvez ele tenha percebido depois de alguns anos que os acessadores afinal servem a um propósito...

Tenha em mente que na verdade não coloquei nenhum código de UI na lógica de negócios.Escrevi a camada UI em termos de AWT (Abstract Window Toolkit) ou Swing, que são camadas de abstração.

Um bom.E se você estiver escrevendo seu aplicativo no SWT?Quão "abstrato" é realmente o AWT nesse caso?Apenas enfrente:este conselho simplesmente leva você a escrever o código da UI em sua lógica de negócios.Que grande princípio.Afinal, só se passaram pelo menos dez anos desde que identificamos essa prática como uma das piores decisões de design que você pode tomar em um projeto.

Meu problema é que um programador novato às vezes tropeça em artigos na Internet e lhes dá mais crédito do que deveria.Talvez este seja um desses casos.

Quando ideias como essas me são apresentadas, gosto de dar uma olhada nas bibliotecas e frameworks que utilizo e que gosto de usar.

Por exemplo, embora alguns discordem, gosto da API Java Standard.Eu também gosto do Spring Framework.Observando as classes nessas bibliotecas, você notará que muito raramente existem setters e getters que existem apenas para expor alguma variável interna.Existem métodos nomeado getX, mas isso não significa que seja um getter no sentido convencional.

Então, acho que ele tem razão, e é o seguinte:toda vez que você pressiona a opção "Gerar getters/setters" no Eclipse (ou no IDE de sua escolha), você deve dar um passo para trás e se perguntar o que está fazendo.É realmente apropriado expor essa representação interna ou estraguei meu design em alguma etapa?

Não acredito que ele esteja dizendo para nunca usar get/set, mas sim que usar get/set para um campo não é melhor do que apenas tornar o campo público (por exemplo,string pública Nome vs.string pública Nome {get;definir;}).

Se get/set for usado, ele limitará a ocultação de informações de OO, o que pode potencialmente prendê-lo em uma interface ruim.

No exemplo acima, Name é uma string;e se quisermos alterar o design posteriormente para adicionar vários nomes?A interface expôs apenas uma única string, portanto não podemos adicionar mais sem quebrar a implementação existente.

No entanto, se em vez de usar get/set você inicialmente tivesse um método como Add(string name), internamente você poderia processar o nome singularmente ou adicionar a uma lista ou algo assim e chamar externamente o método Add quantas vezes quiser adicionar mais nomes.

O objetivo do OO é projetar com um nível de abstração;não exponha mais detalhes do que o absolutamente necessário.

Provavelmente, se você acabou de agrupar um tipo primitivo com um get/set, você quebrou esse princípio.

Claro, isso se você acredita nos objetivos OO;Acho que a maioria não o faz, na verdade, eles apenas usam objetos como uma maneira conveniente de agrupar código funcional.

Variáveis ​​públicas fazem sentido quando a classe nada mais é do que um conjunto de dados sem nenhuma coerência real, ou quando é muito, muito elementar (como uma classe pontual).Em geral, se há alguma variável em uma classe que você acha que provavelmente não deveria ser pública, isso significa que a classe tem alguma coerência, e as variáveis ​​têm uma certa relação que deve ser mantida, então todas as variáveis ​​devem ser privadas.

Getters e setters fazem sentido quando refletem algum tipo de ideia coerente.Em uma classe de polígono, por exemplo, as coordenadas xey de determinados vértices têm um significado fora do limite da classe.Provavelmente faz sentido ter getters e setters.Em uma classe de conta bancária, o saldo provavelmente é armazenado como uma variável privada e quase certamente deve ter um getter.Se tiver um setter, ele precisará ter log integrado para preservar a auditabilidade.

Existem algumas vantagens dos getters e setters sobre as variáveis ​​públicas.Eles fornecem alguma separação entre interface e implementação.Só porque um ponto tem um .getX() função não significa que tem que haver um x, já que .getX() e .setX() pode ser feito para funcionar perfeitamente com coordenadas radiais.Outra é que é possível manter invariantes de classe, fazendo o que for necessário para manter a classe consistente dentro do setter.Outra é que é possível ter funcionalidades acionadas em um conjunto, como o registro do saldo da conta bancária.

No entanto, para classes mais abstratas, as variáveis-membro perdem significado individual e só fazem sentido no contexto.Você não precisa conhecer todas as variáveis ​​internas de uma classe de fluxo C++, por exemplo.Você precisa saber como inserir e retirar elementos e como realizar várias outras ações.Se você contasse com a estrutura interna exata, ficaria atolado em detalhes que poderiam variar arbitrariamente entre compiladores ou versões.

Então, eu diria para usar variáveis ​​privadas quase exclusivamente, getters e setters onde elas tenham um significado real no comportamento do objeto, e não de outra forma.

Só porque getters e setters são frequentemente usados ​​em demasia não significa que sejam inúteis.

O problema com getters/setters é que eles tentam falsificar o encapsulamento, mas na verdade o quebram expondo seus componentes internos.Em segundo lugar, estão a tentar fazer duas coisas distintas – proporcionar acesso e controlar o seu Estado – e acabam por não fazer nenhuma delas muito bem.

Ele quebra o encapsulamento porque quando você chama um método get/set, primeiro você precisa saber o nome (ou ter uma boa ideia) do campo que deseja alterar e, em segundo lugar, saber seu tipo, por exemplo.você não poderia ligar

setPositionX("some string");

Se você souber o nome e o tipo do campo, e o setter for público, então qualquer um poderá chamar o método como se fosse um campo público de qualquer maneira, é apenas uma maneira mais complicada de fazer isso, então por que não simplificar e fazer é um campo público em primeiro lugar.

Ao permitir o acesso ao seu estado, mas tentar controlá-lo ao mesmo tempo, um método get/set apenas confunde as coisas e acaba sendo um clichê inútil ou enganoso, por não fazer realmente o que diz que faz por ter lado- efeitos que o usuário pode não esperar.Se a verificação de erros for necessária, ela poderá ser chamada de algo como

public void tryPositionX(int x) throws InvalidParameterException{
    if (x >= 0)
        this.x = x;
    else
        throw new InvalidParameterException("Holy Negatives Batman!");
}

ou se for necessário código adicional, ele poderá ser chamado de um nome mais preciso com base no que todo o método faz, por exemplo.

tryPositionXAndLog(int x) throws InvalidParameterException{
    tryPositionX(x);
    numChanges++;
}

IMHO, precisar de getters/setters para fazer algo funcionar geralmente é um sintoma de design ruim.Use o princípio "diga, não pergunte" ou repense por que um objeto precisa enviar seus dados de estado em primeiro lugar.Exponha métodos que alteram o comportamento de um objeto em vez de seu estado.Os benefícios disso incluem manutenção mais fácil e maior extensibilidade.

Você também menciona o MVC e diz que um modelo não pode ser responsável por sua visão, nesse caso Allen Holub dá um exemplo de criação de uma camada de abstração com uma classe "dê-me-um-JComponent-que-representa-sua-identidade" que ele diz que "isolaria a forma como as identidades são representadas do resto do sistema." Não tenho experiência suficiente para comentar se isso funcionaria ou não, mas à superfície parece uma ideia decente.

Getters/setters públicos são ruins se fornecem acesso aos detalhes de implementação.Ainda assim, é razoável fornecer acesso às propriedades do objeto e usar getters/setters para isso.Por exemplo, se Car tiver a propriedade color, é aceitável permitir que os clientes o "observem" usando um getter.Se algum cliente precisar recolorir um carro, a classe pode fornecer um setter (embora 'recolorir' seja um nome mais claro).É importante não permitir que os clientes saibam como as propriedades são armazenadas nos objetos, como são mantidas e assim por diante.

Ummmm... ele nunca ouviu falar do conceito de Encapsulamento.Os métodos Getter e Setter são implementados para controlar o acesso aos membros de uma classe.Ao tornar todos os campos visíveis publicamente... qualquer pessoa poderia escrever quaisquer valores que quisesse, invalidando completamente o objeto inteiro.

Caso alguém esteja um pouco confuso sobre o conceito de encapsulamento, leia aqui:

Encapsulamento (Ciência da Computação)

... e se eles forem realmente maus, o .NET incorporaria o conceito de propriedade na linguagem?(Métodos Getter e Setter que parecem um pouco mais bonitos)

EDITAR

O artigo menciona encapsulamento:

"Getters e setters podem ser úteis para variáveis ​​que você deseja encapsular especificamente, mas não é necessário usá-los para todas as variáveis.Na verdade, usá-los para todas as variáveis ​​é um cheiro desagradável de código."

Usar esse método tornará extremamente difícil manter o código no longo prazo.Se você descobrir no meio de um projeto que dura anos que um campo precisa ser encapsulado, você terá que atualizar CADA REFERÊNCIA desse campo em todo o seu software para obter o benefício.Parece muito mais inteligente usar o encapsulamento adequado antecipadamente e evitar dores de cabeça mais tarde.

Eu acho que getters e setters só devem ser usados ​​para variáveis ​​que precisam ser acessadas ou alteradas fora de uma classe.Dito isto, não acredito que as variáveis ​​devam ser públicas, a menos que sejam estáticas.Isso ocorre porque tornar públicas variáveis ​​que não são estáticas pode fazer com que elas sejam alteradas indesejavelmente.Digamos que você tenha um desenvolvedor que usa variáveis ​​públicas de maneira descuidada.Ele então acessa uma variável de outra classe e, sem querer, a altera.Agora ele tem um erro em seu software como resultado desse acidente.É por isso que acredito no uso adequado de getters e setters, mas você não precisa deles para todas as variáveis ​​privadas ou protegidas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top