Os getters protegidos triviais são um exagero flagrante?

8

Algo que eu realmente não tinha pensado antes (sintaxe AS3):

private var m_obj:Object;
protected function get obj():Object
{
    return m_obj;
}

private var m_str:String;
protected function get str():String
{
    return m_str;
}

Pelo menos as subclasses não poderão definir m_obj ou m_str (embora ainda possam modificar m_obj).

Minha pergunta: isso é apenas um exagero flagrante?


A pergunta deste programador: quando ou por que alguém deve usar getters / setters para propriedades de classe em vez de simplesmente torná-los propriedades públicas? foi sugerido como duplicado.

Essa pergunta é diferente porque aborda apenas propriedades públicas versus privadas e se uma propriedade deve ser agrupada com getters e setters. Para minha pergunta, estou focando nas variáveis protegidas e em como as classes herdadas interagem com essas variáveis.

Portanto, a implementação alternativa seria:

protected var m_obj:Object; //more accessible than a private variable with a protected getter
protected var m_str:String; //more accessible than a private variable with a protected getter

Minha pergunta é semelhante, porque estou falando sobre o uso de getters protegidos triviais para impedir que subclasses gravem nas variáveis, mas para permitir a todos os outros acessos.
Em idiomas como o AS3, isso permitirá que eles façam alterações em variáveis ​​de objeto mutáveis, desde que as próprias referências não sejam modificadas.

Panzercrisis
fonte
2
pergunte a si mesmo; é perigoso que subclasses pode configurá-lo para algo arbitrário, se a resposta for sim, então não é um exagero
catraca aberração
1
Editei a pergunta para deixar mais claro o que estou falando sobre não fazer.
Panzercrisis 24/10

Respostas:

6

Isso é apenas um exagero flagrante?

Muitas vezes é, às vezes não é.

Manter m_objum bom estado conhecido ajuda a proteger o Princípio de Substituição de Liskov, mantendo seu código mais resiliente e com maior qualidade. Às vezes, você pode confiar nos herdeiros para respeitar esse comportamento, às vezes não (devido ao padrão de uso ou à sutileza do contrato que implementa). Embora esse código / pergunta também tropeçar em alguns dos motivos de "Por que o Código Limpo está sugerindo evitar variáveis ​​protegidas?"

Telastyn
fonte
Às vezes vale a pena quando as subclasses ainda podem fazer quase tudo o que desejam m_obj, exceto configurá-lo como nulo ou outra instância?
Panzercrisis
@ Panzercrisis - depende do idioma. Em idiomas gerenciados sem memória, configurá-lo como nulo / outra coisa é um grande problema.
Telastyn
13

Os getters protegidos com trivialidade têm pelo menos duas vantagens práticas sobre uma variável exposta:

  • Para depuração, eles são um lugar perfeito para colocar um ponto de interrupção condicional que é acionado quando "esse valor engraçado entra no seu programa". É muito mais difícil obter o mesmo controle se você acessar o valor diretamente.

  • Para zombar de um objeto, se você estiver usando esse método para testar descendentes.

O custo do uso de getters é praticamente zero, portanto, não há motivo real para não usá-los. Existe?

Michael Le Barbier Grünewald
fonte
Obrigado. A questão foi editada para maior clareza.
Panzercrisis
Minha resposta não se refere ao atributo publicou protecteddo getter! :-)
Michael Le Barbier Grünewald
Oh, desculpe por isso.
Panzercrisis
4
O custo está no código inchado. O custo não é grande, mas é maior que zero. Certamente, os participantes são triviais, mas precisam ser ignorados toda vez que alguém trabalha na classe. E eles podem estar errados: get x() ... { return _y; } ninguém jamais digitaria isso, mas isso pode acontecer devido a um recorte e colar incorreto.
Kevin cline # 24/13
3
É uma troca entre custos diferentes, no entanto. E, na minha experiência, o custo de rastrear bugs em que subclasses comprometem sua classe pai de maneiras sutis é enorme comparado ao custo de manutenção de getters e setters.
perfil completo de Julia Hayward
7

Não importa se você usa getters ou acesso ao campo, qualquer revisor experiente descobrirá o problema em seu design e reclamará que seus objetos expõem dados em vez de comportamento.

Observe que apelar para "protegido" não ajudará, porque o objetivo da herança é permitir que as subclasses ajustem / sintonizem o comportamento do pai ( LSP ), para não justificar a troca indiscriminada de dados com os pais. Como Bertrand Meyer coloca,

Nem o princípio Aberto-Fechado nem a redefinição de herança são uma maneira de solucionar falhas de design ... (A única exceção potencial a essa regra é o caso de software defeituoso que você não tem a liberdade de modificar.)

O raciocínio "Getters are evil" se aplica independentemente de você soletrar getexplicitamente ou mascará-lo atrás do acesso ao campo.

veja este artigo ou este artigo de Alan Holub ...

Você não deve usar métodos de acesso (getters e setters), a menos que seja absolutamente necessário, porque esses métodos expõem informações sobre como uma classe é implementada e, como conseqüência, dificultam a manutenção do código. Às vezes, os métodos get / set são inevitáveis, mas um designer experiente de OO provavelmente poderia eliminar 99% dos acessadores atualmente no seu código sem muita dificuldade.

Os métodos getter / setter costumam aparecer no código porque o codificador estava pensando proceduralmente. A melhor maneira de romper essa mentalidade processual é pensar em termos de uma conversa entre objetos que têm responsabilidades bem definidas ...

Se você estiver seriamente preocupado com o problema, a primeira coisa a considerar é o redesenho do código para livrar-se dos getters e do acesso não particular ao campo , em vez de jogar jogos mentais infrutíferos escolhendo qual caminho é menos inapropriado .

Vale ressaltar mais uma vez, ocultar o design problemático por trás da cortina de fumaça "protegida" não fará com que o problema desapareça - a herança não justifica truques como esse. Parafraseando sua pergunta, esforce-se para projetar as coisas de uma maneira que evite que as classes herdadas interajam com essas variáveis - nem por getters nem por acesso a campo. Diga, não pergunte :

O código processual obtém informações e toma decisões. O código orientado a objetos informa aos objetos para fazerem as coisas.

Gastar tempo e esforço para decidir qual caminho preferir obter informações, contra o princípio clássico de evitar isso, agora isso me parece um exagero flagrante .

mosquito
fonte
Nós somos da mesma escola. A pergunta não deve ser: "oh, a classe base não funciona corretamente, então como eu mexo com seus dados", deveria ser "como faço para que a classe base funcione para que eu não precise mexer com seu estado interno? " Raramente eu implemento getters. Quase nunca implanto setters. Os métodos nas minhas aulas são comandos na veia de "Tell Don't Ask". Às vezes, sinto que uma das minhas principais responsabilidades profissionais é corrigir erros ocultando detalhes de implementação expostos por programadores preguiçosos ou impensados.
Dash-tom-bang
1

No caso de uma implementação de classe imutável, proteger suas variáveis ​​internas violaria o contrato que você definiu; nesse caso, os getters protegidos não seriam um exagero e serão necessários (embora você precise devolver cópias de sua variável, não um ponteiro para ela). )

Nas classes mutáveis, ainda prefiro getters / setters a conceder acesso direto às minhas variáveis ​​internas. Isso garante que você possa alterar a maneira como lida com suas variáveis ​​internas como desejar, sem medo de quebrar nenhum código de cliente. Por exemplo, imagine que você tenha uma classe Java da seguinte maneira:

public abstract class SomeClass {
    protected int[] someVariable;

    // Rest of implementation
}

A extensão da classe cliente acessaria SomeClassdiretamente someVariable, manipulando-a como bem entenderem. Agora, isso pode criar todos os tipos de problemas se essas classes não saírem someVariableem um estado aceitável. Além disso, se você decidir, em um release subsequente, alterar a definição de SomeClassusar a em List<Integers>vez de uma matriz, você quebrará a compatibilidade com a base de código existente, que espera SomeClasster uma matriz interna de ints, não uma lista deint s.

O uso de getters protegidos nesse caso permitiria fazer essa alteração sem quebrar nenhum contrato pré-estabelecido.

public abstract class SomeClass {
    List<Integer> someVariable;

    protected int[] getSomeVariable () {
        // Return int[] version of someVariable
    }

    // Rest of implementation
}
Laf
fonte