-
-
Save mhlavac/465a0efc33ddaae61218 to your computer and use it in GitHub Desktop.
<?php | |
interface Greeter | |
{ | |
public function greet($name); | |
} | |
class ChainGreeter implements Greeter | |
{ | |
private $greeters = []; | |
public function addGreeter(Greeter $greeter) | |
{ | |
$this->greeters[] = $greeter; | |
} | |
public function greet($name) | |
{ | |
foreach ($this->greeters as $greeter) { | |
$greeter->greet($name); | |
} | |
} | |
} | |
class StringGreeter() implements Greeter | |
{ | |
private $sentence; | |
public function __construct($sentence) | |
{ | |
$this->sentence = $sentence; | |
} | |
public function greet($name) | |
{ | |
spritnf($sentence, $name); | |
} | |
} | |
$chainGreeter = new ChainGreeter(); | |
$chainGreeter->add(new StringGreeter('Hello %s')); | |
$chainGreeter->add(new StringGreeter('Good morning %s')); | |
$chainGreeter->greet('John Doe'); |
From what I know I would be pick Chain
because it's Chain of Responsibility
pattern. Container would be confusing and I think that Composite is something different, isn't it?
<?php
interface Greeter
{
public function greet($name);
}
class StringGreeter() implements Greeter
{
private $sentence;
public function __construct($sentence)
{
$this->sentence = $sentence;
}
public function greet($name)
{
spritnf($sentence, $name);
}
}
class GreeterCollection
{
private $greeters = [];
public function addGreeter(Greeter $greeter)
{
$this->greeters[] = $greeter;
}
public function getArray()
{
return $this->greeters;
}
}
class GreeterCommand
{
private $greeterCollection;
public function __construct(GreeterCollection $greeterCollection)
{
$this->greeterCollection = $greeterCollection;
}
public function execute($name)
{
foreach ($this->greeterCollection->getArray() as $greeter) {
$greeter->greet($name);
}
}
}
$greeterCollection = new GreeterCollection();
$greeterCollection->add(new StringGreeter('Hello %s'));
$greeterCollection->add(new StringGreeter('Good morning %s'));
$greeterCommand = new GreeterCommand($greeterCollection);
$greeterCommand->execute('John Doe');
I would rather use the design above, responsibilities are kept separately then.
P.S.: GreeterCollection should have a design of iterator, but I didn't focus on that.
Jakub I like your approach, it's a nice way how to separate responsibilities and btw. another name for original Chain
can be also Collection
;-)
@mhlavac I see Collection
as a collection of objects being on the same level, but the Chain
is an object inside an object inside an object (auto na auta na auta) :-).
@jakubzapletal exactly, or at least if there is a foreach that goes over inner objects it ends as soon as it finds one. Like this example from ChainUserProvider:
/**
* {@inheritdoc}
*/
public function loadUserByUsername($username)
{
foreach ($this->providers as $provider) {
try {
return $provider->loadUserByUsername($username);
} catch (UsernameNotFoundException $notFound) {
// try next one
}
}
$ex = new UsernameNotFoundException(sprintf('There is no user with name "%s".', $username));
$ex->setUsername($username);
throw $ex;
}
@mhlavac accurate example of Chain of Responsibilities
This is not Chain of Responsibility pattern: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern This is Composite: https://en.wikipedia.org/wiki/Composite_pattern
Haha the naming problem :)
Would agree with @pavel-dohnal-momentumft. To me this seems like the composite pattern.
So CompositeGreeter
? 😄
@pavel-dohnal-momentumft I also thought it's a composite, because this is how usuall composites are implemented.
So in the end I came with 2 ideas.
Command
where we can use just __invoke. So it feels almost like named closure
.
class GreetCommand
{
/**
* @var Greeter[]
*/
private $greeters;
public function __construct(array $greeters = [])
{
$this->greeters = $greeters;
}
public function __invoke($name)
{
foreach ($this->greeters as $greeter) {
$greeter->greet($name);
}
}
}
And simple Greeters
, because it's just multiple Greeters anyway.
class Greeters
{
/**
* @var Greeter[]
*/
private $greeters;
public function __construct(array $greeters = [])
{
$this->greeters = $greeters;
}
public function greet($name)
{
foreach ($this->greeters as $greeter) {
$greeter->greet($name);
}
}
}
What do you think? ;-)
The question is what the name of ChainGreeter should be? Should it be Chain, Composite, Container?
From https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern I would expect chain to to behave like example below, but still when I search for Chain usage in my project using Symfony, I get a lot of chains that behave exactly like
ChainGreeter
in example:Container sounds more like a DI and I never saw anyone use Composite even though it feels like Composite.
What do you think?