Posts

SOLID - Single Responsibility Principle (SRP)

SOLID - Single Responsibility Principle (SRP) The Single Responsibility Principle states that a class should have only one reason to change. In other words, a class should have a single responsibility or purpose. Here's an example: class FileManager: def read_file(self, file_path): # Code to read the file def write_file(self, file_path, content): # Code to write content to the file def compress_file(self, file_path): # Code to compress the file def decompress_file(self, file_path): # Code to decompress the file In the above example, the FileManager class violates the SRP because it has multiple responsibilities. It handles file reading, writing, compression, and decompression. A better approach would be to separate these responsibilities into distinct classes, each with a single responsibility. For example: class FileReader: def read_file(self, file_path): # Code to read the file class FileWriter: def write_fil...

SOLID - Dependency Inversion Principle (DIP)

SOLID - Dependency Inversion Principle (DIP) Dependency Inversion Principle emphasizes decoupling and abstraction in software systems by defining guidelines for dependency relationships between modules or classes. High-level modules should not depend on low-level modules. Both should depend on abstractions. In simpler terms, DIP encourages the use of interfaces or abstract classes to define contracts and dependencies between modules, rather than depending on concrete implementations. This allows for flexibility, extensibility, and easier maintenance of the codebase. To adhere to DIP, the following practices are recommended: Programming to interfaces or abstract classes rather than concrete implementations. Using dependency injection to provide dependencies to classes rather than instantiating them directly. Employing dependency inversion frameworks or inversion of control containers to manage object dependencies. DIP and Multiple Inheritance This is a special case of Depende...

SOLID - Liskov Substitution Principle (LSP)

SOLID - Liskov Substitution Principle (LSP) The Liskov Substitution Principle (LSP) is a fundamental principle in object-oriented programming (OOP) that defines guidelines for substitutability of objects within a program's inheritance hierarchy. It states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. In simpler terms, the LSP emphasizes that derived classes or subclasses must be able to be used in place of the base class without introducing unexpected behavior or breaking the functionality of the program. The principle can be summarized with the following statement: "If S is a subtype of T, then objects of type T can be replaced with objects of type S without altering the correctness of the program." Design by Contract (DbC) To adhere to the Liskov Substitution Principle, derived classes must follow these guidelines: Subtype Requirement: The contract or behavior defined by the base...

SOLID - Open-Closed Principle (OCP)

SOLID - Open-Closed Principle (OCP) The Open-Closed Principle states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. It means that you should be able to extend the behavior of a software entity without modifying its existing code. from abc import ABC, abstractmethod class Area(ABC): @abstractmethod def calculate_area(self): pass class Rectangle(Area): def __init__(self, length, width): self.length = length self.width = width def calculate_area(self): return self.length * self.width class Circle(Area): def __init__(self, radius): self.radius = radius def calculate_area(self): return 3.14 * self.radius ** 2 def print_areas(shapes): for shape in shapes: area = shape.calculate_area() print(f"Area: {area}") # Client code shapes = [Rectangle(4, 5), Circle(3)] print_areas(shapes) SOLID and Design Pattern Open-Clos...

SOLID Principles

SOLID Principles To identify when and where to apply the SOLID principles, it's important to understand the core principles and their purposes. Here are some guidelines to help you identify their application: Single Responsibility Principle (SRP): Identify distinct responsibilities or reasons for change within your system. If a class or module has multiple responsibilities, consider separating them into separate classes or modules. Look for classes or modules that have more than one reason to change and consider refactoring them. Open-Closed Principle (OCP): Identify components or modules that should be open for extension but closed for modification. Consider using abstraction, interfaces, or inheritance to allow for extension without modifying existing code. Look for areas where you anticipate future changes or variations and design them to be easily extended without modifying existing code. Liskov Substitution Principle (LSP): Assess whether derived classes ca...

Design Patterns

Design Patterns Creational Patterns These patterns focus on object creation mechanisms, providing flexibility and decoupling the client code from the specific classes instantiated. Some examples of creational patterns are: Singleton: Ensures that only one instance of a class exists throughout the program. Factory: Provides an interface for creating objects, but allows subclasses to decide which class to instantiate. Builder: Separates the construction of complex objects from their representation, allowing the same construction process to create different representations. Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes. Prototype: Creates new objects by cloning existing ones and modifying them as needed. Structural Patterns These patterns deal with the composition of classes and objects, focusing on how they can be organized to form larger structures while keeping them flexible and ef...

Abstract Class and Interface usage in Python

Abstract Class and Interface usage in Python The choice between using an abstract class or an interface depends on the specific requirements and design goals of your application. Both abstract classes and interfaces have their own advantages and use cases. Here are some considerations: Abstract Classes Default Implementation Abstract classes can provide a default implementation for some methods, allowing subclasses to inherit and override only the necessary methods. This can help reduce code duplication and provide shared functionality. Code Reusability Abstract classes can contain common code or attributes that can be inherited by subclasses, promoting code reuse and maintaining a consistent implementation across related classes. Flexibility Abstract classes can define both abstract methods (to be implemented by subclasses) and concrete methods (with default implementation). This provides flexibility in designing class hierarchies and allows for a mix of common and specialized...