Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

The importance of interfaces in SOLID design

April 3, 2023

The importance of interfaces in SOLID design

In software development, the SOLID principles are a set of five design principles that help developers create well-structured, easy-to-maintain, and scalable code. One of these principles, the Interface Segregation Principle (ISP), emphasizes the importance of using interfaces to decouple components and create more flexible and maintainable code.

What are interfaces?

An interface in programming defines a set of methods that a class must implement. It acts as a contract, specifying what a class can do without dictating how it should do it. Interfaces allow us to define behavior without implementing it, promoting loose coupling and providing a way to achieve abstraction.

The importance of interfaces in SOLID design

Interfaces play a crucial role in SOLID design, particularly in supporting the Dependency Inversion Principle (DIP), which states that high-level modules should not depend on low-level modules, but rather both should depend on abstractions. By relying on interfaces to define interactions between components, we can decouple parts of the codebase and make our software more maintainable, testable, and extensible.

Let’s explore the importance of interfaces in SOLID design with extensive examples in C and Python.

Using interfaces in C

In C, interfaces are typically represented as function pointers in a struct. Let’s consider a simple example where we want to create a set of shapes, each with its own area calculation method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Shape interface
typedef struct {
    double (*calculateArea)(void *self);
} ShapeInterface;

// Square implementation
typedef struct {
    ShapeInterface *interface;
    double side;
} Square;

double calculateSquareArea(void *self) {
    Square *square = (Square *)self;
    return square->side * square->side;
}

ShapeInterface squareInterface = {&calculateSquareArea};

// Circle implementation
typedef struct {
    ShapeInterface *interface;
    double radius;
} Circle;

double calculateCircleArea(void *self) {
    Circle *circle = (Circle *)self;
    return 3.14 * circle->radius * circle->radius;
}

ShapeInterface circleInterface = {&calculateCircleArea};

int main() {
    Square square = {&squareInterface, 5.0};
    Circle circle = {&circleInterface, 3.0};

    // Calculate and print areas
    printf("Square area: %.2f\n", square.interface->calculateArea(&square));
    printf("Circle area: %.2f\n", circle.interface->calculateArea(&circle));

    return 0;
}

In the above example, we use function pointers and structs to define the ShapeInterface and implement it for the Square and Circle. This approach provides a way to define behavior without exposing the internal details of each shape, promoting loose coupling and adhering to the ISP.

Using interfaces in Python

In Python, interfaces are not explicitly defined like in C, as Python is a dynamically typed language. However, we can achieve a similar effect using abstract base classes (ABCs) from the abc module.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from abc import ABC, abstractmethod

# Shape interface
class ShapeInterface(ABC):
    @abstractmethod
    def calculate_area(self):
        pass

# Square implementation
class Square(ShapeInterface):
    def __init__(self, side):
        self.side = side

    def calculate_area(self):
        return self.side * self.side

# Circle implementation
class Circle(ShapeInterface):
    def __init__(self, radius):
        self.radius = radius

    def calculate_area(self):
        return 3.14 * self.radius * self.radius

# Use the interfaces
square = Square(5.0)
circle = Circle(3.0)

# Calculate and print areas
print(f"Square area: {square.calculate_area():.2f}")
print(f"Circle area: {circle.calculate_area():.2f}")

In the Python example, we define the ShapeInterface using an abstract method, ensuring that all classes implementing it must provide a calculate_area method. This serves as a contract for our shape implementations, promoting loose coupling and adhering to the principles of SOLID design.

Conclusion

Interfaces are a crucial tool in SOLID design, particularly in supporting the Interface Segregation Principle and providing a way to achieve abstraction, loose coupling, and flexibility in our code. Whether working in a low-level language like C or a high-level language like Python, interfaces play a key role in creating well-structured and maintainable software systems. By utilizing interfaces effectively, developers can write more testable, scalable, and maintainable code that conforms to the principles of SOLID design.


➡️ Virtual machine library


⬅️ Flow library


Go back to Posts.