„Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request long the chain until an object handles it.„
„The pattern frees an object from knowing which other object handles a request.“ Демек, кой точно ще свърши необходимата работа е въпрос на някаква допълнителна логика.
„…and an object in the chain doesn’t have to know about the chain’s structure.“
„Instead of objects maintaining references to all candidate receivers, they keep a
single reference to their successor.“
„Since a request has no explicit receiver, there’s no
guarantee it’ll be handled – the request can fall off the end of the chain without
ever being handled. A request can also go unhandled when the chain is not
configured properly.“
Имаме група обекти, които работят в комбина, т.е. всеки прави своята си работа, но си предават действието, което трябва да се извърши един на друг,
докато не се намери кой да го изпълни. т.е. работата, която трябва да се свърши е една, но не се знае кой точно обект да я свърши.
Като в Държавната администрация – всеки те прехвърля на другото гише. Или за дадено нещо трябва да минеш през 5 гишета.
Или като една стара игра, където пускаш отгоре едно топче и то минава през различни възможни пътища, през различни разклонения, но предварително си задал според някаква конкретна логика на къде да поеме.
Затова си правим набор от класове и ръчно задаваме кой след кого е във въпросната „верига на отговорности“.
И подаваме задачата на първия. В него първо проверяваме дали той може да я свърши и ако не – предаваме задачата на следващия…. и така нататък докато попаднем на клас, който може да я свърши. Ако не попаднем – трябва да имаме по default клас, който например да покаже, че не сме обработили задачата.
Примерна програмна реализация
Първо, имаме един интерфейс ChainInterface където имаме декларирани две метода – setNextChainLink() и doTheJob(), личи си от имента им кой какво прави.
В setNextChainLink() ще се задава следващият елемент от веригата, а в doTheJob() ще проверяваш дали е за него работата и ако да – свърши я,
ако не – предай нататък.
Пак повтатям – задаването на „веригата от отговорности“ става предварително и ръчно. Примерно, правим си 5 класа, които трябва да имплементират
интерфейса ChainInterface, и всеки ще има едно private пропърти $nextChainLink където в конструктора, като създаваме този обект, ще му подаваме обект, създаден преди.
Този design pattern е много удобен например за избягване от заплетена логика, вложени IF-ове и т.н… Просто трябва логиката да се организира поточно.
Пример:
interface ChainInterface { public function setNextChainLink(ChainInterface $oChainLink); public function doTheJob($jobToBeDone); } class Responsible1 implements ChainInterface { private ChainInterface $nextChainLink;public function setNextChainLink(ChainInterface $oChainLink = NULL)
{
if (empty($oChainLink)) {
// do smth...
} else {
$this->nextChainLink = $oChainLink;
}
}
public function doTheJob($jobToBeDone)
{
if ($jobToBeDone['job'] == 'add') {
return $jobToBeDone['tova'] + $jobToBeDone['onova'];
}
if (empty($this->nextChainLink)) {
return 'Stignahme do kraq i ne namerih ChainInterface, kojto da obraboti tazi zada4a.';
}
return $this->nextChainLink->doTheJob($jobToBeDone);
}
} class Responsible2 implements ChainInterface { private ChainInterface $nextChainLink;public function setNextChainLink(ChainInterface $oChainLink = NULL)
{
if (empty($oChainLink)) {
// do smth...
} else {
$this->nextChainLink = $oChainLink;
}
}
public function doTheJob($jobToBeDone)
{
if ($jobToBeDone['job'] == 'sub') {
return $jobToBeDone['tova'] - $jobToBeDone['onova'];
}
if (empty($this->nextChainLink)) {
return 'Stignahme do kraq i ne namerihme ChainInterface, kojto da obraboti tazi zada4a.';
}
return $this->nextChainLink->doTheJob($jobToBeDone);
}
} class Responsible3 implements ChainInterface { private ChainInterface $nextChainLink;public function setNextChainLink(ChainInterface $oChainLink = NULL)
{
if (empty($oChainLink)) {
// do smth...
} else {
$this->nextChainLink = $oChainLink;
}
}
public function doTheJob($jobToBeDone)
{
if ($jobToBeDone['job'] == 'mul') {
return $jobToBeDone['tova'] * $jobToBeDone['onova'];
}
if (empty($this->nextChainLink)) {
return 'Stignahme do kraq i ne namerihme ChainInterface, kojto da obraboti tazi zada4a.';
}
return $this->nextChainLink->doTheJob($jobToBeDone);
}
} $resp1 = new Responsible1(); $resp2 = new Responsible2(); $resp3 = new Responsible3(); $resp1->setNextChainLink($resp2); $resp2->setNextChainLink($resp3); $res = $resp1->doTheJob(array('tova' => 4, 'onova' => 3, 'job' => 'add')); var_dump($res);
Литература:
https://refactoring.guru/design-patterns/chain-of-responsibility