27
loading...
This website collects cookies to deliver better user experience
abstract
in order to let concrete classes extend the "template" and provide a concrete implementation. This pattern is an example of Inversion Of Control, which I’ve already written about. Based on its nature, Template Method strongly relies on the inheritance principle, but despite this, we can reach pretty much the same results by using composition.abstract class UserRegister
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
final public function register(User $user): void
{
$user->enable();
if (!$this->sendVerification($user)) {
return;
}
$this->em->persist($user);
$this->em->flush();
}
protected abstract function sendVerification(User $user): bool;
}
class EmailVerificationUserRegister extends UserRegister
{
protected function sendVerification(User $user): bool
{
$email = $user->getEmail();
return $this->sendEmail($email);
}
private function sendEmail(string $email): bool
{
// omitted: sending email code
}
}
class PhoneVerificationUserRegister extends UserRegister
{
protected function sendVerification(User $user): bool
{
$phone = $user->getPhoneNr();
return $this->sendSms($phone);
}
private function sendSms(string $phone): bool
{
// omitted: sending sms code
}
}
class NoVerificationUserRegister extends UserRegister
{
protected function sendVerification(User $user): bool
{
$user->verified();
return true;
}
}
class Client
{
private $register;
public function __construct(UserRegister $register)
{
$this->register = $register;
}
public function register(User $user)
{
// do something before registration
$this->register->register($user);
// do something after registration
}
}
$client = new Client(new EmailVerificationUserRegister());
$client->register($new User());
EmailVerificationUserRegister
was randomly choosen and you can both choose another concrete implementation, or configure it with dependency injection mechanism (that’s not reported here in order to keep the complexity as low as possible).class UserRegister
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
final public function register(User $user, $sendVerification): void
{
$user->enable();
if (!is_callable($sendVerification) {
return;
}
$verificationHasBeenSent = call_user_func_array($sendVerification, [$user]);
if (is_bool($verificationHasBeenSent) && $verificationHasBeenSent) {
return;
}
$this->em->persist($user);
$this->em->flush();
}
}
interface UserRegisterInterface
{
public function register(User $user): void;
}
class EmailVerificationUserRegister implements UserRegisterInterface
{
private $register;
public function __construct(UserRegister $register)
{
$this->register = $register;
}
public function register(User $user)
{
$this->register->register($user, [$this, 'sendVerification']);
}
private function sendVerification(User $user): bool
{
$email = $user->getEmail();
return $this->sendEmail($email);
}
private function sendEmail(string $email): bool
{
// omitted: sending email code
}
}
call_user_func_array
and to the fact that UserRegister::register
has no typehint on the callback. I could have done something likeclass UserRegister
{
[...]
final public function register(User $user, \Closure $sendVerification): void
{
$user->enable();
$verificationHasBeenSent = $sendVerification($user);
if (is_bool($verificationHasBeenSent) && $verificationHasBeenSent) {
return;
}
[...]
}
}
class EmailVerificationUserRegister implements UserRegisterInterface
{
[...]
public function register(User $user)
{
$this->register->register($user, function (User $user) { return $this->sendVerification($user); });
}
[...]
}
call_user_func_array
is much safer. Moreover I needed to introduce UserRegisterInterface
in order to use it in Client
class (that I won’t show again because the only thing that changes is the typehint in its constructor).interface VerificationStrategy
{
public function sendVerification(User $user): bool;
}
class UserRegister
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
final public function register(User $user, VerificationStrategy $strategy): void
{
$user->enable();
if (!$strategy->sendVerification($user)) {
return;
}
$this->em->persist($user);
$this->em->flush();
}
}
class EmailVerificationUserRegister implements VerificationStrategy
{
public function sendVerification(User $user): bool
{
$email = $user->getEmail();
return $this->sendEmail($email);
}
private function sendEmail(string $email): bool
{
// omitted: sending email code
}
}
class Client
{
private $register;
public function __construct(UserRegister $register)
{
$this->register = $register;
}
public function register(User $user, VerificationStrategy $strategy)
{
// do something before registration
$this->register->register($user, $strategy);
// do something after registration
}
}
$client = new Client();
$client->register(new User(), new EmailVerificationUserRegister());