Estamos fazendo muitos testes de unidade e refatoração de nossos objetos de negócios, e eu pareço ter opiniões muito diferentes sobre o design de classe do que outros pares.
Um exemplo de classe da qual eu não sou fã:
public class Foo
{
private string field1;
private string field2;
private string field3;
private string field4;
private string field5;
public Foo() { }
public Foo(string in1, string in2)
{
field1 = in1;
field2 = in2;
}
public Foo(string in1, string in2, string in3, string in4)
{
field1 = in1;
field2 = in2;
field3 = in3;
}
public Prop1
{ get { return field1; } }
{ set { field1 = value; } }
public Prop2
{ get { return field2; } }
{ set { field2 = value; } }
public Prop3
{ get { return field3; } }
{ set { field3 = value; } }
public Prop4
{ get { return field4; } }
{ set { field4 = value; } }
public Prop5
{ get { return field5; } }
{ set { field5 = value; } }
}
Na classe "real", eles não são todos string, mas em alguns casos, temos 30 campos de apoio para propriedades completamente públicas.
Eu odeio essa aula e não sei se estou sendo exigente. Algumas coisas a serem observadas:
- Campos de apoio privados, sem lógica nas propriedades, parecem desnecessários e incham a classe
- Vários construtores (um pouco ok), mas juntamente com
- todas as propriedades com um setter público, eu não sou fã.
- Potencialmente, nenhuma propriedade receberia um valor devido ao construtor vazio; se um chamador não souber, você poderá obter algo muito indesejável e difícil de testar quanto ao comportamento.
- São muitas propriedades! (no caso 30)
Acho muito mais difícil realmente saber em que estado o objeto Foo
está a qualquer momento, como implementador. O argumento foi formulado "talvez não tenhamos as informações necessárias para definir Prop5
no momento da construção do objeto. Ok, acho que posso entender isso, mas se for esse o caso, torne apenas o Prop5
setter público, nem sempre até 30 propriedades em uma classe.
Estou apenas sendo exigente e / ou louco por querer uma aula que seja "fácil de usar" em vez de ser "fácil de escrever (tudo público)"? Classes como a acima gritam para mim, eu não sei como isso será usado, então apenas tornarei tudo público para o caso .
Se não estou sendo muito exigente, quais são os bons argumentos para combater esse tipo de pensamento? Não sou muito bom em articular argumentos, pois fico muito frustrado ao tentar expressar meu ponto de vista (não intencionalmente, é claro).
fonte
get
ouset
:-)Respostas:
Classes completamente públicas têm uma justificativa para certas situações, bem como as outras classes extremas, com apenas um método público (e provavelmente muitos métodos particulares). E aulas com alguns públicos, alguns métodos privados também.
Tudo depende do tipo de abstração que você vai modelar com eles, quais camadas você possui em seu sistema, o grau de encapsulamento necessário nas diferentes camadas e (é claro) que escola de pensamento o autor da turma vem de. Você pode encontrar todos esses tipos no código SOLID.
Existem livros inteiros escritos sobre quando preferir que tipo de design, por isso não vou listar nenhuma regra aqui, o espaço nesta seção não seria suficiente. No entanto, se você tiver um exemplo do mundo real para uma abstração que você gosta de modelar com uma classe, tenho certeza de que a comunidade aqui terá todo o prazer em ajudá-lo a melhorar o design.
Para abordar seus outros pontos:
Então, ao invés de
Escreva
ou
"Vários construtores": isso não é um problema em si. Ocorre um problema quando há duplicação de código desnecessária, como mostrado no seu exemplo, ou a hierarquia de chamada é complicada. Isso pode ser facilmente resolvido refatorando partes comuns em uma função separada e organizando a cadeia de construtores de maneira unidirecional
"Potencialmente, nenhuma propriedade teria um valor atribuído ao construtor vazio": em C #, todo tipo de dados tem um valor padrão claramente definido. Se as propriedades não forem inicializadas explicitamente em um construtor, elas receberão esse valor padrão atribuído. Se isso for usado intencionalmente , está perfeitamente correto - portanto, um construtor vazio pode estar correto se o autor souber o que está fazendo.
"São muitas propriedades! (No caso 30)": sim, se você é livre para projetar essa classe de maneira greenfield, 30 são demais, eu concordo. No entanto, nem todos nós temos esse luxo (você não escreveu no comentário abaixo, é um sistema legado?). Às vezes, você precisa mapear registros de um banco de dados, arquivo ou dados existente de uma API de terceiros para o seu sistema. Portanto, nesses casos, 30 atributos podem ser algo com que se deve conviver.
fonte
null
é não é? portanto, se uma das propriedades realmente usada em a.ToUpper()
, mas um valor nunca for definido para ela, seria lançada uma exceção de tempo de execução. Este é outro exemplo bom porque os necessários dados para a classe deve ter para ser definido durante a construção objeto. Não basta deixar para o usuário. THanksAo dizer que "Campos de apoio privados sem lógica nas propriedades parecem desnecessários e incham a classe", você já tem um argumento decente.
Não parece que vários construtores sejam o problema aqui.
Quanto a ter todas as propriedades como públicas ... Talvez pense dessa maneira, se você quiser sincronizar o acesso a todas essas propriedades entre os threads, será muito difícil, pois poderá escrever código de sincronização em qualquer lugar em que esteja usava. No entanto, se todas as propriedades fossem incluídas por getters / setters, você poderia facilmente criar a sincronização na classe.
Acho que quando você diz "difícil testar o comportamento", o argumento fala por si. Seu teste pode ter que verificar se não é nulo ou algo parecido (em todos os locais em que a propriedade é usada). Realmente não sei porque não sei como é seu teste / aplicativo.
Você está certo em muitas propriedades. Você poderia tentar usar herança (se as propriedades forem semelhantes o suficiente) e depois ter uma lista / matriz usando genéricos? Em seguida, você pode escrever getters / setters para acessar as informações e simplificar a maneira como você interagirá com todas as propriedades.
fonte
Como você me disse,
Foo
é um objeto de negócios. Ele deve encapsular algum comportamento nos métodos de acordo com o seu domínio e seus dados devem permanecer o mais encapsulados possível.No exemplo que você mostra,
Foo
parece mais um DTO e vai contra todos os princípios de POO (consulte Modelo de domínio anêmico )!Como melhorias, eu sugeriria:
Esta poderia ser uma classe refatorada em potencial com estas observações:
fonte
"Agora podemos testar unitariamente esses métodos refatorados. Como é, por razões x, y, z, o cliente não pode ser testado.
Na medida em que qualquer um dos argumentos acima possa ser apresentado, agregado:
fonte