C++ solid
Date: 2023-02-18Last modified: 2024-11-02
Table of contents
SOLID Principles
- Single Responsibility Principle
- Open Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Single Responsibility Principle
A class should have only one reason to change
- Should have only one responsibility
- Classes with multiple responsibilities break when changed
- Put each responsibility in a separate class
Before:
class Notes {
public:
void Add() {}
void Remove() {}
void Display() {}
};
After
class Notes {
public:
void Add() {}
void Remove() {}
};
Trasfer the responsability to display Notes to view class
class View {
public:
void Display(Notes *notes) {
// implementation
}
};
Open-Closed Principle
Modules should be open for extension but closed for modification
- Modification to existing code leads to bugs and causes the software to
- it should be possible to change behavior of existing code without
- it the behavior should be changed by adding new code
- Cornerstone of good design
- Use design patterns
This principle explains why we should write software in such a way that its behavior can be modified in future without changing existing code.
Liskov-Substitution Principle
Subtypes must be substitutable for their base types
- Applies to inheritance relationship
- The inheritance relationship should be based on behavior
- A subclass must have all the behaviors of its base type & must not remove
- This allows a subclass to replace its base type in code
- New subclasses can be added without modifying existing code
Learn how inheritance should be used correctly through the Liskov
Interface Segregation Principle
Clients should not be forced to depend on methods they do not use
- An interface with too many methods will be complex to use (called fat
- Some clients may not use all the methods
- But will be forced to depend on them
- Separate the interface and put methods based on the client usage
This principle deals with fat interfaces.
Before:
struct IFile {
virtual void Read() = 0;
virtual void Write() = 0;
virtual ~IFile() = default;
};
After:
struct IRead {
virtual void Read() = 0;
virtual ~IRead() = default;
};
struct IWrite {
virtual void Write() = 0;
virtual ~IWrite() = default;
};
Dependency Inversion Principle
Abstractions should not depend on details. Details should depend on
- Abstraction means an interface and details mean classes
- Using a concrete class directly creates a dependency
- Software becomes difficult to modify
- Invert the dependency by using an interface rather a concrete class
A very important principle that forms the flexible structure of all class
Before:
class ImageReader {
public:
virtual void Decode() = 0;
virtual ~ImageReader() = default;
};
class BitmapReader : public ImageReader {
public:
void Decode();
};
class ImageViewer {
BitmapReader *m_Reader{}; // depends on concrete class
public:
void Display() {}
};
After
class ImageViewer {
ImageReader *m_Reader{}; // depends on interface
public:
void Display() {}
};
Possible output