Em um projeto, encontrei um código como este:
class SomeClass
{
private SomeType _someField;
public SomeType SomeField
{
get { return _someField; }
set { _someField = value; }
}
protected virtual void SomeMethod(/*...., */SomeType someVar)
{
}
private void SomeAnotherMethod()
{
//.............
SomeMethod(_someField);
//.............
}
};
Como convencer meus colegas de equipe que esse é um código incorreto?
Eu acredito que isso é uma complicação desnecessária. Por que passar uma variável de membro como um parâmetro de método se você já tem acesso a ela? Isso também é uma violação do encapsulamento.
Você vê outros problemas com este código?
Respostas:
Penso que este é um tópico válido, mas a razão pela qual você está obtendo respostas contraditórias é por causa da forma como a pergunta foi formada. Pessoalmente, tive as mesmas experiências com minha equipe em que passar membros como argumentos era desnecessário e complicou o código. Teríamos uma classe que trabalha com um conjunto de membros, mas algumas funções acessam os membros diretamente e outras funções modificam os mesmos membros através de parâmetros (ou seja, usam nomes completamente diferentes) e não havia absolutamente nenhuma razão técnica para fazer isso. Por razões técnicas, quero dizer um exemplo que Kate forneceu.
Eu recomendaria dar um passo atrás e, em vez de focar exatamente na passagem dos membros como parâmetros, iniciar discussões com sua equipe sobre clareza e legibilidade. Mais formalmente ou apenas nos corredores, discuta o que torna alguns segmentos de código mais fáceis de ler e outros segmentos de código mais difíceis. Em seguida, identifique medidas de qualidade ou atributos de código limpo que, como uma equipe, você gostaria de buscar. Afinal, mesmo quando trabalhamos em projetos de campo verde, passamos mais de 90% do tempo lendo e assim que o código é escrito (digamos 10 a 15 minutos depois), ele entra em manutenção, onde a legibilidade é ainda mais importante.
Portanto, para o seu exemplo específico aqui, o argumento que eu usaria é que menos código é sempre mais fácil de ler do que mais código. Uma função que possui 3 parâmetros é mais difícil para o cérebro processar do que uma função que não possui nenhum ou 1 parâmetro. Se houver outro nome de variável, o cérebro precisará acompanhar outra coisa ao ler o código. Então, lembre-se de "int m_value" e "int localValue" e lembre-se de que um realmente significa que o outro sempre é mais caro para o seu cérebro do que simplesmente trabalha com "m_value".
Para mais munição e idéias, eu recomendaria pegar uma cópia do Código Limpo do tio Bob .
fonte
Eu posso pensar em uma justificativa para passar um campo de membro como um parâmetro em um método (privado): torna explícito do que seu método depende.
Como você diz, todos os campos de membros são parâmetros implícitos do seu método, porque o objeto todo é. No entanto, o objeto completo é realmente necessário para calcular o resultado? Se
SomeMethod
é um método interno do qual depende apenas_someField
, não é mais fácil tornar essa dependência explícita? De fato, tornar essa dependência explícita também pode sugerir que você pode refatorar esse pedaço de código da sua classe! (Observe que presumo que não estamos falando de getters ou setters aqui, mas de um código que realmente calcula algo)Eu não faria o mesmo argumento para métodos públicos, porque o chamador não sabe nem se importa com qual parte do objeto é relevante para calcular o resultado ...
fonte
_someField
é o único parâmetro necessário para calcular o resultado, e nós apenas o tornamos explícito. (. Nota: isto é importante Nós não adicionar uma dependência, nós só tornou explícita!)this
(ouself
), mas é explicitada pela própria chamadaobj.method(x)
). Outras dependências implícitas são o estado do objeto; isso geralmente torna o código mais difícil de entender. Sempre que possível - e dentro do razoável - torne as dependências explícitas e com estilo funcional. No caso de métodos privados, se possível, passe explicitamente todos os parâmetros que eles precisam. E sim, ajuda a refatorá-los.Eu vejo um forte motivo para passar variáveis-membro como argumentos de função para métodos privados - pureza da função. As variáveis de membro são efetivamente um estado global do ponto de vista; além disso, um estado global mutável se esse membro for alterado durante a execução do método. Substituindo referências de variáveis de membros por parâmetros de método, podemos efetivamente tornar uma função pura. As funções puras não dependem de um estado externo e não têm efeitos colaterais, sempre retornando os mesmos resultados, com o mesmo conjunto de parâmetros de entrada - facilitando o teste e a manutenção futura.
Claro que não é fácil nem prático ter todos os seus métodos como métodos puros em uma linguagem OOP. Mas acredito que você ganha muito em termos de clareza de código ao manter os métodos que lidam com lógica complexa pura e usar variáveis imutáveis, enquanto mantém o tratamento de estado "global" impuro separado dentro de métodos dedicados.
Passar variáveis de membro para uma função pública do mesmo objeto ao chamar a função externamente, no entanto, constituiria um grande cheiro de código na minha opinião.
fonte
Se a função for chamada várias vezes, às vezes passando essa variável de membro e outras vezes, então tudo bem. Por exemplo, eu não consideraria isso tão ruim assim:
onde
newStartDate
está alguma variável local em_StartDate
é uma variável de membro.No entanto, se a função apenas for chamada com a variável de membro passada para ela, isso é estranho. As funções de membro trabalham em variáveis de membro o tempo todo. Eles podem estar fazendo isso (dependendo do idioma em que você está trabalhando) para obter uma cópia da variável membro - se for esse o caso e você não conseguir ver, o código poderá ser melhor se tornar explícito todo o processo.
fonte
O que ninguém tocou é que SomeMethod está protegido virtual. Significando que uma classe derivada pode usá-lo E reimplementar sua funcionalidade. Uma classe derivada não teria acesso à variável privada e, portanto, não poderia fornecer uma implementação personalizada de SomeMethod que depende da variável privada. Em vez de assumir a dependência da variável privada, a declaração exige que o chamador a transmita.
fonte
As estruturas de GUI geralmente têm algum tipo de classe 'View' que representa coisas desenhadas na tela, e essa classe geralmente fornece um método como
invalidateRect(Rect r)
marcar parte de sua área de desenho como precisando ser redesenhada. Os clientes podem chamar esse método para solicitar uma atualização para parte da exibição. Mas uma visão também pode chamar seu próprio método, como:para causar redesenho de toda a área. Por exemplo, ele pode fazer isso quando é adicionado pela primeira vez à hierarquia de visualizações.
Não há nada de errado em fazer isso - o quadro da vista é um retângulo válido e a própria vista sabe que deseja se redesenhar. A classe View pode fornecer um método separado que não usa parâmetros e usa o quadro da exibição:
Mas por que adicionar um método especial apenas para isso quando você pode usar o mais geral
invalidateRect()
? Ou, se você optou por fornecerinvalidateFrame()
, provavelmente o implementaria em termos dos mais geraisinvalidateRect()
:Você deve passar variáveis de instância como parâmetros para seu próprio método se o método não operar especificamente nessa variável de instância. No exemplo acima, o quadro da vista é apenas outro retângulo no que diz respeito ao
invalidateRect()
método.fonte
Isso faz sentido se o método é um método utilitário. Digamos, por exemplo, que você precise derivar um nome abreviado exclusivo de várias seqüências de texto livre.
Você não gostaria de codificar uma implementação separada para cada string, em vez disso, faz sentido passar a string para um método comum.
No entanto, se o método sempre funciona em um único membro, parece um pouco idiota passá-lo como parâmetro.
fonte
O principal motivo para ter uma variável de membro em uma classe é permitir que você a defina em um local e tenha seu valor disponível para todos os outros métodos da classe. Portanto, em geral, você esperaria que não houvesse necessidade de passar a variável membro para um método da classe.
No entanto, posso pensar em algumas razões pelas quais você pode passar a variável membro para outro método da classe. A primeira é se você precisa garantir que o valor da variável de membro precise ser usado inalterado quando usado com o método chamado, mesmo que esse método precise alterar o valor real da variável de membro em algum momento do processo. A segunda razão está relacionada à primeira, na qual você pode querer garantir a imutabilidade de um valor no escopo de uma cadeia de métodos - ao implementar uma sintaxe fluente, por exemplo.
Com tudo isso em mente, eu não chegaria ao ponto de dizer que o código é "ruim", como tal, se você passar uma variável de membro para um dos métodos da classe. No entanto, eu sugeriria que geralmente não é o ideal, pois isso poderia incentivar muita duplicação de código onde o parâmetro é atribuído a uma variável local, por exemplo, e os parâmetros extras adicionam "ruído" no código onde não é necessário. Se você é um fã do livro Código Limpo, deve saber que ele deve manter o número mínimo de parâmetros do método mínimo e apenas se não houver outra maneira mais sensata de acessar o parâmetro. .
fonte
Algumas coisas que vêm a mim quando penso sobre isso:
fonte
Você está ausente se o parâmetro ("argumento") for referência ou somente leitura.
Muitos desenvolvedores, geralmente atribuem diretamente os campos internos de uma variável, nos métodos construtores. Existem algumas exceções.
Lembre-se de que "setters" podem ter métodos adicionais, não apenas atribuição, e às vezes até métodos virtuais, ou chamar métodos virtuais.
Nota: Sugiro manter os campos internos das propriedades como "protegidos".
fonte