Exploring the SOLID Principles of Dart Programming

20 Jul 2023 Balmiki Mandal 0 Dart Programming

SOLID principles are a set of coding guidelines that help software developers create clearer, more maintainable code. They're often applied to object-oriented programming languages like Dart. In this article, we will discuss the SOLID principles and how they can be applied to Dart programming.

The Five SOLID Principles

The five SOLID principles are: Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), and Dependency Inversion Principle (DIP).

Single Responsibility Principle (SRP)

The SRP states that every class or module should have only one reason to change. This means that each class should have a single purpose and any changes made should not affect other classes. As an example, let's consider a simple “User” class in Dart.

class User {
String name;
int age;
void updateName(String newName) {
name = newName;
}
void updateAge(int newAge) {
age = newAge;
}
}

The class has two responsibilities: updating the name and updating the age. We could separate these into two classes to better adhere to the SRP. For instance, we could create a “Name” class for updating the name and an “Age” class for updating the age.

Open/Closed Principle (OCP)

The OCP states that software should be open for extension but closed for modification. This means that code should be written in such a way that future changes can be made without directly modifying existing code. As an example, let's consider a “Calculator” class.

class Calculator {
int calculate(int a, int b) {
return a + b;
}
}

The “Calculator” class only supports addition, but if we want to add support for other operations (such as subtraction and multiplication) we would have to modify the existing code. To better adhere to the OCP, we could create an “Operation” interface and separate the operations into separate classes.

Liskov Substitution Principle (LSP)

The LSP states that any derived class should be substitutable for its base class. This means that if a base class implements a particular interface, any derived class should also be able to implement the same interface. As an example, let's consider an abstract “Shape” class and two derived classes, “Circle” and “Rectangle”.

abstract class Shape {
abstract double getArea();
}
class Circle extends Shape {
double radius;
@override
double getArea() => radius * radius * Math.PI;
}
class Rectangle extends Shape {
double width;
double height;
@override
double getArea() => width * height;
}

The LSP states that if you have a function that takes a “Shape” as an argument, it should also be able to take either a “Circle” or “Rectangle” as an argument, since both are derived from the base “Shape” class.

Interface Segregation Principle (ISP)

The ISP states that no client should be forced to depend on methods it does not use. This means that interfaces should be broken down into smaller, more specific interfaces. As an example, let's consider an “Animal” interface.

interface Animal {
void eat();
void sleep();
}

This “Animal” interface describes two behaviors, “eat” and “sleep”. If we had a class that only needed to perform one of these behaviors (for instance, a “Carnivore” class that only needs to “eat”), then it would be better to break the interface into two separate interfaces to better adhere to the ISP.

Dependency Inversion Principle (DIP)

The DIP states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions. This means that rather than depending on concrete classes, modules should depend on abstract interfaces. This allows for better code reuse and maintainability. As an example, let's consider a “Logger” class.

class Logger {
void log(String message) {
print(message);
}
}

The “Logger” class is dependent on the “print” method, which is a low-level detail. To better adhere to the DIP, we could create an abstraction for the logging functionality. For instance, we could create an “ILogger” interface.

interface ILogger {
void log(String message);
}

Now our “Logger” class can depend on the “ILogger” interface instead of the “print” method, allowing us to easily replace the “Logger” class with a different implementation.

Conclusion

In this article, we discussed the five SOLID principles and how they can be applied to Dart programming. By following the principles, developers can create clearer, more maintainable code that is easier to extend and modify.

BY: Balmiki Mandal

Related Blogs

Post Comments.

Login to Post a Comment

No comments yet, Be the first to comment.