2016-07-22 127 views
2

我对这个神秘的标题感到非常抱歉,但我真的不知道如何用简短的标题风格来形容它。PHP:如何在不违反SOLID原则的情况下使用扩展接口?

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

+0

我可能会在这里得到错误的结尾,但是你没有实例化接口,所以这种事情对我来说没什么意义:'公共函数成功(ConfirmationObjectInterface $ object)'< - 是一个接口或对象(类实例)? – CD001

+0

@ CD001它强制传递的对象来实现接口,否则php会抛出一个错误。 –

+0

@FélixGagnon-Grenier确实有效吗? – CD001

回答

0

我不清楚为什么确认对象应该实现两个接口。从我在这里看到的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()期待呢。

+0

这是简化的代码。 'ConfirmationObjectInterface'实际上有自己的方法,例如“getType”,“getEmail”或“getToken”。我当前的实现实际上取决于关联数组,因为我无法更好地解决这个问题,但我开始怀疑是否真的没有其他方法:)。 –

+0

这是什么意思,“更好”?超严格型安全并不总是更好。如果'ConfirmationObjectInterface'中的所有方法都是getter,那么这些对象实际上就是数据结构,并不一定是接口。接口的目的是从“what”抽象出“how”,为最简单的东西创建额外的接口只会增加复杂性,并不能解决任何实际问题。 –

+0

这是一个很好的观点。我把这个特定的项目变成了一个练习,将界面推到极限,看看他们什么时候停止有意义 - 以提高我对它们的理解。我会考虑你的答案,如果它是正确的,那么我会完全接受它:)。 –