In C++ utilizzo spesso oggetti in stile RAII per rendere il codice più affidabile e allocarli sullo stack per rendere il codice più performante (e per evitare bad_alloc).Stack assegnato agli oggetti RAII rispetto al principio DI
Ma la creazione di un oggetto di classe concreta su stack viola il principio di inversione di dipendenza (DI) e impedisce di deridere questo oggetto.
Si consideri il seguente codice:
struct IInputStream
{
virtual vector<BYTE> read(size_t n) = 0;
};
class Connection : public IInputStream
{
public:
Connection(string address);
virtual vector<BYTE> read(size_t n) override;
};
struct IBar
{
virtual void process(IInputStream& stream) = 0;
};
void Some::foo(string address, IBar& bar)
{
onBeforeConnectionCreated();
{
Connection conn(address);
onConnectionCreated();
bar.process(conn);
}
onConnectionClosed();
}
posso testare IBar::process
, ma voglio anche mettere alla prova Some::foo
, senza creare oggetto Connection reale.
Sicuramente posso utilizzare una fabbrica, ma complicherà notevolmente il codice e introdurre l'allocazione dell'heap.
Inoltre, non mi piace aggiungere il metodo Connection::open
, preferisco costruire oggetti completamente inizializzati e completamente funzionali.
vorrei fare Connection
tipo un parametro di modello per Some
(o per foo
se estrarlo come una funzione gratuita), ma non sono sicuro che sia giusto modo (i modelli sembrano una magia nera per molte persone, in modo da preferisco usare il polimorfismo dinamico)
I modelli non dovrebbero essere una magia nera per programmatori C++ più o meno competenti, non vedo alcun motivo per evitarli.Inoltre, non penso che l'allocazione dell'heap sia * quella * costosa (questo, ovviamente, dipende dal software che scrivi), quindi non vedo alcun motivo per evitarlo (quando usato con puntatori intelligenti). –
@Alex B: c'è una ragione per evitarli, anche se sono d'accordo che non è perché sono "magia nera". È perché se tutto viene iniettato tramite i parametri del modello, quindi tutto ciò che scrivi è un modello, la tua libreria è solo di intestazione e questo può diventare piuttosto ingombrante in termini di compilazione o distribuzione. Anche se, suppongo che con attenzione si possa testare unitamente la libreria di sola intestazione, quindi costruire da essa una TU che contenga solo le istanze richieste dall'applicazione. –
RAII e DI funzionano bene insieme, quindi il titolo è fuorviante, il problema è Stack Allocation vs DI. –