【问题标题】:PHP: How to use extended interfaces without violating SOLID principles in this case?PHP:在这种情况下如何在不违反 SOLID 原则的情况下使用扩展接口?
【发布时间】:2016-07-22 15:44:24
【问题描述】:

我很抱歉这个神秘的标题,但老实说,我不知道如何用简短的标题风格来描述它。

第一个短版。简单的电子邮件确认机制。一种方法是发送带有确认链接的电子邮件。单击链接后,另一个控制器调用第二个方法,该方法验证来自 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 的一个实例。这种设计有什么缺陷,如何改进?

【问题讨论】:

  • 我可能在这里搞错了,但你没有实例化接口,所以这种事情对我来说没有多大意义:public function success(ConfirmationObjectInterface $object)
  • @CD001 强制传入的对象实现接口,否则php会报错。
  • @FélixGagnon-Grenier 这真的有效吗?
  • 是的,在编写接口时,编码实际上开始变得有趣。有助于解耦代码,通常对 karma 有好处;)
  • 以及多种形式的RegistrationConfirmation如何?

标签: php oop interface liskov-substitution-principle


【解决方案1】:

我不清楚为什么确认对象应该实现两个接口。从我在这里看到的情况来看,RegistrationConfirmationObjectInterface 只有一个方法可以返回一些数据结构,而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() 的数据。

【讨论】:

  • 这是简化的代码。 ConfirmationObjectInterface 实际上有自己的方法,例如“getType”、“getEmail”或“getToken”。我目前的实现实际上完全依赖于关联数组,因为我无法更好地解决这个问题,但我开始怀疑是否真的没有其他方法:)。
  • “更好”是什么意思?超严格的类型安全并不总是更好。如果ConfirmationObjectInterface 中的所有方法都是getter,那么这些对象实际上是数据结构,不一定是接口。接口的目的是从“什么”中抽象出“如何”,为最简单的东西创建额外的接口只会增加复杂性,并不能解决任何实际问题。
  • 这是一个很好的观点。我把这个特殊的项目变成了一种将接口推向极限的练习,看看它们什么时候不再有意义——以提高我对它们的理解。我会考虑你的答案,如果它是正确的,那么我会完全接受它:)。
猜你喜欢
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 2018-06-30
  • 2011-08-20
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
相关资源
最近更新 更多