link: Design Principles

SOLID Principles

Overview

The SOLID principles are like a set of rules for software developers. They help make sure that the programs they create are easy to work with, can grow without too much trouble, and are clear to understand.

Abstract

Think of SOLID as a toolbox for building software. These principles are the foundation for creating strong and reliable systems in object-oriented programming.

Content

Key Concepts

Each SOLID principle focuses on a different aspect of how to design software, working together to create a codebase that’s organized and adaptable:

Important

SRP

Single Responsibility Principle

Overview

A class should have just one reason to change.

The Single Responsibility Principle (SRP) is a basic rule in object-oriented programming that ensures each class in a program handles just one part of the software’s functionality. According to SRP, a class should only have one reason to change, which means it should only have one job.

Abstract

Applying SRP makes classes simpler to manage, test, and maintain. It focuses on giving each class one task to perform well, which reduces coding mistakes and improves the quality of the software. This principle helps keep the program organized and makes it easier to update as needs change.

Content

Principle Explained

The SRP encourages developers to design classes that are dedicated to one functionality:

Important

  • Focused Classes: Each class should handle a single functionality, avoiding the mixture of multiple tasks that complicate both the class’s purpose and its future modifications.
  • Minimized Impact of Changes: Isolating responsibilities ensures that changes made to address one concern have minimal effects on other aspects of the program, fostering a more robust and less error-prone development environment.

Warning

if a class does too many things, you have to change it every time one of these things changes. While doing that, you’re risking breaking other parts of the class which you didn’t even intend to change.

Practical Example

Refactoring a class to adhere to SRP involves:

  • Identifying Multiple Behaviors: Recognize when a class is handling more than one responsibility.
  • Separating Concerns: Create new classes to handle each responsibility separately.

BEFORE

BEFORE: A class with mixed responsibilities is harder to maintain.

AFTER

AFTER: Each responsibility is handled by a dedicated class, leading to a cleaner design.

Link to original

OCP

Open Closed Principle

Overview

Classes should be open for extension but closed for modification.

The Open/Closed Principle (OCP) is a fundamental rule in object-oriented programming. It encourages classes and other software parts to be extendable without needing to change the existing code. This principle is all about making a system strong and easy to maintain. It means you can add new features without causing problems in what’s already there.

Abstract

Following the OCP prevents problems in existing features when you add new ones. It makes updating and expanding the software simpler. Essentially, you can add new features by creating new classes that build upon the existing ones, without having to touch the existing code.

Content

Principle Explained

The OCP promotes designing classes that are both open to extension and closed to modifications:

Important

  • Extendable: You can extend the functionalities of a class through subclasses.
  • Non-modifiable: Once completed, the original class should not need to be modified to add new features.

Practical Application

When you follow the Open Closed Principle (OCP), you usually use interfaces or abstract classes. This helps create a codebase that can easily grow without having to change what’s already there. By doing this, you keep your system intact and ready to adjust to new needs or changes.

Practical Example

Consider a banking application with various account types each having different rules and interest calculations.

Initial Implementation: The initial class design directly incorporates multiple account types, leading to a rigid and difficult-to-modify structure.

Refactored Implementation: By abstracting common functionalities into an interface and implementing specific behaviors in separate classes, we adhere to OCP.

This approach allows each account type to maintain its unique implementation while adhering to a common interface, facilitating easy expansions or modifications without altering the existing codebase.

Above code is implementing both OCP and SRP principle, as each class is doing a single task and we are not modifying class and only doing an extension.

Summary

Summary

The Open/Closed Principle is instrumental in developing software that is capable of growing and changing with minimal disturbance to existing functionality. By designing software systems that are open for extension but closed for modification, developers can add new features with ease while maintaining stability in the existing code.

Link to original

LSP

Liskov Substitution Principle

Overview

Objects of a subclass should be replaceable with objects of their superclass without altering the correctness of the program.

The Liskov Substitution Principle (LSP) is an essential rule in object-oriented programming. It promotes the idea that subclasses should be able to replace their parent classes without causing any issues. This principle guarantees that classes built from a base class can be used just like the base class itself, without users needing to worry about any differences.

Abstract

By following LSP, software becomes more reliable and easier to work with. It ensures that new classes built from existing ones don’t change how the original classes behave, making the system easier to maintain and expand.

Content

Principle Explained

LSP is about ensuring that derived classes only extend without replacing the functionality of their base classes:

Important

  • Substitutability: A child class should enhance, not alter or diminish, the behavior of the parent class.
  • Safeguarding Client Code: Client code that uses the parent class should work with instances of the derived class without being aware of the change.

LSP Requirements

To ensure compliance with the Liskov Substitution Principle, especially in statically typed languages like C#, it’s essential to follow these guidelines:

Important

  • Method Parameter Types: In C#, the types of parameters in methods of a subclass should match or be more abstract than those in the parent class methods. This ensures that any method expecting a parent class instance will operate correctly if a subclass instance is passed.
  • Method Return Types: The return type in methods of a subclass should match or be a subtype of the return type in the parent class methods in C#. This maintains consistency in what the client code expects to receive, preventing errors and maintaining integrity.

These rules are built into C#‘s type system and are crucial for ensuring that derived classes enhance rather than disrupt the functionality of their base classes. By adhering to these requirements, developers can leverage polymorphism effectively, promoting code reusability and scalability while adhering to LSP.

Practical Example

Let’s explore how the LSP can be applied and violated, emphasizing the need for thoughtful class design:

Summary

Summary

The Liskov Substitution Principle (LSP) ensures that you can use subclasses wherever you use their parent classes without causing problems in your program. It’s really important for making software that’s dependable and solid when you’re using inheritance. When developers stick to LSP, they can make systems that are simpler to manage and grow. It means they can make changes and improvements without making mistakes. Following LSP helps create a more predictable and sturdy structure for applications, encouraging good software practices and design.

Link to original

ISP

Interface Segregation Principle

Overview

Clients should not be forced to depend on interfaces they do not use.

The Interface Segregation Principle (ISP) suggests creating interfaces that are specific and focused, avoiding what’s called “interface pollution.” Instead of one big interface, it’s better to have several smaller ones that suit different needs. When you follow ISP, your software design becomes tidier and easier to work with. It means you can maintain and add new features more smoothly.

Abstract

ISP emphasizes the importance of making interfaces to client needs, thereby avoiding the compulsion for clients to implement irrelevant interface methods. This leads to a codebase that’s easier to navigate and update, with minimal side effects from changes.

Content

Principle Explained

ISP directs us to craft interfaces that are:

Important

  • Narrow and Focused: Interfaces should be specific to client requirements, ensuring that classes only implement what they need.
  • Client-Centric: Design interfaces with the client’s use in mind, providing them with the functionality they require without the burden of unused methods.
  • Granular and Decoupled: Segregate large, “fat” interfaces into smaller, more coherent ones to reduce the impact of changes and improve adaptability.

Practical Example

To illustrate the Interface Segregation Principle (ISP), let’s examine a library intended to facilitate integration with various cloud services. We’ll see how applying ISP can refine our approach to designing interfaces for these services.

Warning

As with the other principles, you can go too far with this one. Don’t further divide an interface which is already quite specific. Remember that the more interfaces you create, the more complex your code becomes. Keep the balance

Summary

Summary

The Interface Segregation Principle plays a vital role in building software architectures that are flexible, easy to maintain, and capable of scaling up. When interfaces are designed to be specific and focused on what clients need, developers can build systems that can handle changes well and meet the requirements of different clients. This principle is at the core of object-oriented design, delivering on its promise of creating software that’s adaptable and user-centered.

Link to original

DIP

Dependency Inversion Principle

Overview

High-level modules should not be dependent on low-level modules; both should rely on abstractions. Additionally, these abstractions should not be based on details; rather, the details should be based on abstractions.

The Dependency Inversion Principle (DIP) is crucial for building software that’s modular and simple to maintain. It guides the development of a system where different parts are not tightly connected but can work together through abstract interfaces. This means that the main logic of the program doesn’t depend on the nitty-gritty details of how things are done at a lower level.

Content

Principle Explained

The principle distinguishes between two types of classes:

  • High-Level Modules: Contain complex business logic that directs low-level classes to do something.
  • Low-Level Modules: Implement basic operations such as working with a disk, transferring data over a network, connecting to a database, etc.

To ensure compliance with DIP follow this rules:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend on details. Details should depend on abstractions.

Practical Example

BEFORE

Initially, a high-level module is directly dependent on a low-level module, leading to a rigid and fragile structure that is challenging to test and maintain.

AFTER

By inverting the dependency, both high-level and low-level modules rely on shared abstractions. This results in a more flexible and maintainable system where changes to low-level details have minimal impact on high-level modules.

This approach not only enhances code reusability and maintainability but also simplifies unit testing by allowing for easy mocking or stubbing of the lower-level components.

Summary

Summary

Following the Dependency Inversion Principle brings flexibility and easier maintenance to software systems. It encourages a setup where the core business logic is neatly separated from the technical details of how things are done. This separation is crucial for a well-designed application.

Link to original

Summary

Summary

The SOLID principles are essential in object-oriented design, providing a blueprint for building software that can withstand changes and be iterated upon easily. They act as a roadmap for crafting systems that are simpler to debug, test, and maintain, embodying the fundamental principles of successful software development.

References

Pictures from - The S.O.L.I.D Principles in Pictures | Medium

Examples from - Refactoring and Design Patterns