SOLID Design Principles | Complete Guide with Code Examples

SOLID Design Principles | Complete Guide with Code Examples

Introduction to SOLID Design Principles

Overview of Previous Concepts

  • The lecture begins with an introduction to the new topic: SOLID design principles, emphasizing their importance in managing complex code structures.
  • A recap of Object-Oriented Programming (OOP) pillars is provided, including abstraction, encapsulation, inheritance, and polymorphism.
  • The speaker highlights the challenge of managing thousands of classes in real-world projects and the need for effective design principles to maintain clean code.

Importance of Clean Code

  • Poorly written code can introduce disastrous bugs that affect both technical performance and financial aspects of applications.
  • An analogy is drawn comparing messy wiring in a house to tightly coupled classes in programming, illustrating how difficult it becomes to troubleshoot issues when everything is interconnected.

Problems Faced Without Design Principles

Key Issues Identified

  • Maintainability issues arise when integrating new features into existing applications without introducing bugs or requiring extensive changes.
  • Readability problems occur when new engineers struggle to understand complicated and tightly coupled codebases.
  • Common challenges include introducing numerous bugs that require significant debugging time.

Introduction to Robert C. Martin's Principles

SOLID Design Principles Explained

  • Robert C. Martin introduced these design principles in a paper published in 2000 aimed at improving software architecture.
  • The acronym "SOLID" stands for five key principles:
  • S: Single Responsibility Principle (SRP)
  • O: Open/Closed Principle (OCP)
  • L: Liskov Substitution Principle (LSP)
  • I: Interface Segregation Principle (ISP)
  • D: Dependency Inversion Principle (DIP)

Single Responsibility Principle (SRP)

Definition and Explanation

  • SRP states that a class should have only one reason to change or perform one specific task effectively.

Real-world Analogy

  • An example using a TV remote illustrates how combining multiple responsibilities complicates maintenance; each device should ideally have its own controller.

Application of SRP

  • Classes should be designed so that they handle only one responsibility, making them easier to manage and modify without affecting other functionalities.

Implementing SRP Through Composition

Refactoring Example

  • To adhere to SRP, the shopping cart functionality is broken down into multiple classes:
  • ShoppingCart handles total price calculations.
  • InvoicePrinter manages invoice printing tasks.
  • DBStorage handles database persistence operations.

Benefits of Refactoring

  • Each class now has a single responsibility, simplifying modifications since changes are localized within specific classes rather than affecting multiple functionalities across one class.

Conclusion on Single Responsibility Principle

Clarification on Misconceptions

  • It’s clarified that having multiple methods within a class is acceptable as long as all methods serve the same responsibility. This ensures clarity while maintaining adherence to SRP.

Open/Closed Principle (OCP)

Definition and Importance

  • OCP states that classes should be open for extension but closed for modification; this means adding new features without altering existing code.

Challenges with OCP Implementation

  • Modifying existing classes directly violates OCP; instead, abstraction through inheritance allows for extending functionality without changing original implementations.

Introduction to DB Persistence Class

Overview of Abstract Classes and Interfaces

  • A class named "DB Persistence" is introduced, which will serve as either an interface or an abstract class.
  • An interface is defined as a contract between a class and a client, outlining what the class can do without detailing how it does it.
  • The client can be any software application or user that utilizes the defined class.

Functionality of Interfaces

  • The interface acts as a contract that informs the client about the functionalities of the class without revealing implementation details.
  • In C++, abstraction is expressed through abstract classes, while in Java, interfaces are used directly with specific keywords.

Understanding Abstract Classes and Polymorphism

Purpose of Abstract Classes

  • Abstract classes allow for polymorphism by enabling multiple concrete classes (e.g., A1, A2, A3) to inherit from them.
  • Each concrete subclass provides its own definition for functions declared in the abstract class.

Method Overriding

  • When creating objects from subclasses (A1, A2, A3), method calls will resolve to their respective implementations based on the object type created.

Implementing Inheritance and Polymorphism

Inheritance Structure

  • The DB Persistence class is established as an abstract class to adhere to design principles like Open/Closed Principle.
  • Concrete classes are created for different persistence methods (e.g., SaveToSQLDB, SaveToMongoDB).

Handling Different Persistence Methods

  • Each persistence method implements its own logic for saving data while sharing common function signatures defined in the abstract parent class.

Code Implementation Overview

Class Relationships

  • The Cart class interacts with DB Persistence through various persistence strategies without altering existing code structures when new features are added.

Adhering to Design Principles

  • New features can be integrated by simply creating new subclasses without modifying existing ones, thus following Open/Closed Principle effectively.

Transitioning to Code Examples

Initial Code Structure

  • The initial product and shopping cart classes are outlined along with their respective methods for managing products and calculating totals.

Enhancements Through New Features

  • Two new persistence methods were integrated into existing code but initially broke Open/Closed Principle by declaring all methods together.

Refactoring Code for Better Design

Introducing Abstraction

  • An abstract Persistence class is created with subclasses handling specific storage mechanisms (SQL, MongoDB).

Maintaining Consistency

  • All subclasses implement their own save logic while adhering to a single method signature defined in the parent abstract class.

Exploring Liskov Substitution Principle

Definition of Liskov Substitution Principle

  • Liskov Substitution Principle states that subclasses should be substitutable for their base classes without affecting program correctness.

Importance of Compliance

  • This principle emphasizes that derived classes must extend functionality without narrowing down capabilities provided by base classes.

Practical Example: Account Management System

Account Class Hierarchy

  • An Account superclass defines essential operations like deposit and withdraw; SavingsAccount and CurrentAccount inherit these functionalities.

Client Interaction with Accounts

  • Clients interact with accounts via a unified interface allowing deposits and withdrawals across different account types seamlessly.

Understanding the Open/Closed Principle in Software Design

Issues with Current Implementation

  • The current implementation requires frequent code changes in the client class due to random conditions like withdrawal and deposit, violating the Open/Closed Principle.
  • Attempting to solve the Liskov Substitution Principle inadvertently breaks the Open/Closed Principle, leading to tightly coupled client and application classes.
  • Abstraction was intended to allow clients to interact without needing knowledge of various implementations, but this approach has failed.

Proposed Changes

  • A diagram will be modified to remove unnecessary elements and focus on essential components for clarity.
  • The account interface currently includes methods that are not utilized by all child classes, such as Fixed Deposit Accounts only using deposit methods.
  • The logic dictates that a Fixed Deposit Account should not inherit from a parent class that requires both deposit and withdrawal methods since it only implements one.

Redefining Class Hierarchy

  • To resolve these issues, a new hierarchy is proposed where accounts are categorized into withdrawable and non-withdrawable types.
  • A Non Withdrawable Account will have only a deposit method while an interface for Withdrawable Accounts will extend this functionality.

Implementing New Interfaces

  • Two interfaces are created: one for Non Withdrawable Accounts (with just a deposit method), and another for Withdrawable Accounts (which extends the first).
  • This structure allows for clear inheritance where each account type can implement its specific functionalities without breaking principles.

Finalizing Class Structure

  • The final hierarchy consists of Saving Accounts and Current Accounts inheriting from Withdrawable Accounts, while Fixed Deposit Accounts derive from Non Withdrawable Accounts.
  • This restructuring ensures compliance with Liskov Substitution Principle by maintaining feature integrity across different account types.

Client Interaction with Account Types

Client's Perspective on Account Management

  • Clients now manage two distinct lists: one for Non Withdrawable Accounts and another for Withdrawable Accounts, enhancing clarity in operations.
  • Clients interact solely with interfaces relevant to their accounts—either calling deposit or both deposit and withdrawal methods based on account type.

UML Diagram Representation

  • A UML diagram illustrates the relationship between clients and accounts, showing how clients possess multiple accounts of each type effectively.

Code Implementation Overview

Initial Code Structure

  • An abstract Account class is established with virtual methods for deposit and withdrawal; three specific account types are then defined: Savings, Current, and Fixed Term.

Handling Transactions

  • Each account type implements transaction handling differently; Fixed Term throws logical errors when attempting withdrawals since it's not permitted.

Client Transaction Processing Logic

  • A Bank Client processes transactions through looping over its list of accounts. It attempts deposits followed by withdrawals while managing exceptions appropriately.

Addressing Previous Implementation Flaws

Identifying Problems in Earlier Approaches

  • Previous implementations still resulted in exceptions being thrown due to improper handling of fixed term accounts during transaction processing.

Correcting Methodology

  • The correct approach involves creating separate loops within the client's transaction processing method tailored specifically for each account type's capabilities.

By following these structured notes based on timestamps from the transcript, readers can easily navigate through key concepts discussed regarding software design principles related to account management systems.

Video description

In this video, we dive deep into the SOLID Principles, the foundation of scalable and maintainable software design. Whether you're a beginner or an experienced developer, this guide will help you understand and master: *Introduction to SOLID Principles* *Single Responsibility Principle (SRP)* *Open/Closed Principle (OCP)* *Liskov Substitution Principle (LSP)* *Complete Code Examples Explained* Join Coder Army Whatsapp Group: https://whatsapp.com/channel/0029Va6H0tbHVvTbcuT99Y1f Code and Notes: Github : https://github.com/adityatandon15/LLD Connect with Aditya Tandon Instagram : https://www.instagram.com/adityatandon2 LinkedIn : https://www.linkedin.com/in/adityatandon2 Twitter : https://x.com/adityatandon02 Timestamps: 00:00 Introduction 02:32 Problems without Design principles 04:08 Introduction to SOLID Principles 06:53 Single Responsibility Principle (SRP) 17:16 Code for Single Responsibility Principle 20:30 Conclusion of Single Responsibility Principle 21:38 Open Close Principle (OCP) 35:31 Code for Open Close Principle 39:27 Liskov Substitution Principle (LSP) 01:00:59 Code for Liskov Substitution Principle #LLD #SOLIDPrinciples #SoftwareDesign #SRP #OCP #LSP #CodingInterview #SystemDesign #DesignPatterns #SoftwareEngineering #TechEducation