Um pouco de histórico: como líder de equipe, uso o NDepend uma vez por semana para verificar a qualidade do nosso código. Especialmente a cobertura de teste, linhas de código e métricas de complexidade ciclomática são inestimáveis para mim. Mas quando se trata de ciclos de nivelamento e dependência, estou um pouco ... bem preocupado. Patrick Smacchia tem um bom post no blog que descreve o objetivo da nivelação.
Para ser claro: em "ciclo de dependência" eu entendo uma referência circular entre dois namespaces.
Atualmente, estou trabalhando em uma estrutura de GUI baseada em Windows CE para instrumentos incorporados - pense na plataforma gráfica do Android, mas em instrumentos de ponta muito baixa. A estrutura é um conjunto único com cerca de 50.000 linhas de código (testes excluídos). A estrutura é dividida nos seguintes namespaces:
- Subsistema de Navegação Principal e Menu
- Subsistema de tela (Apresentadores / Exibições / ...)
- Controles / camada de widget
Hoje, passei o meio dia tentando trazer o código para níveis adequados [graças ao Resharper sem problemas em geral], mas em todos os casos existem alguns ciclos de dependência.
Então, minha pergunta: quão estritamente você segue a regra "No Dependency Cycle"? A nivelação é realmente tão importante?
fonte
Respostas:
Recentemente, escrevi 2 livros brancos, publicados no Simple-Talk, sobre o tópico de arquitetura de código .NET (o primeiro livro é sobre assemblies .NET, o segundo sobre namespaces e nivelamento):
Particionando sua base de código por meio de assemblies .NET e projetos do Visual Studio
Definindo componentes .NET com namespaces
Sim, ele é!
Citação do 2º livro branco:
(...)
fonte
Eu nunca permito dependências circulares entre classes ou namespaces. Em C #, Java e C ++, você sempre pode interromper uma dependência de classe circular introduzindo uma interface.
A codificação do teste primeiro dificulta a introdução de dependências circulares.
fonte
É sempre uma troca: você precisa entender o custo das dependências para entender por que as dependências devem ser evitadas. Da mesma forma, se você entender o custo de uma dependência, poderá decidir melhor se vale a pena no seu caso específico.
Por exemplo, em videogames para console, geralmente dependemos de dependências onde precisamos de relacionamentos estreitos de informações, principalmente por motivos de desempenho. Tudo bem, pois não precisamos ser tão flexíveis quanto uma ferramenta de edição, por exemplo.
Se você entende as restrições de que seu software precisa ser executado (seja hardware, SO ou apenas design (como "a interface do usuário mais simples possível")), será fácil selecionar o entendimento de quais dependências não devem ser feitas e qual é a dependência. Está bem.
Mas se você não tiver uma boa e clara razão, evite qualquer dependência que puder. O código dependente é o inferno da sessão de depuração.
fonte
Sempre que esse tópico é discutido, os participantes geralmente perdem o foco da distinção entre ciclos de tempo de compilação e tempo de execução. O primeiro é o que John Lakos chamou de "design físico", enquanto o último é basicamente irrelevante para a discussão (não se preocupe com os ciclos de tempo de execução, como os criados por retornos de chamada).
Dito isto, John Lakos foi muito rigoroso quanto à eliminação de todos os ciclos (em tempo de construção). Bob Martin, no entanto, adotou a atitude de que apenas ciclos entre binários (por exemplo, DLLs, executáveis) eram significativos e deveriam ser evitados; ele acreditava que ciclos entre classes dentro de um binário não são terrivelmente importantes.
Pessoalmente, mantenho a opinião de Bob Martin sobre isso. No entanto, ainda presto alguma atenção aos ciclos entre as classes, porque a ausência de tais ciclos facilita o código para outras pessoas lerem e aprenderem.
No entanto, é importante ressaltar que qualquer código criado com o Visual Studio não é capaz de ter dependências circulares entre binários (código nativo ou gerenciado). Assim, o problema mais grave dos ciclos foi resolvido para você. :)
fonte
Quase totalmente, porque os ciclos de dependência do tipo build são inconvenientes no Haskell e impossíveis no Agda e no Coq, e esses são os idiomas que normalmente uso.
fonte