What is SOLID Principle? go through SOLID Principle by example

By yuseferi, 9 December, 2015
What is SOLID Principle?  go through SOLID Principle by example

SOLID Principle, a buzzword you hear everywhere, A brilliant rules to develop software.

What is SOLID Principle?

In object-oriented computer programming, SOLID is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible and maintainable. It is not related to the GRASP software design principles. The principles are a subset of many principles promoted by Robert C. Martin. Though they apply to any object-oriented design, the SOLID principles can also form a core philosophy for methodologies such as agile development or adaptive software development. The theory of SOLID principles was introduced by Martin in his 2000 paper Design Principles and Design Patterns, although the SOLID acronym itself was introduced later by Michael Feathers.

SOLID is made up of the first five principles in OOP, it stands for 

  • - Single-responsibility principle
  • - Open-closed principle
  • - Liskov substitution principle
  • - Interface segregation principle
  • - Dependency inversion principle

 

Single-responsibility principle

Any class must have one and only one reason to change. In other words, a class must only have a single responsibility. This principle is simple, but not always easy to follow. It’s tempting to sneak new responsibilities into existing classes, but that’s likely to cause future issues. Save yourself time later, and make a new class now.

Incorrect Sample Single-responsibility principle: 

class CustomerOrder {
     
    public function createCustomer(Request $request) {
        // Create customer
    }
     
    public function submitOrder(Request $request) {
        // Submit Orders 
    }
}

Correct Sample Single-responsibility principle: 

class Customer {
     
    public function createCustomer(Request $request) {
        // Create customer
    }
     
}

class Order {
     
    public function submitOrder(Request $request) {
        // Submit Orders
    }
     
}

 

Open-closed principle

Objects or entities must be open for extension but closed for modification. A class must be easily extensible without requiring modification of the class itself. The goal of this principle is to help create well-encapsulated, highly cohesive systems. We’ve found following the open-close rule really helps when multiple developers are involved—less system-specific domain knowledge is required to make updates and expansions.

 

Incorrect Sample of Open-closed principle:

class SavingAccount  
{  
    public function CalculateInterest($accountType)  
    {  
        if($accountType=="Regular")  
        {  
            //Calculate interest for regular saving account 
        }  
        else if($accountType=="Salary")  
        {  
            //Calculate interest for saving account 
        }  
    }  
}

Correct Sample of Open-closed principle:

interface ISavingAccount  
{  
   public function CalculateInterest();  
}  

class RegularSavingAccount implements ISavingAccount  
{  
  public function CalculateInterest()  
  {  
    //Calculate interest for regular saving account
  }  
}  

  

class SalarySavingAccount implements ISavingAccount  
{  
  public function CalculateInterest()  
  {  
    //Calculate interest for saving account 
  }  
}

 

Liskov substitution principle

Derived types must be completely substitutable for their base types. New derived classes must just extend without replacing the functionality of old classes. Otherwise, the new classes can produce undesired effects if they’re used in existing program modules.

Incorrect Sample of Liskov substitution principle:

class Bird
{
        public function  Fly()
        {
            return “I can Fly”;
        }
}

class Parrot extends Bird
{
        public function  Fly()
        {
            return “I can Fly”;
        }
}

class Ostrich extends Bird
{
        public function  Fly()
        {
            return “I can Fly”;
            // But ostrich can't fly
        }
}

Correct Sample of Liskov substitution principle:

class Bird{
	// Methods related to birds
}

class FlyingBirds extends Bird
{
        public function  Fly()
        {
            Return “I can Fly”;
        }
}

class Parrot extends FlyingBirds{
        public function  Fly()
        {
            Return “I can Fly”;
        }
}

class Ostrich extends Bird{
      // Methods related to birds
}

 

Interface segregation principle

Clients must not be dependent upon interfaces or methods they don't use. Keeping interfaces segregated helps a developer more easily know which one to call for specific functions and helps us avoid fat or polluted interfaces that can affect the performance of a system. Doing so also helps us write better unit tests on the codebase.

Incorrect Sample of Interface segregation principle:

interface RestaurantInterface
{
        public function  acceptOnlineOrder();
        public function  payOnline();
        public function  walkInCustomerOrder();
        public function  payInPerson();

}

class OnlineClient implements RestaurantInterface
{
        public function  acceptOnlineOrder()
        {
            //logic for placing online order
        }

        public function  payOnline()
        {
            //logic for paying online 
        }
}

 

Correct Sample of Interface segregation principle:

interface OnlineClientInterface
{
        public function  acceptOnlineOrder();
        public function  payOnline();
}

interface WalkInCustomerInterface
{
        public function  walkInCustomerOrder();
        public function  payInPerson();
}

class OnlineClient implements OnlineClientInterface
{
        public function  acceptOnlineOrder()
        {
            //logic for placing online order
        }

        public function  payOnline()
        {
            //logic for paying online 
        }
}

class WalkInCustomer implements WalkInCustomerInterface
{
        public function  walkInCustomerOrder()
        {
            //logic for walk in customer order
        }

        public function  payInPerson()
        {
            //logic for payment in person 
        }
}

 

Dependency inversion principle

High-level modules(component) must not depend on low-level modules. Both should depend on abstractions. abstractions should not depend on details. Details should depend on abstractions. This principle helps maintain a system that’s properly coupled. Avoiding high-level class dependencies on low-level classes helps keep a system flexible and extensible.

Incorrect Sample of Dependency inversion principle:

// Low Level Class 
class PDFBook {
 
    function read() {
        return "reading a pdf book.";
    }
}

// High Level Class 
class EBookReader {
 
    private $book;
 
    function __construct(PDFBook $book) {
        $this->book = $book;
    }
 
    function read() {
        return $this->book->read();
    }
 
}


$book = new PDFBook();
$read = new EBookReader($b);
 
$read->read();

Correct Sample of Dependency inversion principle:

interface EBook{
    function read();
}

// Low Level Class 
class PDFBook implements EBook{
 
    function read() {
        return "reading a pdf book.";
    }
}

// High Level Class 
class EBookReader {
 
    private $book;
 
    function __construct(EBook $book) {
        $this->book = $book;
    }
 
    function read() {
        return $this->book->read();
    }
 
}


$book = new PDFBook();
$read = new EBookReader($b);
 
$read->read();

 

Conclusion:

Using SOLID principles is critical to being a really good developer and creating valuable software solutions. SOLID is essentially a balanced nutrition plan for software development. When we don’t use these principles in development, our code base becomes bloated and overweight. The system then requires extra work and attention to “trim down” or even maintain, and is at much higher risk for an emergency scare.

Sample codes are available on my GitHub repo: https://github.com/yuseferi/SOLID