Flutterexperts

Empowering Vision with FlutterExperts' Expertise
S.O.L.I.D Principles In Dart

It’s an acronym for 5 design principles for writing code which is maintainable, scalable and easier to understand. These principles are given by Bob. C Martin (uncle bob) more familiar to the developer world.


Things To Understand Firstly :

What Problem these S.O.L.I.D principles solved?

  1. working with the legacy code.
  2. Re-reading the code multiple times to get to the part you need to change
  3. Hard to understand what the method does. jumping multiple methods of the same name
  4. spending a lot more time to fix a minor bug

Basically, we spend a lot more time reading than writing code.

1. Single Responsibility principle

As it’s clear from its name single responsible. A class should only be responsible for one thing that means a class could change for only one reason.

Let’s first see the bad practise in which maintenance is not pleasant. Because of all the things in one single place validation, getRequest, painting.

class Result
{

checkResult(int rollno)
{
bool isRollnoValidate = isRollnovalidate();
if(isRollnoValidate)
{

ResultModel resultModel = searchResult();
showResult(resultModel);
}
else{
return "Invalid rollno";
}

}

isRollnovalidate()
{

return true;
}

// get request
searchResult()
{
// return result;
}

//painting
showResult(ResultModel model)
{
// show result in pleasant way
}

}

class ResultModel
{

}

After applying the Single Responsibility principle

Now the developer can focus on the desired functionality.

info :: class Result is responsible for the flow.


class Result {
checkResult(int rollno) {
bool isRollnoValidate = Validate().isRollnovalidate();
if (isRollnoValidate) {
ResultModel resultModel = NetworkApi().searchResult();
Printing().showResult(resultModel);
} else {
return "Invalid rollno";
}
}
}

class Validate {
// this is responsible for validate
isRollnovalidate() {
return true;
}
}

class ResultModel {}

class Printing {
// this class is responsible for printing
showResult(ResultModel model) {
// show result in pleasant way
}
}

class NetworkApi {
// this class is responsible for fetching result
searchResult() {
return ResultModel();
}
}

2. OPEN-CLOSED PRINCIPLE

An entity should be open for extensions but closed for modification

It states that for good practise you should be able to add new features without modifying the existing code.

Let’s first see the bad practice. In this, if we need to mechanical branch result feature then we need to edit the existing code.

class Result
{
mechanicalCheckResult()
{
// some code
}

civilCheckResult()
{
// some code
}

}

After applying Open CLOSED PRINCIPLE. In this, we create a class for adding new functionality.


abstract class Result {
checkResult();
}

class ComputerScience implements Result {
@override
checkResult() {
// some code
}
}

class Civil implements Result {
@override
checkResult() {
// some code
}
}

class Mechanical implements Result {
@override
checkResult() {
// some code
}
}

3. Liskov Substitution Principle

It means how good is your design from abstraction perspective

The architecture guarantee that the subclass will maintain the logic correctness of code.Basically prefer composition (with interfaces) over inheritance

As above let’s first see bad practice

 abstract class Result {
checkResult();

codingTestResult();
}

class MechanicalBranch extends Result {
@override
checkResult() {
// some code
}

/*
* Here it is logically incorrect
* */
@override
codingTestResult() {
// some code
}
}

class ComputerScienceBranch extends Result {
@override
checkResult() {
// some codet
}

@override
codingTestResult() {
// some code
}
}

After Applying the Liskov Substitution Principle

abstract class OfflineResult {
checkResult();
}

abstract class CodingResult {
codingTestResult();
}

class MechanicalBranch implements OfflineResult {
@override
checkResult() {
// some code
}
}

class ComputerScienceBranch implements OfflineResult, CodingResult {
@override
checkResult() {
// somecode
}

@override
codingTestResult() {
// somecode
}
}

4. Interface Segregation Principle

It states that no client should be forced to depend on methods it does not use.

Basically client should never depend on anything more than the method its calling.

Bad practiCe :

abstract class Result {
checkResult();

codingTestResult();
}

class MechanicalBranch implements Result {
@override
checkResult() {
// some code
}

/*
* Here we exposed client with the method which none of his * business
* */
@override
codingTestResult() {
// some code
}
}

class ComputerScienceBranch implements Result {
@override
checkResult() {
// some codet
}

@override
codingTestResult() {
// some code
}
}

After applying the Interface Segregation Principle :

abstract class OfflineResult {
checkResult();
}

abstract class CodingResult {
codingTestResult();
}

class MechanicalBranch implements OfflineResult {
@override
checkResult() {
// some code
}
}

class ComputerScienceBranch implements OfflineResult, CodingResult {
@override
checkResult() {
// somecode
}

@override
codingTestResult() {
// somecode
}
}

5. Dependency Inversion Principle

Abstractions should not depend on details(concrete implementations). They should depend on abstractions.

Basically, you should able to change the implementation(background or low level code) without altering the high-level code(actual class you interact to).

Its good to extending a abstract class or interface but reverse (means with no abstract method) is a bad practise.

Depending on abstractions gives the freedom to be independent of the implementations. Let’s dig into it.

abstract class Payment {
payment();
}

class PaymentViaCreditCard implements Payment
{
@override
payment() {
// some code
}
}

class PaymentViaDebitCard implements Payment
{
@override
payment() {
// some code
}
}

class PaymentViaBhimUPI implements Payment
{
@override
payment() {
// some code
}
}


class Checkout
{
// our checkout class knows nothing about how payment works
// its knows pay.payment() is paying method
checkOut(Payment pay)
{
pay.payment();
}
}

I highly recommend watching this video. That’s the minimum thing I can do to express my gratitude.

Conclusion :

Don’t get trapped by Solid

: S.O.L.I.D Principles are principles, not rules

: Always use common sense while applying this. Your goal is to achieve to make your code maintainable, easy to extend.

: Avoid over- fragmenting your code for the sake of SRP or S.O.L.I.D

: Don’t try to achieve S.O.L.I.D, use S.O.L.I.D to achieve maintainability. It’s a tool, not your goal

A warm welcome for all the queries and suggestion. If you find anything that could be improved please let me know, I would love to improve


Feel free to connect with us:
And read more articles from FlutterDevs.com

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on Facebook, GitHub, Twitter and LinkedIn for any flutter related queries.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences!

Leave comment

Your email address will not be published. Required fields are marked with *.