SOLID PRINCIPLES IN C++
The SOLID principles are a set of object-oriented design principles that have completely changed the way we develop software. They are a set of standards and recommended practices for creating clean, flexible, reusable, scalable and maintainable code.
Robert C Martin, popularly known as "Uncle Bob", established the SOLID design principles in his 2000 article Design Principles and Design Patterns. However, Michael Feathers later developed the term. The SOLID concept helps mitigate tight coupling. Tightly coupled describes a set of classes that are highly interdependent and should be avoided in your code. The opposite of tight coupling is loose coupling, and code is considered good if it contains loosely coupled classes.
![SOLID PRINCIPLES IN C++](https://static.tutorialandexample.com/cpp/solid-principles-in-cpp1.png)
SOLID Design Principles in Software Engineering
The term SOLID in SOLID design principles is an acronym for five principles, they are :
- S : Single-responsibility Principle
- O : Open-closed Principle
- L : Liskov Substitution Principle
- I : Interface Segregation Principle
- D : Dependency Inversion Principle
These SOLID design principles provide practises for creating software with maintenance and expansion in mind as the project grows. Adopting these strategies will assist you in avoiding code smells, refactoring, and producing Agile software. Let's take a closer look at each of these ideas.
SRP (Single Responsibility Principle)
Simply expressed, the SOLID design principles of single responsibility dictate that a class, module, or function should only have one duty. According to the most prevalent definition, each class should have just one cause to change. If a class has logic for doing several tasks, it will be difficult to debug and manage as the codebase grows in size. Code is difficult for new developers to read and understand. Separating the logic and allowing each class to perform a single task not only makes the code easier to maintain and debug, but also reusable because each basic functionality is provided in a single class will be Code maintenance is very easy.
Principle of Open/Closed
According to the SOLID design principles' open-closed concept, software entities should be open for expansion but closed for modification. This means that these entities should be designed so that their functionality can be extended to other entities without changing the original entity's code. If developer A needs to publish an update to a library or framework, and developer B wants to change or add functionality, developer B can extend the current class that developer A created, but Classes cannot be edited directly. This technique separates old code from updated code, improving stability and maintainability while minimizing code changes.
Let us understand this principle with an example. Suppose we have implemented an 'Oven' class.
Syntax:
public class Oven {
private String make;
private String model;
private int temperature;
private int time;
//Constructors, getters & setters
After we deploy this software, we aim to add a grilling capability to this oven. We can change this oven class, but doing so may cause the entire codebase to break. As a result, the open-closed concept works against this. Instead, we should broaden the scope of this class:
Syntax:
public class OvenWithGrill extends Oven {
private String make;
private String model;
private int temperature;
private int time;
bool isGrillOn;
//Constructors, getters & setters
}
LSP (Liskov's Substitution Principle)
The Liskov substitution principle, as defined by Barbara Liskov and Jeannette Wing, asserts that:
Let Φ(x) be a provable property of an object x of type T. Then Φ(y) applies to an object y of type S. where S is a subtype of T. A simple definition is to extend the open/close concept to replace a parent class object with a child class object while the application continues to run. Therefore, all subclasses should behave like the parent class. To do this, subclasses should follow these guidelines: Child class input parameters should not have stricter validation criteria than parent class input parameters.
ISP stands for Interface Segregation Principle.
The I in SOLID design principles denotes interface segregation, which means that larger interfaces should be separated into smaller ones. We may guarantee that implementing classes are only concerned with the methods that are relevant to them by doing so, In other words, multiple client-specific interfaces are preferable to a single generic interface. Clients should not force the implementation of unused interfaces or rely on unused methods. For example, it doesn't make sense to implement the volume interface on the Square and Circle classes, which are two-dimensional objects.
PROGRAMME:
#include <iostream>
#include <vector>
class Shape {
public:
virtual double area() const = 0;
};
class Circle : public Shape {
public:
Circle(double radius) : radius(radius) {}
double area() const override {
return 3.14159 * radius * radius;
}
private:
double radius;
};
class Rectangle : public Shape {
public:
Rectangle(double width, double height) : width(width), height(height) {}
double area() const override {
return width * height;
}
private:
double width, height;
};
int main() {
std::vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));
double totalArea = 0.0;
for (const auto shape : shapes) {
totalArea += shape->area();
delete shape;
}
std::cout << "Total area: " << totalArea << std::endl;
return 0;
}
}
Output:
![SOLID PRINCIPLES IN C++](https://static.tutorialandexample.com/cpp/solid-principles-in-cpp2.png)
The code shows the OCP by allowing for the addition of additional forms without affecting the existing code that calculates the overall area.
3. Liskov Substitution Principle (LSP):
#include <iostream>
class Bird {
public:
virtual void fly() const {
std::cout << "Bird is flying" << std::endl;
}
};
class Sparrow : public Bird {
public:
void fly() const override {
std::cout << "Sparrow is flying" << std::endl;
}
};
void makeBirdFly(const Bird& bird) {
bird.fly();
}
int main() {
Bird bird;
Sparrow sparrow;
makeBirdFly(bird);
makeBirdFly(sparrow);
return 0;
}
Output:
![SOLID PRINCIPLES IN C++](https://static.tutorialandexample.com/cpp/solid-principles-in-cpp3.png)
The code adheres to LSP because the Sparrow class, a subclass of Bird, may be substituted for the base Bird class without affecting the program's behaviour.
4.Interface Segregation Principle (ISP):
#include <iostream>
class Printable {
public:
virtual void print() const = 0;
};
class Document : public Printable {
public:
void print() const override {
std::cout << "Printing document..." << std::endl;
}
};
class Photo : public Printable {
public:
void print() const override {
std::cout << "Printing photo..." << std::endl;
}
};
int main() {
Document document;
Photo photo;
Printable* printableObjects[] = { &document, &photo };
for (const auto printable : printableObjects) {
printable->print();
}
Return;
}
Output:
![SOLID PRINCIPLES IN C++](https://static.tutorialandexample.com/cpp/solid-principles-in-cpp4.png)
5. Dependency Inversion Principle (DIP):
#include <iostream>
class Logger {
public:
virtual void log(const std::string& message) = 0;
};
class ConsoleLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "Log: " << message << std::endl;
}
};
class App {
private:
Logger* logger;
public:
App(Logger* logger) : logger(logger) {}
void run() {
// Application logic
logger->log("Application started");
}
};
int main() {
ConsoleLogger consoleLogger;
App app(&consoleLogger);
app.run();
return 0;
}
Output:
![SOLID PRINCIPLES IN C++](https://static.tutorialandexample.com/cpp/solid-principles-in-cpp5.png)
Conclusion:
SOLID stands for five key design principles: the single responsibility concept, the open-closed principle, the Liskov substitution principle, the interface segregation principle, and the dependency inversion principle.
The primary goal of the SOLID design principles is to reduce dependencies so that software developers may change one area of software without impacting others. They are also intended to make designs simpler to comprehend, maintain, and extend. Finally, employing these design principles assists software developers in avoiding difficulties and developing adaptable, maintainable, and agile software.