Rather than having a reusable code, you have a code which is interchangeable.
A real life example using the DI and Adapter patters. Let's say you have a class, which handles data. It does not retrieve the data, but handles it. We could simply call it DataHandler.
class DataHandler { public: void doSomethingWithData() {} protected: private: };
When you start using the DataHandler in your app, you use it to handle data from a SQL Database. So the DataHandler will need an instance of said class.
class SqlDatabaseDataRetriever { public: Data getData() { } protected: private: }; class DataHandler { public: DataHandler(SqlDatabaseDataRetriever& dataRetriever) { _dataRetriever = dataRetriever; } void doSomethingWithData() { Data data = this->_dataRetriever.getData(); } protected: SqlDatabaseDataRetriever _dataRetriever; private: };
But what if later you decide, you don't want to access the database directly via SqlDatabaseDataRetriever, so you create an API and you create new class which is used to handle the request via your API and retrieves data from it.
So we want to use the DataHandler class with our new ApiDataRetriever, but currently our DataHandler uses a different class. You could just rewrite your code, inject the ApiDataRetriever instead, but there's a better solution.
You will create an abstract class, which all your DataRetrievers will inherit, you will inject this abstract class instead and use it's methods.
class DataRetriever { public: virtual Data getData() = 0; protected: private; } class SqlDatabaseDataRetriever : public DataRetriever { public: virtual Data getData() { } protected: private; } class ApiDataRetriever : public DataRetriever { public: virtual Data getData() { } protected: private; } class DataHandler { public: DataHandler(DataRetriever& dataRetriever) { _dataRetriever = dataRetriever; } void doSomethingWithData() { Data data = this->_dataRetriever.getData(); } protected: DataRetriever _dataRetriever; private: };
Because the function in parent class is defined as pure virtual, it has to be implemented in child class, and because you are injecting the parent class, but that cannot be instantiated, you will always get a subject class having the parent method, which you can use without constraints.