Essa é uma prática comum em orientação a objetos? – c# orientação-a-objetos modelagem

Pergunta:


Em orientação a objetos uma forma de garantir o encapsulamento é manter os atributos das classes privados e modificar seu estado via métodos. Além disso, para garantir a integridade dos objetos, é comum exigirmos que dados essenciais para a existência do objeto sejam passados no construtor.

Em classes com poucos atributos isso funciona bem. Acontece que em classes com um número muito grande de atributos, começa a ficar meio estranho. Um exemplo disso é essa classe PessoaJuridica (que eu reconheço não estar muito boa):

public class PessoaJuridica
{
    public string NomeFantasia { get; set; }

    public string RazaoSocial { get; set; }

    public string Endereco { get; set; }

    public string Bairro { get; set; }

    public string Cidade { get; set; }

    public string Estado { get; set; }

    public int CEP { get; set; }

    public int Telefone1 { get; set; }

    public int Telefone2 { get; set; }

    public int Fax { get; set; }

    public int CNPJ { get; set; }

    public int InscricaoEstadual { get; set; }

    public int InscricaoMunicipal { get; set; }

    public DateTime DataConstituicao { get; set; }

    public DateTime DataCadastro { get; set; }

    public string Observacoes { get; set; }
}

A classe tem diversos atributos. Passar tudo isso no construtor é inviavel, e mesmo que escolhesse um subconjunto para passar ainda ficaria ruim. Além disso construir um método para cada atributo não teria muita vantagem em relação ao que já está ai. Por isso pensei na seguinte melhoria:

Olhei na classe algumas coisas que fazem sentido agrupadas. Por exemplo:

  • Endereço, Bairro, Cidade, Estado, CEP formam coletivamente o Endereço da empresa
  • Telefone1, Telefone2 e Fax são tipos de telefones
  • CNPJ, InscricaoEstadual, InscricaoMunicipal e DataConstituicao formam uma documentação da empresa

Por isso codifiquei uma struct Endereco com propriedades “Rua, Numero, Bairro, Cidade, Estado, CEP”, uma struct Telefone com propriedades “DDD, Numero, TipoTelefone” sendo “TipoTelefone” um enum que pode ser “Fixo” ou “Fax”. Finalmente codifiquei uma struct DocumentacaoEmpresa com atributos “RazaoSocial, CNPJ, InscricaoEstadual, InscricaoMunicipal e DataConstituicao”. Com isso fiquei com a classe

public class PessoaJuridica
{
    public string NomeFantasia { get; private set; }

    public Endereco Endereco { get; private set; }

    public IList<Telefone> Telefones { get; private set; }

    public DocumentacaoEmpresa Documentacao { get; private set; }

    public string Observacoes { get; set; }
}

E aí fica mais fácil usar métodos e o construtor para lidar com essa classe. O que eu fiz foi pegar uma classe com vários atributos, agrupar esses atributos em conceitos que fazem sentido e codificar esses conceitos. Além disso, agora tem uma lista de telefones para obedecer o Open/Closed principle.

O que eu gostaria de saber é: essa é uma prática comum na orientação a objetos? A construção dessas structs não foi feita por causa de serem conceitos do domínio, mas sim para melhorar a escrita de uma classe. Existe algum design pattern, ou algo do gênero sobre isso?

Eu sei que não é necessário um design pattern para algo ser válido, mas queria saber se isso é comum e se existe mais informação sobre isso para saber se em geral é uma boa ideia fazer esse tipo de coisa.

Autor da pergunta Leonardo

Comunidade

Solução para o problema

Primeiro vou te dar a solução simples para seu problema de inicializar uma quantidade grande de propriedades:

var pessoa = new PessoaJuridica {
    NomeFantasia = "abc",
    RazaoSocial = "abc",
    Endereco = "rua abc",
    Bairro = "abc",
    Cidade = "abc",
    Estado = "sp",
    CEP = "12345678",
    Telefone1 = "12345678",
    Telefone2 = "12345678",
    Fax = "12345678",
    CNPJ = "11111111000111",
    InscricaoEstadual = "11111111111",
    InscricaoMunicipal = "1111111",
    DataConstituicao = new DateTime(2000, 4, 15),
    DataCadastro = new DateTime(DateTime.Today),
    Observacoes = "abc"
}

Isto é chamado de object initializer.

Mudei alguns para string porque não faz sentido eles serem inteiros. Só porque um dado possui apenas dígitos não significa que ele seja um número. CEP, telefone, CNPJ são identificadores e não números.

Note que o que você fez não facilita a criação da instância. Pelo contrário, você ainda terá que passar todos os dados, mas agora de uma forma uma pouco mais extensa. Não estou dizendo que não deva fazer para organizar, mas não simplifica a criação do objeto.

Por que existe o construtor?

Nem sempre um construtor é necessário, pelo menos em C#. Há linguagens que se viram bem sem um.

Construtores devem dar atomicidade na criação do objeto. Ou seja, ou cria o objeto em um estado válido ou não cria. Se você precisa validar os dados e se alguma validação falhar o objeto não deve ser criado, então um construtor ajuda bastante. O mesmo vale se algum “cálculo” é necessário com os parâmetros iniciais antes de criar o objeto.

Então podemos concluir que só faz sentido passar para um construtor dados que precisam existir obrigatoriamente em um objeto de forma válida ou que precisam ser processados antes de serem inseridos no objeto.

O inicializador de objeto ajuda nesta atomicidade, mas não substitui completamente o construtor. No C# 6 fica até melhor com os inicializadores de propriedade onde é possível fazer um public string NomeFantasia { get; set; } = "" (só exemplo, claro que essa inicialização não é desejável). Se a validação dos dados são individuais isto funciona bem, mas se a validação é interdependente aí só construtor vai resolver elegantemente.

O que substitui o construtor então? A princípio, Nada. Se você precisa do que ele oferece só ele pode fornecer. O que é possível é criar uma classe utilitária para criar um objeto temporário e depois jogar este objeto para a classe definitiva mas raramente isto faz algum sentido.

Separar algumas propriedades em classes auxiliares não ajuda em nada nisto. Pelo contrário, é possível – embora não acho que seja o caso do exemplo demonstrado – quebrar a atomicidade da criação do objeto fazendo desta forma. Mesmo que não quebre, possivelmente complicará a criação do objeto principal tendo que verificar coisas em dois lugares. Sem falar na criação de objetos intermediários que seriam descartados se a criação do objeto principal não for completada.

Mais informações sobre construtor.

Modelagem adequada

Se o problema era a quantidade de parâmetros do construtor e você resolveu encapsular algumas propriedades em outras classes e fazer uma composição só para resolver este problema, então posso dizer que isto não é comum. Você está organizando seus dados para atender uma necessidade do mecanismo da linguagem. Isto definitivamente não é comum, pelo menos em linguagens expressivas, como é o caso do C#.

Então tenho minhas dúvidas se fica mais fácil usar métodos e construtor para lidar com a classe como foi afirmado.

Se criou porque acha que faz sentido organizar os dados assim, ok, é comum fazer isto. Mas não quer dizer que sempre ou quase sempre é feito isto. Depende do caso, depende do seu problema, dos requisitos estabelecidos, do entendimento de como precisa modelar os dados.

Endereço

Existe alguma razão para organizar o endereço em uma classe? Tem alguma vantagem em ter esses dados separados? Você pode precisar desses dados de forma separada da pessoa jurídica? Em modelos simples em geral não precisa. Em modelos mais complexos realmente pode ter vantagens em ter isto separado e bem definido. Quem é mais purista, vai dizer que isto deveria ser feito sempre.

Documentação

O mesmo pode ser dito para a documentação mas a conclusão pode ser diferente. Acho que existe menos motivo para tê-los encapsulados. Eu consigo ver o endereço como uma entidade autônoma, mas eu acho que cada documento já é uma entidade autônoma e não consigo ver muito sentido em agrupá-los a não ser para atender a necessidade do mecanismo, o que já foi dito que não é o ideal. Mas a necessidade real do domínio trabalhado pode dizer o contrário. Por isto não gosto de boas práticas (o grande problema é que a maioria das pessoas não sabem usá-las adequadamente), elas ignoram o problema real.

Telefones

Não sei se entendi mas acho que criar uma lista de telefones não tem nada a ver com o princípio Open/Close. De qualquer forma o motivo para criar uma lista é o fato do número de telefones ser indeterminado. Note que a sua refatoração não produz o mesmo resultado. No primeiro você tem dois telefones quaisquer e um fax. No segundo modelo você pode ter de zero à infinitos telefones – embora dê para restringir isto com algoritmos – e você talvez não saiba o que cada um representa, todos são genéricos – claro que nesta classe, você pode ter uma propriedade extra que determina que tipo de telefone é.

Conclusão

Não sei dizer ao certo se é prática comum, nem sempre é fácil dizer isto com propriedade a não ser em casos bastante óbvios. Mas me parece ser a solução errada. Você mesmo deu o motivo quando diz que não está fazendo por causa do domínio.

Eu não sou purista e se eu realmente precisar, se tiver um motivo muito bom para fazer algo por causa do mecanismo, eu farei, mas sempre que puder eu evitarei, só não será a qualquer custo.

O design pattern que você está fazendo é composição. Mas se pensar bem todas as propriedades fazem composição. Não precisa ser uma propriedade complexa para compor a classe. Quando se fala em preferir composição a herança é que deve-se preferir o jeito mais óbvio e natural de montar um tipo que é a composição. Este é o jeito intuitivo que fazemos e nem percebemos que estamos fazendo ele em todo tipo sendo criado.

Nessa resposta eu já disse que padrões estão em todo lugar.

Utilizo a linguagem Java, porém é muito parecido C#.

O que você poderia fazer é criar uma classe abstrata chamada Terceiro (classes abstratas não podem ser instanciadas) e criar uma classe chamada Juridica, não abstrata que extende de Terceiro (você pode instanciar a classe Juridica). E para os Telefones, como vi acima, você pode fazer uma classe somente para os telefones, sem precisar ter dois atributos Telefone1 e Telefone2.

No Java ficaria assim, acredito que não é muito diferente do C#:

public abstract class Terceiro{

}

public class Juridica extends Terceiro {

}

Atributos como RG, Nome, Telefone seriam colocados na classe abstrata Terceiro e atributos como o CNPJ na classe Juridica.

Outras classes como a Física poderiam extender de Terceiro e esta poderia ter um atributo chamado CPF.

Seria esta uma ótima prática de orientação a objetos, espero que ajude.

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *