S.O.L.I.D – Object Oriented Design – Single Responsibility



S.O.L.I.D – Object Oriented Design – Single Responsibility

0 0


solid-presentation

A presentation on SOLID Object Oriented Design

On Github m1ck3y / solid-presentation

S.O.L.I.D

Object Oriented Design

Created by Jon Kuperman and Shane Iler www.jonkuperman.com/solid

S.O.L.I.D

These principles when applied together intend to make it more likely that a programmer will create a system that is easy to maintain and extend over time.

The Principles

Single responsibility - A class should only do one thing Open-closed - You shouldn't need to change your classes Liskov substitution - Child classes should behave like their parents Interface segregation - Classes shouldn't know more than they need to Dependency inversion - Classes should depend on abstractions, not each other

Single Responsibility

A class should have only one reason to change.

Too Many Responsibilities

class Blog
{
    public function renderPost($id)
    {
        $post = $this->getPost($id);
        $html = $this->formatPost($post);

        return $html;
    }

    private function getPost($id) { ... }
    private function formatPost($post) { ... }
}

The Problem

Classes that do too many things are difficult to extend and are more likely to break when one aspect needs to be changed.

Good Example

class Blog
{
    public function renderPost($id)
    {
        $post = Post::find($id);
        return View::make('post.display', $post);
    }
}

Open-Closed

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Needs Constant Modification

class RenderPost
{
    public function render(Post $post)
    {
        switch($post->type)
        {
        case 'poll':
            return View::make('poll.single', $post);
        case 'blog':
            return View::make('blog.single', $post);
        case 'video':
            return View::make('video.single', $post);
        default:
            throw new \Exception('Oh no!');
        }
    }
}

The Problem

Classes need to be able to be modified without changing the production source code.

Good Example

interface Renderable
{
    public function render($post);
}

class Poll extends Post implements Renderable
{
    public function render($post)
    {
        return View::make('poll.single', $post);
    }
}

Liskov Substitution Principle

If A is a subtype of B, then objects of type B may be replaced with objects of type A.

Different Return Types

class PostModel implements Model
{
    public function get($id)
    {
        $postObject = Post::find($id)
        return $postObject;
    }
}
class PollModel implements Model
{
    private $pollArrays;

    public function get($id)
    {
        return $pollArrays[$id];
    }
}

The Problem

You find yourself compensating for differences in objects that implement an interface.

Good Example

class PostModel implements Model
{
    public function get($id)
    {
        return Post::find($id);
    }
}

class PollModel implements Model
{
    public function get($id)
    {
        return Poll::find($id);
    }
}

Interface Segregation

A class should not be forced to depend on methods it does not use.

Too Many Requirements

interface User
{
    public function sendEmail(Email $email);
    public function receiveEmail();
    public function drinkCoffee();
    public function takeAWalk();
}

class Emailer
{
    public function sendEmail(User $user, $message)
    {
        $user->sendEmail($message);
    }
} 

The Problem

Using interfaces to further describe the intent of the software is often a good idea.

Good Example

interface Emailable
{
    public function sendEmail($message);
}

class Emailer
{
    public function sendEmail(Emailable $user, $message)
    {
        $user->sendEmail($message);
    }
}

Dependency Inversion

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

Absolute Dependencies

class MySQLConnection
{
    private $hostname;
    private $user;

    public function __construct($hostname) { ... }
    public function connect($user) { ... }
}

class PasswordReminder
{
    public function __construct(MySQLConnection $connection, $user)
    {
        $connection->connect($hostname, $user);
        $this->connection = $connection;
    }
}

The Problem

Classes that instantiate other classes are impossible to test. Furthermore, dependency injection that passes in too much or too specific information breaks the Interface Segregation principle.

Good Example

interface Connection
{
    public function connect($hostname, $user);
    ...
}

class MySQLConnection implements Connection { ... }

class PasswordReminder
{
    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }
}

Dependency Injection Container

$container['hostname'] = 'localhost';
$container['user'] = 'not_root';

$container['connection'] = function ($c) {
    $conn = new MySQLConnection();
    $conn->connect($c['hostname'], $c['user']);
    return $conn;
};

$container['passwordReminder'] = function ($c) {
    return new PasswordReminder($c['connection']);
};

$reminder = $container['passwordReminder'];
$reminder->remind();

Principles, Not Laws!

Like design patterns, these are guidelines. They are pieces of information to follow if there is no compelling reason to do something differently. Software development will always come down to your best judgement.

Resources