PHP: 在这种情况下,如何在不违反SOLID原则的情况下使用扩展接口?
PHP: How to use extended interfaces without violating SOLID principles in this case?
对于这个神秘的标题,我感到非常抱歉,但老实说,我不知道如何用简短的 title-style 时尚来描述它。
第一个简短版本。简单的电子邮件确认机制。一种方法是发送带有确认信息的电子邮件 link。单击 link 后,另一个控制器调用第二个方法,该方法验证来自 URL 的令牌。在这两个操作之间,ConfirmationObject 与令牌和可能的其他数据一起被存储。确认成功后 "successHandler" 正在使用中。
简化代码:
interface SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object);
}
class EmailTester {
public function try(ConfirmationObjectInterface $object) {
// some code
}
public function confirm($token) {
$confirmationObject = $this->repository->findByToken($token);
$type = $confirmationObject->getType();
$successHandler = $this->handlersRegistry->getSuccessHandler($type);
$successHandler->success($confirmationObject);
}
}
现在我们要这样使用它:
// Firstly let's implement our own success handler.
class UserRegistrationSuccessHandler implements SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object) {
// Do some stuff on success.
}
}
// Then let's register this success handler to be available in our `handlersRegistry` object.
$handlersRegistry->addType('user_registration', new UserRegistrationSuccessHandler());
// Now we will extend ConfirmationObjectInterface
interface RegistrationConfirmationObjectInterface extends ConfirmationObjectInterface {
public function getSomeDataGivenOnRegistration();
}
// And at the end, let's try our email
$confirmationObject = new RegistrationConfirmationObject(); // Which implements above interface.
// $confirmationObject->getType() === 'user_registration'
$emailTester->try($confirmationObject);
// Now confirmation link with token is being sent to the given email. If user will click it, below method will be invoked.
$emailTester->confirm($token);
现在的问题是,我宁愿在成功处理程序中使用 RegistrationConfirmationObjectInterface
,而不是 ConfirmationObjectInterface
。
我知道我能做到:
// Firstly let's implement our own success handler.
class SuccessHandler implements SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object) {
if ($object instanceof RegistrationConfirmationObjectInterface) {
// Do stuff
}
}
}
但感觉很糟糕。此检查毫无意义,因为 $object
将始终是 RegistrationConfirmationObjectInterface
的实例。这个设计有什么缺陷,如何改进?
我不清楚为什么确认对象应该实现两个接口。从我在这里看到的情况来看,RegistrationConfirmationObjectInterface
只有一种方法 returns 一些数据结构,而 ConfirmationObjectInterface
根本没有任何方法。这里真的需要严格的类型安全,特别是如果您确定您的自定义 SuccessHandler
将始终收到 RegistrationConfirmationObjectInterface
吗?
如果 ConfirmationObjectInterface
实现不包含任何逻辑并且只是数据结构,请将它们替换为关联数组。否则,我会建议这样的事情:
interface ConfirmationObjectInterface
{
/**
* @return array
*/
public function getData();
}
class RegistrationConfirmationObject implements ConfirmationObjectInterface
{
public function getData()
{
return ['data specific to registration here'];
}
}
class SomethingElseConfirmationObject implements ConfirmationObjectInterface
{
public function getData()
{
return ['data specific to something else'];
}
}
由于自定义处理程序特定于具体类型,因此它们无论如何都会知道从 getData()
中期望得到什么数据。
对于这个神秘的标题,我感到非常抱歉,但老实说,我不知道如何用简短的 title-style 时尚来描述它。
第一个简短版本。简单的电子邮件确认机制。一种方法是发送带有确认信息的电子邮件 link。单击 link 后,另一个控制器调用第二个方法,该方法验证来自 URL 的令牌。在这两个操作之间,ConfirmationObject 与令牌和可能的其他数据一起被存储。确认成功后 "successHandler" 正在使用中。
简化代码:
interface SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object);
}
class EmailTester {
public function try(ConfirmationObjectInterface $object) {
// some code
}
public function confirm($token) {
$confirmationObject = $this->repository->findByToken($token);
$type = $confirmationObject->getType();
$successHandler = $this->handlersRegistry->getSuccessHandler($type);
$successHandler->success($confirmationObject);
}
}
现在我们要这样使用它:
// Firstly let's implement our own success handler.
class UserRegistrationSuccessHandler implements SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object) {
// Do some stuff on success.
}
}
// Then let's register this success handler to be available in our `handlersRegistry` object.
$handlersRegistry->addType('user_registration', new UserRegistrationSuccessHandler());
// Now we will extend ConfirmationObjectInterface
interface RegistrationConfirmationObjectInterface extends ConfirmationObjectInterface {
public function getSomeDataGivenOnRegistration();
}
// And at the end, let's try our email
$confirmationObject = new RegistrationConfirmationObject(); // Which implements above interface.
// $confirmationObject->getType() === 'user_registration'
$emailTester->try($confirmationObject);
// Now confirmation link with token is being sent to the given email. If user will click it, below method will be invoked.
$emailTester->confirm($token);
现在的问题是,我宁愿在成功处理程序中使用 RegistrationConfirmationObjectInterface
,而不是 ConfirmationObjectInterface
。
我知道我能做到:
// Firstly let's implement our own success handler.
class SuccessHandler implements SuccessHandlerInterface {
public function success(ConfirmationObjectInterface $object) {
if ($object instanceof RegistrationConfirmationObjectInterface) {
// Do stuff
}
}
}
但感觉很糟糕。此检查毫无意义,因为 $object
将始终是 RegistrationConfirmationObjectInterface
的实例。这个设计有什么缺陷,如何改进?
我不清楚为什么确认对象应该实现两个接口。从我在这里看到的情况来看,RegistrationConfirmationObjectInterface
只有一种方法 returns 一些数据结构,而 ConfirmationObjectInterface
根本没有任何方法。这里真的需要严格的类型安全,特别是如果您确定您的自定义 SuccessHandler
将始终收到 RegistrationConfirmationObjectInterface
吗?
如果 ConfirmationObjectInterface
实现不包含任何逻辑并且只是数据结构,请将它们替换为关联数组。否则,我会建议这样的事情:
interface ConfirmationObjectInterface
{
/**
* @return array
*/
public function getData();
}
class RegistrationConfirmationObject implements ConfirmationObjectInterface
{
public function getData()
{
return ['data specific to registration here'];
}
}
class SomethingElseConfirmationObject implements ConfirmationObjectInterface
{
public function getData()
{
return ['data specific to something else'];
}
}
由于自定义处理程序特定于具体类型,因此它们无论如何都会知道从 getData()
中期望得到什么数据。