DIP, Dependency Inversion Principle
Intent
- Use an interface of an abstract class.
Explain
One of Object Oriented Programming(OOP) principles is Single Responsibility Principle(SRP), a class has one responsibility. Therefore, each feature should be distributed in multiple classes. Code 1 shows simple example.
// 01_C_TightlyCoupling.cpp
#include <iostream>
using namespace std;
class Vim
{
public:
void write(string& code)
{
cout << code << endl;
}
};
class Developer
{
public:
void writeCode(Vim* p, string code)
{
p->write(code);
}
};
int main()
{
Developer developer;
Vim vim;
developer.writeCode(&vim, "Hello, World!");
return 0;
}
// Compile: clang++ -std=c++14
// -o 01_C_TightlyCoupling 01_C_TightlyCoupling.cpp
Hello, World!
There are two classes, Vim and Developer. Responsibility to write a code is implemented in Vim class, and Developer class has a responsibility to trigger Vim instance to write a code with its pointer.
However, if Developer wants to write a code with another class, such as Emacs, the callers should be changed, main() and Developer. The reason is that they use the pointer of Vim directly. In this case, we say they are tightly coupled.
To make program flexible, an interface is necessary. An interface is a pure virtual function defined in abstract class. Code 2 shows example of an interface.
// 02_C_LooselyCoupling
#include <iostream>
using namespace std;
class IEditor
{
public:
virtual void write(string& code) = 0;
virtual ~IEditor() {}
};
class Developer
{
public:
void writeCode(IEditor* p, string code)
{
p->write(code);
}
};
class Vim : public IEditor
{
public:
virtual void write(string& code)
{
cout << "Vim: " << code << endl;
}
};
class Emacs : public IEditor
{
public:
void write(string& code)
{
cout << "Emacs: " << code << endl;
}
};
int main()
{
Developer developer;
Vim vim;
Emacs emacs;
developer.writeCode(&vim, "Hello, World!");
developer.writeCode(&emacs, "Hello, World!");
return 0;
}
// Compile: clang++ -std=c++14
// -o 02_C_LooselyCoupling 02_C_LooselyCoupling.cpp
Vim: Hello, World!
Emacs: Hello, World!
IEditor is an abstract class defining interfaces, and Vim and Emacs are concrete classes inheriting abstract class. The concrete classes overrides and have implement of interfaces. As a result, Developer can use the concrete classes with the unified way, the pointer of IEditor class. Additionally, a caller does not need to consider new class which inherits the abstract class. In this case, we call they are loosely coupled.
This design is based on Dependency Inversion Principe(DIP). DIP guides a caller use an interface of an abstract class. Because of SRP, each class has its own responsibility. However, without an interface, it is not easy to support all of them. Fortunately, C++ supports DIP by providing an interface and an abstract class, so we can simply handle them with the pointer of abstract class.
Summary
- DIP guides using an abstract class and an interface, instead of concrete class directly.
- Interface and abstract class make loosely coupling, so a program becomes flexible.
COMMENTS