No meu artigo de ontem, apresentei o conceito "Trem da Dependência". Isso é o que acontece quando você importa uma função da sua biblioteca de código, mas acaba tendo que importar vários módulos adicionais apenas para satisfazer todas as dependências. Você fica com um "trem" inteiro de módulos de código quando tudo o que você precisava era de um único "assento" (função).
Você acaba nessa situação quando seus módulos estão fortemente acoplados. Então, o que você pode fazer sobre isso? Existem algumas maneiras de lidar com essa situação.
Violar "não se repita"
Uma maneira de preservar o acoplamento fraco - o que resulta em módulos mais "independentes" - é criar cópias privadas das funções do módulo de origem no módulo de chamada. Isso elimina a dependência entre os dois módulos, mas resulta em código repetitivo.
O conceito é simples. Você copia a função de seu módulo de código primário. Você então o cola no módulo de chamada, mas o marca como privado para evitar ambiguidade.
Isso faz sentido nas seguintes situações:
- Métodos simples sem lógica complexa.
- Procedimentos que provavelmente não serão alterados.
- Quando a rotina faz parte de um módulo muito maior e você não precisa de nenhuma das outras funções do módulo. Copiar uma função evita o inchaço.
Essa abordagem tem as seguintes desvantagens:
- Se houver existir um bug na função copiada, você terá que corrigi-lo em muitos lugares.
- Você terá duplicação de código se acabar importando o módulo de origem de qualquer maneira.
Escolha suas batalhas
Eu costumava me esforçar muito para manter todos os meus módulos de biblioteca de código padrão completamente independentes. O problema era que isso resultava em muita duplicação de código. A razão é que a maioria das funções que eu estava copiando para outros módulos para uso privado vinham de módulos que eu estava importando para meu aplicativo de qualquer maneira.
Um excelente exemplo disso foi meu StringFunctions módulo. Esse módulo tem vários métodos simples que existem em grande parte para tornar meu código mais legível. Por exemplo, eu tenho um
Conc()
função que eu estava incluindo como uma função privada em mais da metade dos meus módulos de biblioteca de código. Com o tempo, percebi que incluía StringFunctions módulo em todos os meus projetos. Eu nunca estava introduzindo uma nova dependência quando chamei uma função desse módulo. Eu estava perdendo tempo e introduzindo código duplicado para pouco ou nenhum benefício.
Havia alguns módulos de código que eu poderia assumir com segurança que estariam em todos os aplicativos. Esses foram os módulos com funções que usei com mais frequência. O que significava que muitas dessas dependências poderiam ser essencialmente ignoradas.
Agora mantenho uma "Biblioteca Padrão" de módulos de código que importo para cada novo projeto no início. Eu chamo livremente funções desses módulos agora com a certeza de que não estarei introduzindo novas dependências.
Use um token de comentário exclusivo
Um dos módulos na minha "Biblioteca Padrão" é um módulo de classe (clsApp ) que inclui propriedades e métodos de nível de aplicativo, como o nome de usuário atual e o texto da barra de título. Também exponho outros módulos de classe de dentro do clsApp , como clsStatus e clsRegistry , que fornecem acesso mais legível à barra de status do Access e ao registro do Windows, respectivamente.
No entanto, não preciso acessar a barra de status ou o registro do Windows em todos os projetos. Portanto, para evitar criar uma dependência no clsStatus ou clsRegistry classes, eu comentaria o código referenciando essas classes usando um "token de comentário" exclusivo.
Isso é mais fácil de demonstrar com um exemplo:
' Notes
' - Find and replace '$$ with blank to enable Status property (requires clsStatus)
' - Find and replace '&& with blank to enable Reg property (requires clsRegistry)
'$$Private m_objStatus As clsStatus
'&&Private m_objReg As clsRegistry
'$$Public Property Get Status() As clsStatus
'$$ Set Status = m_objStatus
'$$End Property
'&&Public Property Get Reg() As clsRegistry
'&& Set Reg = m_objReg
'&&End Property
Private Sub Class_Initialize()
'$$ Set m_objStatus = New clsStatus
'&& Set m_objReg = New clsRegistry
End Sub
Se eu quisesse habilitar o
Status
propriedade da classe acima, eu poderia realizar uma busca global e substituir em '$$
. Isso funcionou bem por um tempo, mas sempre me pareceu estranho. Provavelmente porque era. A outra coisa a ser observada é que os tokens de comentário precisariam ser globalmente exclusivos em toda a minha biblioteca de código. Isso teria sido um pesadelo de manutenção se eu mantivesse essa abordagem por muito tempo.
Usar compilação condicional
Uma abordagem muito mais limpa é aproveitar a compilação condicional. Essas são as linhas no VBA que começam com um sinal de libra/hashtag ("#"). As linhas que começam com esse caractere estão sujeitas a "pré-processamento".
O que é pré-processamento? Esse é um passo que as linguagens de programação dão antes da compilação. Portanto, antes que ocorra qualquer verificação em tempo de compilação, as linhas de pré-processamento são avaliadas. Isso nos permite colocar código que, de outra forma, não seria compilado em nossos projetos.
Como podemos tirar proveito disso com nossas bibliotecas de código? Novamente, isso é mais simples de demonstrar com um exemplo:
' Notes
' - Replace the '$$ and '&& kludges with conditional compilation
#Const EnableStatusProperty = True 'If True, requires import of clsStatus class
#Const EnableRegProperty = False 'If True, requires import of clsRegistry class
#If EnableStatusProperty Then
Private m_objStatus As clsStatus
#End If
#If EnableRegProperty Then
Private m_objReg As clsRegistry
#End If
#If EnableStatusProperty Then
Public Property Get Status() As clsStatus
Set Status = m_objStatus
End Property
#End If
#If EnableRegProperty Then
Public Property Get Reg() As clsRegistry
Set Reg = m_objReg
End Property
#End If
Private Sub Class_Initialize()
#If EnableStatusProperty Then
Set m_objStatus = New clsStatus
#End If
#If EnableRegProperty Then
Set m_objReg = New clsRegistry
#End If
End Sub
O melhor dos dois mundos
Como você pode ver, esta é uma maneira muito limpa de evitar o problema do "Trem da Dependência".
Ele nos permite criar dependências opcionais . Cada pedaço de código que depende de outro módulo de biblioteca de código é encapsulado dentro de uma compilação condicional #If ... Then instrução. As constantes de compilação condicional estão todas listadas na parte superior do módulo de código.
Agora, quando reimportamos uma versão atualizada do nosso módulo de biblioteca de código, simplesmente temos que passar e definir os sinalizadores de compilação condicional para o que havia antes. Se não nos lembrarmos de como os sinalizadores foram definidos, poderemos continuar compilando e ajustando os sinalizadores até que o projeto seja totalmente compilado.
E se estivermos usando controle de versão, não precisamos nos preocupar em esquecer o que havia antes.