Como você decide onde a funcionalidade deve pertencer a um projeto em larga escala?

8

Na minha situação atual de desenvolvimento, temos muitas DLLs, executáveis ​​e bibliotecas estáticas. Como você decide o que deve entrar em uma DLL? O que deve entrar em um executável? Por que ter funcionalidade separada em diferentes arquivos executáveis? Espero que a resposta seja concisa, mas parece ser um tópico amplamente opinativo.

Como você decide onde reside a funcionalidade em um projeto em larga escala (mais de um arquivo executável)? Estou esperando que as respostas variem de "Bom design" a "Modularidade" a "O que quer que o gerenciamento coloque em um documento de requisitos".

wfoster
fonte

Respostas:

13

A escolha entre uma biblioteca e um arquivo executável é relativamente simples: faz sentido executar o código que você deseja colocar em um executável como um programa independente? Caso contrário, provavelmente deve ser uma biblioteca. Em geral, eu preferiria uma camada executável fina sobre quantas bibliotecas forem necessárias, pois isso facilita a reutilização dessas bibliotecas de back-end posteriormente e elas não estão vinculadas a um programa específico.

Quanto a decidir como dividir seu código entre bibliotecas, você pode achar útil o artigo sobre granularidade do tio Bob Martin .

Nele, ele fala sobre a estrutura OO e define vários princípios que podem ajudá-lo a empacotar seu código adequadamente. Estes também são abordados com mais detalhes em seu livro Princípios, padrões e práticas ágeis em C # .

Vou resumir os princípios abaixo:

O Princípio da Equivalência de Reutilização / Liberação (REP)

O grânulo de reutilização é o grânulo de liberação. Somente componentes liberados por meio de um sistema de rastreamento podem ser efetivamente reutilizados. Este grânulo é o pacote.

O tio Bob define a reutilização como capaz de vincular estaticamente ou dinamicamente a biblioteca reutilizada ao seu programa e nunca ter que procurar seu código-fonte. Quando uma nova versão da biblioteca é lançada, ele pode simplesmente integrá-la ao seu sistema.

Tratar bibliotecas dessa maneira leva apenas a manter as coisas relacionadas juntas no mesmo pacote. Caso contrário, os consumidores da biblioteca talvez precisem atualizar para uma nova versão sem motivo ou ficar algumas versões atrasadas.

O Princípio Comum de Reutilização (CRP)

As classes em um pacote são reutilizadas juntas. Se você reutilizar uma das classes em um pacote, reutiliza todas elas.

Este princípio suporta o acima. Se você tem classes no mesmo pacote que não estão relacionadas entre si, pode estar forçando os usuários da sua biblioteca a atualizar desnecessariamente.

O princípio comum de fechamento (PCC)

As classes em um pacote devem ser fechadas contra os mesmos tipos de alterações. Uma alteração que afeta um pacote afeta todas as classes nesse pacote.

Este princípio fala sobre manutenção. A idéia aqui é agrupar classes com base em como elas podem precisar mudar. Dessa forma, suas alterações podem ser localizadas em uma parte do aplicativo e não se espalhar por todo o lado.

Princípio das Dependências Acíclicas (ACP)

A estrutura de dependência entre pacotes deve ser um gráfico acíclico direcionado (DAG). Ou seja, não deve haver ciclos na estrutura de dependência.

A exclusão de dependências cíclicas permite que cada pacote seja desenvolvido independentemente e "liberado" para o restante da empresa quando novas alterações estiverem prontas. Dessa forma, você não acaba com duas equipes em conflito, esperando uma para a outra terminar um trabalho.

Adam Lear
fonte
2

O que tornará mais fácil manter o código a longo prazo? Se você possui funções não relacionadas no mesmo componente (por exemplo, dll, exe, unidade compilada) e precisa alterar uma função, isso quebrará a outra? Quando você faz uma alteração em um componente, é necessário testar novamente todos os componentes downstream, dependendo de TODAS as funções desse componente. Se você limitar a funcionalidade em cada componente, alterar cada um deles será menos arriscado.

Além disso, ao focar os componentes em um pequeno número de funcionalidades intimamente relacionadas, você terá muito mais facilidade para provar que esse componente se comporta da maneira que deveria.

Matthew Flynn
fonte
Sempre pense no cenário "DLL Hell". É como o Sr. Flynn disse, pense no que acontece quando algo muda. Sempre conheça suas dependências muito bem e verifique se você pode cobrir os cenários de instalação sem prejudicar o usuário final.
NoChance
2

Uma maneira simples de abordar uma resposta é reconhecer que "DLL" significa "biblioteca de vínculo dinâmico" e o termo-chave é "biblioteca".

O que você quer em uma biblioteca normal? Bons livros que serão compartilhados por muitos leitores (usuários); itens úteis que acumulam poeira até serem referenciados por um leitor (usuário) para responder a uma pergunta crítica em um momento talvez desesperador; e recursos que vinculam leitores (usuários) a outros recursos. As bibliotecas de código são semelhantes. Mantemos itens como código compartilhado frequentemente, módulos de referência especializados ou de alto valor e recursos da estrutura arquitetônica. As bibliotecas de software podem ser representadas em vários tipos de artefatos de código, como scripts, bibliotecas estáticas, bibliotecas dinâmicas, componentes e arquivos de recursos.

Geralmente, recomendo que seus módulos executáveis ​​funcionem como scripts. Eles descrevem e gerenciam claramente a estrutura principal e o fluxo do seu sistema, mas recorrem a recursos de suas bibliotecas para lidar com os detalhes minuciosos. Acho essa abordagem melhor do que confundir a lógica de alto nível com preocupações de implementação de baixo nível confusas e excessivamente especializadas e técnicas.

Ao considerar como alocar seu código entre executáveis ​​e bibliotecas, é necessário considerar o design lógico e o design físico.

Em sistemas orientados a objetos, por exemplo, é importante organizar logicamente seu código para atribuir responsabilidades corretamente aos métodos corretos nas classes corretas. Isso faz parte do design da solução lógica. Seu design lógico deve ser claro, limpo e enxuto e deve ser expresso em terminologia que se relacione bem com o domínio de seus usuários.

Quando você planeja realmente instalar seu sistema no site de um usuário, pode estar preocupado em criar um design físico que especifique como você agrupará seu código em um conjunto de objetos de recursos de software facilmente implantáveis ​​(geralmente arquivos) que podem ser facilmente misturados e combinados às necessidades de um sistema de destino específico. Determinar quais recursos pertencem a qual pacote de implantação geralmente envolve algumas considerações específicas de design físico que não têm nada a ver diretamente com o design lógico. Por exemplo, convém trocar determinadas bibliotecas de processamento de arquivos de imagem, dependendo de qual cliente receberá um determinado conjunto de objetos de implantação.

Na prática, você descobrirá que um bom design lógico geralmente leva a um bom design físico.

Para resumir, o princípio mais importante é a embalagem inteligente. Ao organizar seus materiais de código em bibliotecas fortemente relacionadas, você acaba com artefatos de código implementáveis ​​úteis que podem ser facilmente reutilizados, movidos ou distribuídos.

John Tobler
fonte