Tight coupling and Loose coupling

Един добър пример за loose coupling е например, ако хвърля топка към някого за да я хване. Интересува ме той да я хване, не ми трябва да знам на колко години е, какво е закусвал тази сутрин и т.н… Щом просто свършим работата, колкото и по-малко да знаем един за друг, ето това е loose coupling.

Един пример за tight coupling и даже по-скоро за rigid and fragile код:

class CustomerRepository
{
    private Database $database;     

    public function __construct(Database $database)
    {
        $this->database = $database;
    }

    public function Add(string $tableName, string $customerName) : void
    {
        $this->database->AddRow('John Doe', $customerName);
    }
}

class Database
{
    public function AddRow(string $table, string $value) : void 
    {
        //...
    } 
}

Показаният код е реално напълно редовен, но проблемът може да се види в перспектива, защото ако променим нещо в AddRow() методът на класа Database, това би счупило използването му в CustomerRepository->Add().
Каквото и да било, поредноста на аргументите, типовете на съответните им параметри…

Демек, хвърлям ти топката но дали ще я хванеш…
За да съм сигурен, че ще я хванеш, ще те задължа да имаш съответният метод, със съответният signature.

Добър пример за това как да превърнем горният пример от tight coupled в loose coupled, би бил например да задължим с интерфейс методът AddRow на класа Database да спазва зададен signature.

class CustomerRepository
{
    private IDatabase $database;     

    public function __construct(IDatabase $database)
    {
        $this->database = $database;
    }

    public function Add(string $tableName, string $customerName) : void
    {
        $this->database->AddRow('John Doe', $customerName);
    }
}

interface IDatabase
{
    public function AddRow(string $table, string $value) : void;
}

class Database implements IDatabase
{
    public function AddRow(string $table, string $value) : void 
    {
        //...
    }
}

Можем да си направим извод, че loose coupling не значи непременно по-голяма либералност, а както се вижда в случая – даже обратното. По-скоро стандартизация, защото така премахваме опасността от различни разновидности (по signature например) на методът AddRow()

Демек, от Add() на CustomerRepository викаме директно AddRow() на Database, но той е длъжен да спазва сигнъчъра на интерфейса IDatabase. Като с цел взаимозаменяемост, той може да има различни имплементации.

Друг пример за това как можем да опростяваме нещата и да ги направим по-гъвкави и взаимозаменяеми.
Например имаме близки като функционалност неща, и за отделните от тях правим отделни, независими класове и съответно, викаме когато който ни трябва.
Вместо това можем пак да имаме отделни класове, но имплементиращи общ интерфейс.

class DiscOperations
{
    public function doDiscWrite(string $data) : bool
    {
        //...
    }
}

class DVDOperations
{
    public function doDVDWrite(string $data) : bool
    {
        //...
    }
}

if (WRITE2DISC) {
    (new DiscOperations)->doDiscWrite($someData);
} else {
    (new DVDOperations)->doDVDWrite($someData);
}

Не би ли по-унифицирано ако например…

interface StorageOperations
{
    public function write(string $data) : bool;
}

class DiscOperations implements StorageOperations
{
    public function write(string $data) : bool
    {
        //...
    } 
} 

class DVDOperations implements StorageOperations
{
    public function write(string $data) : bool
    {
        //...
    }
}

$operator = WRITE2DISC ? new DiscOperations : new DVDOperations;

$operator->write($someData);

Защо? Защото най-малкото можем да си напишем много подобни Оператори, който ще са лесно заменими.

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *