Един добър пример за 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);
}
}
interfaceIDatabase
{public function AddRow(string $table, string $value) : void
; }class Database
implementsIDatabase
{
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 : newDVDOperations
; $operator->write($someData);
Защо? Защото най-малкото можем да си напишем много подобни Оператори, който ще са лесно заменими.