SOLID

SOLID Design Principles

These are class level design principles. They allow you to minimize coupling and dependencies. The result is code that is easy to read and refactor.

 

  • Single Responsibility Principle (SRP)

  • Open/Closed Principle (OCP)

  • Liskov Substitution Principle (LSP)

  • Interface Segregation Principle (ISP)

  • Dependency Inversion Principle (DIP)

SOLID (SRP)

A class should have only one reason to change and when making that change it should only effect the class.

 

  • Every class should only have a single responsibility.

  • The single responsibility should be entirely encapsulated by its class. 

A single responsibility

a single reason for change

class Book {
 
    function getTitle() {
        return "A Great Book";
    }
 
    function getAuthor() {
        return "John Doe";
    }
 
    function turnPage() {
        // pointer to next page
    }
 
    function printCurrentPage() {
        echo "current page content";
    }
}

The function printCurrentPage changes depending on how you want to output the page's content.

 

Console, File, or DataBase. All require altering the Book class.

A single responsibility

a single reason for change

class Book {
 
    function getTitle() {
        return "A Great Book";
    }
 
    function getAuthor() {
        return "John Doe";
    }
 
    function turnPage() {
        // pointer to next page
    }
 
    function getCurrentPage() {
        return "current page content";
    }
 
    function getLocation() {
        // returns the position in the library
        // ie. shelf number & room number
    }
}

Location is only meaningful in the context of a library, so the library should handle the location.

Location is only meaningful in the context of a library, so the library should handle the location.

class Book {
 
    function getTitle() {
        return "A Great Book";
    }
 
    function getAuthor() {
        return "John Doe";
    }
 
    function turnPage() {
        // pointer to next page
    }
 
    function printCurrentPage() {
        echo "current page content";
    }

    function save() {
        $filename = '/documents/'. $this->getTitle(). ' - ' . $this->getAuthor();
        file_put_contents($filename, serialize($this));
    }

    function printCurrentPage() {
        echo "current page content";
    }
}

Lets break it into multiple classes each with a single responsibility.

SOLID (Open/Closed)

Software entities should be open for extension,

but closed for modification.

 

You should be able to add new features to and existing system without modifying preexisting code.

 

The concepts for "extension" are language specific, for example subclasses, extension methods, or categories.

This seems obvious but why is it important?

@implementation C1NetworkOperation

- (NSString *)endpoint
{
    NSString *base = @"http:someurl/api";
    if (self.command != None)
        base = [base stringByAppendingFormat:@"/%@", [self commandEnumToString]];
        
    return base;
}

- (NSDictionary *)parameters
{
    return @{
        @"amount" : self.amount
    };
}

- (C1CSSerializerType)responseSerializerType
{
    return C1CSSerializerTypeJSON;
}

@end

SOLID (Open/Closed)

SOLID (Liskov Substitution Principle)

Functions that use pointers or references to base

classes must be able to use objects of derived

classes without knowing it.

Rectangle rect = new Rectangle();
rect.SetHeight(10); rect.SetWidth(2);
assert(rect.CalculateArea() == 20);

Rectangle rect1 = new Square(); 
rect1.SetHeight(10); rect1.SetWidth(2);
assert(rect1.CalculateArea() == 20);

public class Rectangle
{
    private int height, width;
    public void SetHeight(int height) { this.height = height; }
    public void SetWidth(int width) { this.width = width; }

    public int CalculateArea()
    {
        return this.Height * this.Width;
    }
}

public class Square : Rectangle
{
    public override void SetHeight(int height) { 
        base.height = height;
        base.width = height; 
    }

    public override void SetWidth(int width) { 
        base.height = width; 
        base.width = width; 
    }
}
IList<Card> cards = new ArrayList<Card>();

void setUp() {
    cards.add(new DebitCard());
}

void testProcessTransaction() {
    for (Card card : cards) {
        Card.setAvailableFunds(300);
        assert(Card.canDebit(320) == false);
    }
}
public class Card {
    protected int availableFunds;

    public void setAvailableFunds(int funds) {
        availableFunds = funds;
    }

    public virtual bool canDebit(int amount) {
        return (availableFunds > amount);
    }
}
public class DebitCard : Card {
    private int overdraftProtection;

    public DebitCard(int odProtection) {
        overdraftProtection = odProtection;
    }

    public override bool canDebit(int amount) {
        return (super.availableFunds + overdraftProtection > amount);       
    }
}

SOLID (Interface Segregation Principle)

The interface-segregation principle (ISP) states that no consumer should be forced to depend on methods it does not use.

SOLID (Dependency Inversion Principle)

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.


B. Abstractions should not depend upon details. Details should depend upon abstractions.

SOLID

By Scott Franks