Os madeireiros são o que chamamos de "preocupação transversal". Eles cedem a técnicas como Programação Orientada a Aspectos; se você tem uma maneira de decorar suas classes com um atributo ou executar alguma tecelagem de código, é uma boa maneira de obter recursos de registro, mantendo seus objetos e listas de parâmetros "puros".
O único motivo para você querer passar um criador de logs é se você deseja especificar diferentes implementações de log, mas a maioria das estruturas de log possui a flexibilidade de permitir que você as configure, por exemplo, para diferentes destinos de log. (arquivo de log, Windows Event Manager, etc.)
Por esses motivos, prefiro tornar o log uma parte natural do sistema, em vez de passar um log para todas as classes para fins de log. Então, o que geralmente faço é referenciar o espaço de nome de log apropriado e simplesmente usar o logger em minhas classes.
Se você ainda deseja passar um logger, minha preferência é torná-lo o último parâmetro na lista de parâmetros (torne-o um parâmetro opcional, se possível). Ser o primeiro parâmetro não faz muito sentido; o primeiro parâmetro deve ser o mais importante, o mais relevante para a operação da classe.
Nas linguagens com sobrecarga de funções, eu argumentaria que, quanto mais provável um argumento for opcional, mais certo deverá ser. Isso cria consistência quando você cria sobrecargas onde elas estão ausentes:
Nas linguagens funcionais, o inverso é mais útil - quanto maior a probabilidade de você escolher algum padrão, mais resta deve ficar. Isso facilita a especialização da função simplesmente aplicando argumentos a ela:
No entanto, conforme mencionado nas outras respostas, você provavelmente não deseja passar explicitamente o criador de logs na lista de argumentos de todas as classes no sistema.
fonte
Definitivamente, você pode dedicar muito tempo à engenharia deste problema.
Para idiomas com implementações de log canônico, instancie o logon canônico diretamente em todas as classes.
Para idiomas sem uma implementação canônica, tente encontrar uma estrutura de fachada de log e atenha-se a ela. slf4j é uma boa escolha em Java.
Pessoalmente, prefiro manter uma única implementação concreta de log e enviar tudo para o syslog. Todas as boas ferramentas de análise de log são capazes de combinar logs de sysout de vários servidores de aplicativos em um relatório abrangente.
Quando uma assinatura de função inclui um ou dois serviços de dependência, além de alguns argumentos "reais", coloco as dependências por último:
int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)
Como meus sistemas tendem a ter apenas cinco ou menos desses serviços, sempre asseguro que os serviços sejam incluídos na mesma ordem em todas as assinaturas de funções. A ordem alfabética é tão boa quanto qualquer outra. (Além disso: a manutenção dessa abordagem metodológica para o manuseio de mutex também reduzirá suas chances de desenvolver impasses.)
Se você estiver injetando mais de uma dúzia de dependências em seu aplicativo, o sistema provavelmente precisará ser dividido em subsistemas separados (ouso dizer microsserviços?).
fonte
Os madeireiros são um caso especial porque precisam estar disponíveis literalmente em qualquer lugar.
Se você decidiu passar um logger para o construtor de cada classe, definitivamente deve definir uma convenção consistente sobre como fazer isso (por exemplo, sempre o primeiro parâmetro, sempre passado por referência, a lista de inicialização do construtor sempre começa com m_logger (theLogger), etc). Qualquer coisa que será usada em toda a sua base de código se beneficiará da consistência algum dia.
Como alternativa, todas as classes podem instanciar seu próprio objeto de logger, sem precisar que nada seja passado. O logger pode precisar saber algumas coisas "por mágica" para que isso funcione, mas codificar um caminho de arquivo na definição de classe é potencialmente um muito mais sustentável e menos tedioso do que passá-lo corretamente para centenas de classes diferentes e, sem dúvida, muito menos maligno do que usar uma variável global para contornar o referido tédio. (É certo que os registradores estão entre os poucos casos de uso legítimos para variáveis globais)
fonte
Concordo com aqueles que sugerem que o criador de logs deve ser acessado estaticamente, e não passado para as classes. No entanto, se há uma forte razão que você quer passá-lo em (casos talvez diferentes quer registrar para locais diferentes ou algo assim), então eu sugiro que você não passá-lo usando o construtor, mas sim fazer uma chamada separada para o fazer, por exemplo,
Class* C = new C(); C->SetLogger(logger);
em vez do queClass* C = new C(logger);
A razão para preferir esse método é que o criador de logs não é essencialmente uma parte da classe, mas um recurso injetado usado para algum outro propósito. Colocá-lo na lista de construtores o torna um requisito da classe e implica que faz parte do estado lógico real da classe. É razoável esperar, por exemplo (com a maioria das classes, embora não todas), que, se houver
X != Y
,C(X) != C(Y)
mas é improvável que você teste a desigualdade do criador de logs se estiver comparando também instâncias da mesma classe.fonte
Vale a pena mencionar, algo que eu não vi as outras respostas abordar aqui, é que, ao fazer o logger injetado via propriedade ou estática, torna mais difícil testar a unidade. Por exemplo, se você injetar seu logger via propriedade, agora precisará injetar esse logger sempre que testar um método que usa o logger. Isso significa que você pode configurá-lo como uma dependência do construtor, porque a classe exige isso.
A estática se presta ao mesmo problema; se o criador de logs não funcionar, sua classe inteira falhará (se a sua classe usar o criador de logs) - mesmo que o criador de log não seja necessariamente 'parte' da responsabilidade da classe -, embora não seja tão ruim quanto a injeção de propriedade porque você pelo menos saiba que o logger está sempre "lá" em um sentido.
Apenas um pouco de reflexão, especialmente se você usar TDD. Na minha opinião, um criador de logs não deve realmente fazer parte de uma parte testável de uma classe (quando você testa a classe, você não deve testar também o seu log).
fonte
Estou com preguiça de passar um objeto logger para cada instância da classe. Portanto, no meu código, esse tipo de coisa fica em um campo estático ou em uma variável local de thread em um campo estático. O último é meio legal e permite que você use um logger diferente para cada thread e adicione métodos para ativar e desativar o log que fazem algo significativo e esperado em um aplicativo multiencadeado.
fonte