Sou bastante pragmático, mas minha principal preocupação aqui é que você possa permitir que isso ConfigBlock
domine seus designs de interface de uma maneira possivelmente ruim. Quando você tem algo parecido com isto:
explicit MyGreatClass(const ConfigBlock& config);
... uma interface mais apropriada pode ser assim:
MyGreatClass(int foo, float bar, const string& baz);
... em vez de apenas escolher esses foo/bar/baz
campos de uma forma massivaConfigBlock
.
Design de Interface Preguiçoso
No lado positivo, esse tipo de design facilita o design de uma interface estável para o construtor, por exemplo, se você precisar de algo novo, basta carregá-lo em um ConfigBlock
(possivelmente sem nenhuma alteração no código) e, em seguida, escolha qualquer coisa nova que você precise sem nenhum tipo de alteração na interface, apenas uma alteração na implementação do MyGreatClass
.
Portanto, é um tanto prós e contras que isso o libera de projetar uma interface mais cuidadosa que aceita apenas as entradas de que realmente precisa. Ele aplica a mentalidade de "Apenas me dê esse enorme blob de dados, selecionarei o que preciso deles" em oposição a algo mais como preciso deles "Esses parâmetros precisos são o que essa interface precisa para funcionar".
Definitivamente, existem alguns profissionais aqui, mas eles podem ser superados pelos contras.
Acoplamento
Nesse cenário, todas essas classes sendo construídas a partir de uma ConfigBlock
instância acabam tendo suas dependências da seguinte maneira:
Isso pode se tornar uma PITA, por exemplo, se você quiser testar a unidade Class2
neste diagrama isoladamente. Pode ser necessário simular superficialmente várias ConfigBlock
entradas que contêm os campos relevantesClass2
está interessado para poder testá-lo sob uma variedade de condições.
Em qualquer tipo de novo contexto (seja teste de unidade ou projeto totalmente novo), essas classes podem acabar se tornando mais um fardo para (re) usar, pois acabamos sempre trazendo ConfigBlock
consigo o passeio e configurando-o adequadamente.
Reutilização / Implantação / Testabilidade
Em vez disso, se você projetar essas interfaces adequadamente, podemos dissociá-las ConfigBlock
e terminar com algo assim:
Se você observar neste diagrama acima, todas as classes se tornam independentes (seus acoplamentos aferentes / de saída são reduzidos em 1).
Isso leva a muito mais classes independentes (pelo menos independentes de ConfigBlock
), que podem ser muito mais fáceis de (re) usar / testar em novos cenários / projetos.
Agora, esse Client
código acaba sendo aquele que precisa depender de tudo e montar tudo junto. A carga acaba sendo transferida para esse código do cliente para ler os campos apropriados de a ConfigBlock
e passá-los para as classes apropriadas como parâmetros. No entanto, esse código do cliente geralmente é projetado de maneira restrita para um contexto específico, e seu potencial de reutilização normalmente será fechado ou fechado de qualquer maneira (pode ser a main
função do ponto de entrada do aplicativo ou algo parecido).
Portanto, do ponto de vista de reutilização e teste, pode ajudar a tornar essas classes mais independentes. Do ponto de vista da interface para aqueles que usam suas classes, também pode ajudar a declarar explicitamente quais parâmetros eles precisam, em vez de apenas um maciço ConfigBlock
que modela todo o universo de campos de dados necessários para tudo.
Conclusão
Em geral, esse tipo de design orientado a classe que depende de um monólito que possui tudo o que é necessário tende a ter esses tipos de características. Sua aplicabilidade, implantação, reutilização, testabilidade etc. podem ficar significativamente degradadas como resultado. No entanto, eles podem simplificar o design da interface se tentarmos dar uma olhada positiva nela. Cabe a você medir esses prós e contras e decidir se as compensações valem a pena. Normalmente, é muito mais seguro errar nesse tipo de design em que você escolhe um monólito em classes que geralmente se destinam a modelar um design mais geral e amplamente aplicável.
Por último mas não menos importante:
extern CodingBlock MyCodingBlock;
... isso é potencialmente ainda pior (mais distorcido?) em termos das características descritas acima do que a abordagem de injeção de dependência, pois acaba acoplando suas classes não apenas ConfigBlocks
, mas diretamente a uma instância específica . Isso degrada ainda mais a aplicabilidade / implantação / testabilidade.
Meu conselho geral seria errar ao projetar interfaces que não dependem desses tipos de monólitos para fornecer seus parâmetros, pelo menos para as classes mais aplicáveis que você cria. E evite a abordagem global sem injeção de dependência, se puder, a menos que você tenha realmente uma razão muito forte e confiante para não evitá-la.
switch
instrução ouif
teste de instrução em relação a um valor lido nos arquivos de configuração.Sim. É melhor centralizar constantes e valores de tempo de execução e o código para lê-los.
Isso é ruim: a maioria dos seus construtores não precisará da maioria dos valores. Em vez disso, crie interfaces para tudo o que não é trivial de construir:
código antigo (sua proposta):
novo Código:
instanciar um MyGreatClass:
Aqui
current_config_block
está uma instância da suaConfigBlock
classe (a que contém todos os seus valores) e aMyGreatClass
classe recebe umaGreatClassData
instância. Em outras palavras, passe apenas para os construtores os dados de que precisam e adicione recursos ao seuConfigBlock
para criar esses dados.Este código sugere que você terá uma instância global de CodingBlock. Não faça isso: normalmente você deve ter uma instância declarada globalmente, em qualquer ponto de entrada que seu aplicativo use (função principal, DllMain, etc) e passar isso como um argumento para onde você precisar (mas, como explicado acima, você não deve passar toda a classe, apenas exponha interfaces em torno dos dados e passe-as).
Além disso, não vincule suas classes de clientes (suas
MyGreatClass
) ao tipo deCodingBlock
; Isso significa que, se vocêMyGreatClass
pegar uma string e cinco números inteiros, será melhor passar essa string e números inteiros do que passará aCodingBlock
.fonte
Resposta curta:
Você não precisa de todas as configurações para cada um dos módulos / classes no seu código. Se você o fizer, haverá algo errado com o design orientado a objetos. Especialmente no caso de teste de unidade, definir todas as variáveis que você não precisa e passar esse objeto não ajudaria na leitura ou manutenção.
fonte
ConfigBlock
classe. O ponto aqui é não fornecer todo, neste caso, o contexto do estado do sistema, apenas os valores necessários para fazê-lo.