So you know how to code in general, understand the object-oriented programming, learned C++, and completed at least one Software Development Course (if you're not there yet, these articles aren't for you). You can write software easily if you know at least one programming language, but is your code any good? Could it be done any better? Is it clean (and what on earth does that mean)? Is your architecture any good Should you use a different one? What about Design Patterns? These were some of the questions I've had when I started, and answering them helped me to step up to a professional level. Which is why I have written these series SOLID as a Rock design principle. Liskov's Substitution Principle in C++ is the second principle in this series which I will discuss here.
The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like override, final, public(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing "public:" sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.
Note:
Subtypes must be substitutable for their base types without altering the correctness of the program
struct Rectangle {
Rectangle(const uint32_t width, const uint32_t height) : m_width{width}, m_height{height} {}
uint32_t get_width() const { return m_width; }
uint32_t get_height() const { return m_height; }
virtual void set_width(const uint32_t width) { this->m_width = width; }
virtual void set_height(const uint32_t height) { this->m_height = height; }
uint32_t area() const { return m_width * m_height; }
protected:
uint32_t m_width, m_height;
};
struct Square : Rectangle {
Square(uint32_t size) : Rectangle(size, size) {}
void set_width(const uint32_t width) override { this->m_width = m_height = width; }
void set_height(const uint32_t height) override { this->m_height = m_width = height; }
};
void process(Rectangle &r) {
uint32_t w = r.get_width();
r.set_height(10);
assert((w * 10) == r.area()); // Fails for Square <--------------------
}
int main() {
Rectangle r{5, 5};
process(r);
Square s{5};
process(s);
return EXIT_SUCCESS;
}
void process(Rectangle &r) {
uint32_t w = r.get_width();
r.set_height(10);
if (dynamic_cast<Square *>(&r) != nullptr)
assert((r.get_width() * r.get_width()) == r.area());
else
assert((w * 10) == r.area());
}
void process(Rectangle &r) {
uint32_t w = r.get_width();
r.set_height(10);
if (r.is_square())
assert((r.get_width() * r.get_width()) == r.area());
else
assert((w * 10) == r.area());
}
struct Shape {
virtual uint32_t area() const = 0;
};
struct Rectangle : Shape {
Rectangle(const uint32_t width, const uint32_t height) : m_width{width}, m_height{height} {}
uint32_t get_width() const { return m_width; }
uint32_t get_height() const { return m_height; }
virtual void set_width(const uint32_t width) { this->m_width = width; }
virtual void set_height(const uint32_t height) { this->m_height = height; }
uint32_t area() const override { return m_width * m_height; }
private:
uint32_t m_width, m_height;
};
struct Square : Shape {
Square(uint32_t size) : m_size(size) {}
void set_size(const uint32_t size) { this->m_size = size; }
uint32_t area() const override { return m_size * m_size; }
private:
uint32_t m_size;
};
void process(Shape &s) {
// Use polymorphic behaviour only i.e. area()
}
struct ShapeFactory {
static Shape CreateRectangle(uint32_t width, uint32_t height);
static Shape CreateSquare(uint32_t size);
};
Don’t get me wrong, I like SOLID and the approaches it promotes. But it’s
just a shape of deeper principles lying in its foundation. The examples above made it clear what this principle is striving for i.e. loose coupling & ensuring correct inheritance.
Now, go out there and make your subclasses swappable, and thank Dr. Barbara Liskov for such a useful principle.
/!\: Originally published @ www.vishalchovatiya.com.
If you haven't gone through my previous articles on design principles, then below is the quick links: