Sono abbastanza pragmatico, ma la mia principale preoccupazione qui è che potresti consentire a questo ConfigBlock
di dominare i tuoi progetti di interfaccia in un modo probabilmente negativo. Quando hai qualcosa del genere:
explicit MyGreatClass(const ConfigBlock& config);
... un'interfaccia più appropriata potrebbe essere così:
MyGreatClass(int foo, float bar, const string& baz);
... al contrario della semplice raccolta di questi foo/bar/baz
campi da un campo enorme ConfigBlock
.
Lazy Interface Design
Tra i lati positivi, questo tipo di design semplifica la progettazione di un'interfaccia stabile per il tuo costruttore, ad esempio, dal momento che se hai bisogno di qualcosa di nuovo, puoi semplicemente caricarlo in un ConfigBlock
(possibilmente senza alcuna modifica del codice) e quindi ciliegia- scegli qualsiasi nuovo materiale di cui hai bisogno senza alcun tipo di modifica dell'interfaccia, solo una modifica all'implementazione di MyGreatClass
.
Quindi è sia un tipo di pro che un contro che questo ti libera di progettare un'interfaccia più attentamente pensata che accetta solo input di cui ha effettivamente bisogno. Applica la mentalità di "Dammi solo questo enorme blocco di dati, sceglierò ciò di cui ho bisogno da esso" invece di qualcosa di più simile a "Questi parametri precisi sono ciò di cui questa interfaccia ha bisogno per funzionare".
Quindi ci sono sicuramente alcuni pro qui, ma potrebbero essere pesantemente compensati dai contro.
accoppiamento
In questo scenario, tutte queste classi che vengono costruite da ConfigBlock
un'istanza finiscono per avere le loro dipendenze così:
Questo può diventare un PITA, ad esempio, se si desidera Class2
isolare l' unità in questo diagramma. Potrebbe essere necessario simulare superficialmente vari ConfigBlock
input contenenti i campi rilevanti a cui Class2
è interessato per poterlo testare in una varietà di condizioni.
In qualsiasi tipo di nuovo contesto (sia che si tratti di unit test o di un intero nuovo progetto), tali classi possono finire per diventare più un peso da (ri) utilizzare, poiché alla fine dobbiamo sempre portare ConfigBlock
con noi il viaggio e impostarlo di conseguenza.
Riutilizzabilità / schierabilità / Testability
Invece, se si progettano queste interfacce in modo appropriato, possiamo separarle ConfigBlock
e finire con qualcosa del genere:
Se noti in questo diagramma sopra, tutte le classi diventano indipendenti (i loro accoppiamenti afferenti / in uscita si riducono di 1).
Questo porta a classi molto più indipendenti (almeno indipendenti da ConfigBlock
) che possono essere molto più facili da (ri) usare / testare in nuovi scenari / progetti.
Ora questo Client
codice finisce per essere quello che deve dipendere da tutto e assemblare tutto insieme. L'onere finisce per essere trasferito a questo codice client per leggere i campi appropriati da a ConfigBlock
e passarli nelle classi appropriate come parametri. Tuttavia, tale codice client è generalmente progettato in modo restrittivo per un contesto specifico e il suo potenziale per il riutilizzo sarà in genere zilch o chiuso comunque (potrebbe essere la main
funzione del punto di ingresso dell'applicazione o qualcosa del genere).
Quindi, dal punto di vista della riutilizzabilità e dei test, può aiutare a rendere queste classi più indipendenti. Dal punto di vista dell'interfaccia per coloro che usano le tue classi, può anche aiutare a dichiarare esplicitamente quali parametri sono necessari invece di un solo enorme ConfigBlock
che modella l'intero universo di campi dati richiesti per tutto.
Conclusione
In generale, questo tipo di design orientato alla classe che dipende da un monolite che ha tutto il necessario tende ad avere questo tipo di caratteristiche. Di conseguenza, la loro applicabilità, schierabilità, riusabilità, testabilità, ecc. Possono subire un degrado significativo. Eppure possono in qualche modo semplificare il design dell'interfaccia se tentiamo una rotazione positiva su di esso. Sta a te misurare quei pro e contro e decidere se valgono i compromessi. In genere è molto più sicuro sbagliare contro questo tipo di design in cui stai scegliendo un monolite in classi che generalmente intendono modellare un design più generale e ampiamente applicabile.
Ultimo, ma non per importanza:
extern CodingBlock MyCodingBlock;
... questo è potenzialmente anche peggio (più distorto?) in termini di caratteristiche sopra descritte rispetto all'approccio dell'iniezione di dipendenza, in quanto finisce per accoppiare le tue classi non solo ConfigBlocks
, ma direttamente a una sua specifica istanza . Ciò degrada ulteriormente l'applicabilità / schierabilità / testabilità.
Il mio consiglio generale potrebbe errare sul lato della progettazione di interfacce che non dipendono da questo tipo di monoliti per fornire i loro parametri, almeno per le classi più generalmente applicabili progettate. Ed evita l'approccio globale senza iniezione di dipendenza se puoi, a meno che tu non abbia davvero una ragione molto forte e sicura di non evitarlo.