Você fez uma alteração em algum sistema, e na hora de compilar viu que a IDE (Visual Studio, Eclipse) gerou um monte de erros que não existiam antes? E quando foi analisar os erros, percebeu que houve quebra em vários pontos do sistema, devido a incompatibilidade entre classes?
Dependência entre Classes – isso precisa ser avaliado.
Muitas vezes, uma simples exclusão de um atributo de uma classe pode gerar dezenas de quebras no sistema, inviabilizando a compilação, e demandando refatoração.
A causa disso é que há relacionamento de dependência entre as classes contidas na estrutura do sistema.
Neste contexto – devido ao acoplamento entre estas classes – dependendo da alteração realizada em uma classe, pode haver efeito colateral em várias outras que dependem dela.
Dependência entre Classes
Quando falamos de Orientação a Objetos, estamos falando de trazer o mundo real para dentro do projeto de software, onde devemos tentar ao máximo espelhar na estrutura do sistema (seus domínios, módulos, camadas, classes, métodos) o cenário real do problema que o software está resolvendo.
/* A OOP ou POO – Programação Orientada a Objetos – foi pensada, dentre outros motivos, para viabilizar o desenvolvimento de software com uma visão mais próxima da vida real, da realidade do problema, quando na implementação da solução. Por exemplo, ao invés de ter uma lista de classes, com nomes como cls123, cls456, cls789, todas elas no mesmo nível hierárquico na estrutura do sistema, a POO nos orienta a pensar no cenário do cliente, e modelar as classes, por exemplo, conforme a realidade do negócio. Se temos três classes, onde uma representa o cliente, outra um fornecedor, e outra um vendedor, seguindo a POO poderíamos ter uma outra classe chamada Pessoa, e Cliente, Fornecedor e Vendedor herdarem dela, e conceitualmente teríamos classes com nomes como, PessoaEntidade, ClienteEntidade, FornecedorEntidade, VendedorEntidade, e escopos também próximos da vida real, do negócio. */
E na vida real, a dependência acontece sempre.
Vamos imaginar uma Casa (que seria nossa classe). Para uma Casa existir, ela depende de uma Rua, e depende também de um Lote. Depende porque sem a Rua ou sem o Lote a Casa não consegue ser uma Casa. E por depender do Lote ou da Rua, qualquer alteração na estrutura do Lote ou da Rua pode gerar efeito colateral na casa.
Por exemplo, se houver uma mudança no tamanho útil do Lote, imposta pela Prefeitura, que visa reduzir o tamanho da área construída dentro dela, isso vai gerar impacto na casa, que terá que ter uma demolição parcial para adequar-se à nova legislação imposta. Ainda, se houver uma obra na rede de esgoto da rua, que paralise a rede de esgoto da rua por 48 horas, isso vai gerar efeito colateral na casa, que não poderá ser habitada por 48 horas, pois uma casa sem sistema de esgoto não funciona.
Esse tipo de relacionamento é muito comum entre as partes de um sistema. As partes do todo geralmente funcionam juntas.
Cuidados ao utilizar Dependência
Mas toda facilidade tem seu custo. Utilizar dependências favorece o alto acoplamento na estrutura do sistema, e isso não é bom. Mas é uma facilidade pois diminui a complexidade na modelagem do sistema. Tentar não usar dependência na maioria das vezes, torna a implementação mais complexa, o que demanda mais tempo para modelar e implementar a solução, e dificulta a manutenibilidade do software (considerando o perfil médio de Analista de Sistemas).
Então, é importantíssimo utilizar com bom senso as dependências, procurando sempre isolar as classes quando possível, focando sempre no desacoplamento.
Um conceito fundamental de entender, para quem quer implementar soluções desacopladas é a Injeção de Dependência. Recomendo que, quem puder, estude sobre o assunto. E ainda, alguns Padrões de Projeto (Design Patterns), tanto Criacionais como Estruturais, que são pertinentes para ajudar no desacoplamento, por exemplo: Façade, Simple Factory, Factory e Abstract Factory.
Relacionamento de Dependência no Diagrama de Classes
Dependência talvez seja o relacionamento mais comum entre as classes de um sistema. Em função do uso de diagramas de classes não ser algo onipresente nas empresas que produzem software, e também por muitas dependências não serem de conhecimento do analista (nem todo profissional sabe o que “vem junto” com um usign ou um import por exemplo) talvez isso não seja percebido.
Em termos práticos, a existência de dependência entre classes significa que para uma classe ser compilada e/ou executada a outra classe precisa estar “linkada” a ela (às vezes a dependência ocorre apenas em tempo de execução, quando há o uso de injeção de dependência, factories, reflexão etc.).
A seguir temos um pequeno diagrama, com a explicação dos detalhes logo a seguir:
No diagrama acima temos três classes, e uma relação de dependência da classe “ClienteNegocio” para as classes “ClienteEntidade” e “ClienteFiltro”.
Porque ClienteNegocio depende de ClienteEntidade?
Em todos os métodos da classe podemos perceber que parâmetros ou retorno são do tipo ClienteEntidade. Então a classe de negócio não pode nem ser compilada sem levar junto ClienteEntidade, por razões de referência. Para existir, ClienteNegocio depende de ClienteEntidade.
Porque ClienteNegocio depende de ClienteFiltro?
No método BuscarPorFiltro(…) o tipo do parâmetro utilizado é ClienteFiltro. Como no caso anterior, não é possível nem compilar sem a referência à classe citada. Para existir, ClienteNegocio depende de ClienteFiltro.
Porque ClienteEntidade e ClienteFiltro não dependem de ClienteNegocio?
Como pode-se ver no diagrama, a direção do relacionamento é de ClienteNegocio para as outras duas classes. Isso porque ClienteNegocio depende delas, mas elas não dependem de ClienteNegocio, vivem sem ela. No contexto apresentado são classes POCO (no C#, POJO no Java), não dependem de nenhuma classe específica salvo as do próprio framework utilizado.
Como ver as dependências em sistemas que não possuem diagramas de classe
Quando o sistema possui artefatos de modelagem de sua estrutura e arquitetura, como diagramas de classe, basta consultar estas especificações para verificar como foram projetados os relacionamentos entre as classes do sistema.
/* Apenas um detalhe: infelizmente é pouco comum que empresas que produzam diagramas nos projetos de software não deem manutenção adequada nestes artefatos, gerando muita defasagem de informação (o software evolui mas a documentação “fica no passado”). Se isso for plausível no projeto em que está, não confie cegamente nas especificações, sempre verifique no sistema se o que está especificado está igual ao que está implementando no código fonte. */
Quando não existem diagramas de classe, essa verificação tem que ser feita diretamente na IDE, na ferramenta utilizada para desenvolvimento do sistema.
As IDEs mais antigas não possuem recurso para isso, mas IDEs mais modernas como o Visual Studio ou Eclipse possuem. Estes recursos são chamados de “Find References” ou “Call Hierarchy”. Abaixo dois exemplos, o primeiro do Eclipse e o segundo do Visual Studio, com um dos recursos citados.
Para quem desenvolve usando ferramentas Microsoft, o Visual Studio possui um recurso muito legal para visualização da estrutura estática do software em desenvolvimento, o que possibilita uma clareza excelente das dependências da estrutura do projeto.
É o recurso de Geração de Mapas de Código, algo como diagramas de Classe da UML, dentro do Visual Studio.
Grande abraço!