Videa Blog

SOLID principy: Princip odděleného rozhraní

Michal Katuščák  

Princip odděleného rozhraní je definované Robertem C. Martinem původně pro Xerox a říká:

Více specifických rozhraní je lepší než jedno obecné rozhraní.

Při jeho dodržování se kód stává více znovupoužitelný a užitečný. Pokud je více tříd nuceno implementovat rozhraní s metodami, které nepotřebují, je vhodné najít logický průnik (v čem se shodují) a rozhraní oddělit.

Video (1:29)

Video na Youtube

Na ukázku zde mám rozhraní IBag, třídu Bag, která jej implementuje a třídu Renderer:

<?php

interface IBag {
    public function setContent(string $content);
    public function appendContent(string $content);
    public function prependContent(string $content);
    public function getContent(string $content);
    public function loadFromFile(string $file);
    public function saveToFile(string $file);
    public function render(): string;
}

class Bag implements IBag
{
    private $content;

    public function setContent(string $content)
    {
        $this->content = $content;
    }

    public function appendContent(string $content)
    {
        $this->content = $this->content . $content;
    }

    public function prependContent(string $content)
    {
        $this->content = $content . $this->content;
    }

    public function getContent(string $content)
    {
        return $content;
    }

    public function loadFromFile(string $file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException("File from argument not exists.");
        }

        $this->content = file_get_contents($file);
    }

    public function saveToFile(string $file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException("File from argument not exists.");
        }

        return file_put_contents($file, $this->content);
    }

    public function render(): string
    {
        return "Content: " . $this->content;
    }
}

class Renderer
{
    public function render(IBag $bag)
    {
        echo $bag->render();
    }
}

Větší interface IBag mohu rozdělit na tři logické rozhraní: Contentable, Fileable, Renderable.

Třída Renderer tak už není nucena záviset na IBag, ale postačí Renderable. Třída Bag implementuje všechny tři rozhraní, ale další třídě může stačit už jenom třeba jedno.

<?php

interface Renderable {
    public function render(): string;
}

interface Contentable
{
    public function setContent(string $content);
    public function appendContent(string $content);
    public function prependContent(string $content);
    public function getContent(string $content);
}

interface Fileable
{
    public function loadFromFile(string $file);
    public function saveToFile(string $file);
}

class Bag implements Renderable, Contentable, Fileable
{
    private $content;

    public function setContent(string $content)
    {
        $this->content = $content;
    }

    public function appendContent(string $content)
    {
        $this->content = $this->content . $content;
    }

    public function prependContent(string $content)
    {
        $this->content = $content . $this->content;
    }

    public function getContent(string $content)
    {
        return $content;
    }

    public function loadFromFile(string $file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException("File from argument not exists.");
        }

        $this->content = file_get_contents($file);
    }

    public function saveToFile(string $file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException("File from argument not exists.");
        }

        return file_put_contents($file, $this->content);
    }

    public function render(): string
    {
        return "Content: " . $this->content;
    }
}

class Renderer
{
    public function render(Renderable $bag)
    {
        echo $bag->render();
    }
}

Jako u všech zásad je potřeba dbát na to, aby to nebylo přehnané. Aby nás pak nestrašily třídy se stovkou rozhraní. :)

Zdroje