
Learn the solid five object-oriented design principles—single responsibility principle, open closed principle, Liskov substitution principle, interface segregation principle, and dependency inversion principle—to improve maintainability, flexibility, and understandability of code.
Explore why this course focuses on C++, a modern, powerful general-purpose language. Learn its object-oriented, generic, and functional features, dynamic and static polymorphism, and idioms that support solid principles.
Explore the CP Solid repository on GitHub, discover presentations, standalone C++ examples, the server app project, unit and high-level tests, and continuous integration via GitHub Actions.
This course presents a c++ server that receives json or xml tax reports over tcp, authenticates and authorizes clients, parses and stores reports, illustrated by a uml sequence diagram.
Examine the toy project's initial commit to understand its main.cpp structure, boost io context usage, tcp server setup, json and xml parsing, and per-connection session handling before applying SOLID principles.
Explore the latest solid refactor of a toy c++ project: a multi-library server built with cmake, featuring authentication, text service handling text reports, and xml/json parsers with unit tests.
Explains a synchronous, blocking tcp server model that creates a thread per connection, uses blocking reads, and contrasts with asynchronous designs for scalability.
Download the Windows solid c++ project from GitHub, clone the repository, open it in Visual Studio Code, install Git and dependencies, then compile the code on Windows.
Install the MinGW distribution with gcc, g++, and boost libraries, and set environment variables. Then compile and run hello world and examples with g++ and C++17 in a build directory.
Install and configure CMake on Windows, add it to the system path, generate makefiles with CMake, build the plant care example, link libraries, and run the executable.
Install Google test and Google Mock on Windows, clone the repository, build with CMake and g++, and copy lib and include files to enable unit tests.
On Windows, compile the project with CMake, ensure Boost and Google Test are found and linked, then build with make and run unit tests to confirm all tests pass.
Run high-level Python tests against the server application to validate text reports in JSON and XML over TCP. Build the project, start the executable, and run tests to confirm correctness.
download and clone the course repository via git on linux, install dependencies, and compile the solid principles code examples using vscode on ubuntu.
Install and configure C++ development dependencies on Linux, including gcc/g++, cmake, boost libraries, google test and google mock, with optional valgrind for testing.
Learn to build a C++ project with CMake, link Boost libraries, and run unit tests using Google Test and Google Mock, illustrated by a plant care and server example.
Learn to run high-level integration tests for a server application using Python3, launching the app, connecting over TCP, and validating JSON and XML reports.
Explore unified modeling language UML and its diagrams, including class diagrams, sequence diagrams, state machines, and physical diagrams, and learn how UML documents software architecture and communicates design decisions.
Class diagrams use rectangles for classes and arrows for relationships, with attributes, operations, and visibility. Cover association, inheritance, realization, dependency, and composition or aggregation, noting composition ownership and lifetime.
Build UML class diagrams with Umbrello, defining classes, attributes, and operations; model relationships like generalization, association, and dependency; specify parameters, visibility, and export diagrams.
Adopt the single responsibility principle to ensure every class, function, and module has one clear purpose, a single reason to change, and fewer dependencies for easier maintenance and testing.
Apply the single responsibility principle by keeping modules and functions simple and focused on one purpose, using divide and conquer, and refactoring to maintain cohesion and balance.
Explore a visual analogy of the single responsibility principle, showing how a pit-stop team assigns one task per member and software uses separate classes for one purpose.
Explore applying the single responsibility principle by refactoring a database class into separate connection, cursor, and records/queries handling components.
The text editor example violates the single responsibility principle by mixing text manipulation, display, printing, and conversions in one class. Separate concerns into dedicated classes to improve cohesion.
Examine how a plant care system violates the single responsibility principle by combining sensors, actions, and state updates, and learn refactoring ideas for modular, testable, flexible design.
Refactor the plant care example to enforce single responsibility by separating sensors and caretakers into distinct classes, with moisture and sunlight sensors using dedicated loops and a mutex for safety.
Refactor the initial commit toward the single responsibility principle by extracting configuration and json/xml parsing logic from main into modules, and isolate the reporting session for easier testing.
Applies single responsibility principle and refactors into modular static libraries, making main minimal and enabling unit-tested startup configuration with boost program options.
Apply the open for extension, closed for modification principle by extending behavior without modifying source code, reducing coupling, preserving tests, and speeding compilation in C++ projects.
learn to keep code open to extension and closed to modification using dynamic polymorphism, abstract base classes, templates, and patterns like decorator, adapter, and facade.
Apply the open-closed principle judiciously; closure is impossible and premature abstraction adds complexity. Refactor when changes arise, using dynamic or static polymorphism only where domain knowledge predicts changes.
Explore the open-closed principle with a visual cartridge analogy, showing how a device is open for extension yet closed for modification through abstraction and interface.
Explore UML class diagrams and the open-closed principle by contrasting concrete button dependencies with abstract button interfaces, showing how polymorphism enables extension without modifying form window.
Learn dynamic polymorphism and inheritance to apply the open-closed principle in c++, using an abstract animal interface with a single speak method implemented by dog and cat.
Explore static polymorphism with templates as an alternative to inheritance, using a speak function that calls each type's speak at compile time for open-closed design.
Explore template specialization in c++, applying the open closed principle by extending graph algorithm behavior for directed and undirected graphs without modifying the core class.
Explore using templates and the open closed principle with c++20 concepts like flyable and swimmable, demonstrated by Bird and Fish selecting the appropriate action.
Demonstrate the dynamic decorator pattern to extend dog and cat behavior without changing their types, aligning code with the open-closed principle.
Explore the static decorator pattern for the open-closed principle in C++20. See how it decorates dog and cat speak behavior using a base speakable type and variadic templates.
learn how the template method pattern lets subclasses implement step a and override the hook to extend base behavior while preserving the algorithm and the open-closed principle.
Explore composition versus inheritance in the open-closed principle, showing how a player composes delegate objects to vary behavior without relying on inheritance.
Explore the runtime concept idiom, enabling polymorphism via type erasure with wrapper, concept, and model. Wrap dog and cat in a vector to demonstrate open-closed extension.
Explore applying the open-closed principle by refactoring report session and text service to depend on abstractions, extract parsing logic into interfaces, and enable mocking for unit testing.
Explore how open closed principle and single responsibility principle drive refactoring of a cpp project by introducing abstract text service, a report parser interface, and a factory driven parser selection.
Explore the Liskov substitution principle, showing that subtypes must be substitutable for base types and that replacing base objects with subtype objects should preserve the program's desirable properties.
Differentiate nominal vs structural subtyping and explain the is-a relationship and behavioral subtyping within the Liskov substitution principle, emphasizing interfaces preserve expected behavior for clients.
Explore the implications of the Liskov substitution principle and how substituting a subtype can cause misbehavior, invariant violations, and fragile client code that relies on runtime type checks and casts.
Define base classes with only common properties and methods, aligning to the is-a relationship. Extract subclass-specific members into a base class when needed, upholding Liskov substitution principle through use-case tests.
Explore design by contract in the Liskov substitution principle, defining invariants, preconditions, and postconditions, and how subclasses preserve invariants with equal or weaker preconditions and equal or stronger postconditions.
State and verify contracts in C++ using asserts, expects, and ensures, while leveraging static_assert, type traits, and concepts to guide template selection and testing.
Use a screwdriver handle interface to illustrate the Liskov substitution principle, where tools share a common base but differ at the tip. Avoid substitutions that merely fit the interface.
Explore a UML diagram illustrating a Liskov substitution principle violation with a rectangle and a square, showing how distinct height and width setters cause misbehavior under polymorphism.
Examine a classic rectangle–square example that violates Liskov substitution due to preconditions and postconditions and inherited width and height setters, then learn a cleaner separate-classes solution.
Explore Liskov substitution principle violations and how to fix them by refactoring a C++ animal example into a clean hierarchy with separate flyable and workable interfaces, avoiding diamond problems.
Explore Liskov substitution principle with covariant return types using tiger and elephant in a zoo example. Observe stronger postconditions, factory returns, and returning concrete types instead of base class references.
Demonstrates a Liskov substitution principle violation using a queryset class inspired by Django, showing how delete and update return values must align with the database interface.
Demonstrates how a common collection base class with unfeasible operations like push front or pop front and reserve leads to Liskov substitution principle violations in vector, array, and list.
the video demonstrates adding a validated report parser that extends json and xml parsers, validating year and text name while preserving the open-closed principle and liskov substitution principle.
Explore the interface segregation principle, splitting interfaces into cohesive ones so clients avoid unused methods. Understand why bloated interfaces hinder cohesion and how each client uses only the needed interface.
Explore the implications of the interface segregation principle and how cohesive, smaller interfaces reduce changes, dependencies, and test complexity, while supporting the open-closed principle.
Learn to follow the interface segregation principle by avoiding fat interfaces, applying single responsibility, and grouping methods by purpose and client into smaller interfaces, with delegation or adapters when needed.
Explore the interface segregation principle through a visual analogy, contrasting cohesive versus incohesive interfaces and showing how focused, related methods in interfaces serve a common purpose in object-oriented programming.
Explore interface segregation in practice by refactoring a procedure hierarchy into separate bases: one with start and cancel, another adding revert, so clients depend only on needed methods.
Explore a door example showing the interface segregation principle in SOLID C++, comparing a timed door using timer client interface to a normal door without timeout, highlighting interface segregation violation.
Demonstrates two solutions to the interface segregation principle for a door and timer: multiple inheritance and delegation via an adapter pattern, using a timer client interface.
Illustrates the interface segregation principle in SOLID C++, using a printer example to separate a device interface for print, scan, and fax into interfaces via inheritance, avoiding Liskov substitution issues.
Explain the caveats of multiple inheritance, including the diamond problem, virtual inheritance, and how to provide a unique override to avoid ambiguity.
Analyze how the plant care app violates interface segregation principle and Liskov substitution principle, using moisture and sunlight sensors, mutex, and observer patterns, and plan refactoring for cohesive interfaces.
Refactor the plant care app to follow the interface segregation principle and fix Liskov substitution principle violations by introducing water device and window device interfaces and tailoring caretakers and sensors.
Introduce an authentication manager with authentication and authorization interfaces, plus separate credentials and report parsers, applying the interface segregation principle to enable authenticated, authorized tax report submission by clients.
Apply the dependency inversion principle by making high level client classes depend on abstractions, not concrete implementations, and have details depend on those abstractions through abstract base classes.
Explains the implications of the dependency inversion principle: high level modules depend on abstractions, not implementation details, reducing coupling and easing testing, maintenance, and reuse with mocks.
Learn how to follow dependency inversion by shaping abstractions, using interfaces and dependency injection, and selecting where to apply them for flexible, testable C++ code.
This visual analogy explains dependency inversion principle by comparing abstractions like power sockets, USB, and a power adapter, showing how programs depend on interfaces rather than concrete devices.
Explore how the dependency inversion principle shapes UML class diagrams by refactoring from concrete dependencies to a positionable interface with get position, enabling decoupled, reusable, and testable components.
Demonstrate dependency inversion by decoupling lamp and button via an abstract switchable interface, enabling different lamp implementations and testing with mocks using dynamic and static polymorphism.
Demonstrate the observer pattern and dependency inversion by decoupling forecast and users with interfaces, enabling dynamic polymorphism and templates for reusable, testable implementations.
Demonstrate dependency inversion with templates using a notifiable concept and a weather forecast that depends on abstraction, not concrete types, yielding decoupled components and flexible subscriptions.
Demonstrating dependency inversion by moving interfaces to the sensors library, the plant care example shows caretakers depend on abstractions rather than concrete implementations.
Examine dependency inversion in a server that processes text reports using interfaces and abstractions. Move interfaces to a separate package to improve testability and compilation time with mocks.
Learning syntax of a programming language is not enough to become professional and build high quality software. One must also learn how to make good design and implement it properly so that software is robust, maintainable, flexible and testable. That’s why we need object-oriented design principles like SOLID:
Single responsibility
Open-closed
Liskov substitution
Interface segregation
Dependency inversion
This is probably the most complete SOLID principles course on Udemy. But it’s also special in that it focuses on C++ programming language, which we use for examples and assignments. And this course covers both theory and practice.
By the end of this course you should
Develop an intuitive understanding of all SOLID principles
Understand when SOLID principles are needed and when not
Learn how to follow SOLID principles in general, regardless of a programming language, using appropriate abstractions and programming patterns
Learn how to follow SOLID principles using different C++ language features and idioms
Know how to structure C++ projects, test and refactor
However, there should be no illusions about this or any other course. One cannot become a true master of object-oriented design and programming only by completing one or few courses – it takes years of practice. But this course is a good starting point and provides the needed foundations.
So what exactly is covered in the course?
Presentations (theory and concepts about the principles)
Visual analogies from real world (to better understand and remember the principles)
UML diagrams (mainly class diagrams with examples about the principles)
Plenty of stand-alone code examples (at least a few for each principle)
Almost a real-world project (server application that receives over TCP connection and processes tax reports in JSON or XML formats) with unit and high-level tests
Explanations how to install the project’s dependencies, compile it and run tests (uses C++17, CMake, Boost libraries (asio, program options, etc.), Google test)
Quizzes and final test (some have additional code examples)
Programming assignments (work on the project, etc.)
This course is for you if you
Are a beginner or intermediate C++ programmer (but not complete beginner)
Have a basic understanding of OOP (classes, polymorphism, inheritance, encapsulation, etc.)
Want to learn more and become a better software engineer
Ideally, before this course you should have already worked on a few small hobby projects or have some commercial software development experience.
You should be able to pick up the rest of the materials and technologies during the course. At the same time, some applied technologies and examples are more advanced but optional to the main subject of this course.
And even if you don’t primarily program in C++, you will still find that most of the code examples, as well as the principles, apply to many other programming languages.
What is expected from you?
Besides watching lectures and/or reading presentations, it’s expected that you
Compile, read and understand code examples and the project source code (ability to read and work with somebody else's code is very important and often underestimated)
Refactor some of the provided code examples while doing assignments
Work on the project source code in the final assignment, extend its functionality and add tests
The images in visual examples are from website pixabay and the audio in videos is from website bensound.