【问题标题】:php OOP: confused about a method that returns an interfacephp OOP:对返回接口的方法感到困惑
【发布时间】:2014-04-04 13:54:03
【问题描述】:

这段代码是 ZF2,但感觉它可能是一个更普遍的 OOP 问题,因为它与我对接口的一些混淆有关。这个例子有点啰嗦,但我想用我所有的实际代码来展示我正在做的一切。

所以这里的代码 sn-p 是我困惑的根源:

// References
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Adapter\DbTable as DbTableAuthAdapter;

// Class definition
public function getAuthService()
{
if (! $this->authservice) {
   $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
   $dbTableAuthAdapter = new DbTableAuthAdapter($dbAdapter, 'user','email','password', 'MD5(?)');
   $authService = new AuthenticationService();
   $authService->setAdapter($dbTableAuthAdapter);
   $this->authservice = $authService;
}
return $this->authservice;
}

public function processAction()
//
$this->getAuthService()->getAdapter()
   ->setIdentity($this->request->getPost('email'))
   ->setCredential($this->request->getPost('password'));

$result = $this->getAuthService()->authenticate();
if ($result->isValid()) {
   $this->getAuthService()->getStorage()->write($this->request->getPost('email'));
   return $this->redirect()->toRoute(NULL , array( 
      'controller' => 'login', 
      'action' => 'confirm' 
   ));
}

在第一个方法中,我们使用一些参数构造一个新的Zend\Authentication\Adapter\DbTable,将其传递给Zend\Authentication\AuthenticationService 的实例,然后返回该AuthenticationService 的实例。

在下一个方法中,我们调用该方法 ($this->getAuthService()) 来获取 AuthenticationService 的实例,调用 AuthenticationService 的 getAdapter() 方法,然后开始在返回的对象上调用 Zend\Authentication\Adapter\DbTable 方法.

这让我感到困惑。查看定义getAdater()。它实际上并没有返回 Zend\Authentication\Adapter\DbTable 的实例,它只返回一个接口: Zend\Authentication\Adapter\AdapterInterface 并且这个接口没有定义任何 Zend\Authentication\Adapter\DbTable 方法。

所以如果 getAdapter() 只返回一个接口,我怎么能在返回的对象上调用 Zend\Authentication\Adapter\DbTable 方法?

对不起,如果这个问题读起来令人困惑,我对这里发生的事情在一个非常基本的层面上感到困惑,所以我很难更清醒。

【问题讨论】:

    标签: php oop interface zend-framework2


    【解决方案1】:

    Zend\Authentication\AuthenticationService 是如何通过 Design by Contract 方法和(可以说)Strategy Pattern 实现来尊重 SOLID 原则的示例(哇,这真是一口……)。

    该类的架构有点混乱,但我会尝试将其剔除,作为我理解设计模式的练习。 (请务必纠正我的错误)。

    在这种情况下Dipendency Inversion Principle的第一点是AuthenticationService不应该依赖于任何低级认证过程的实现细节,而应该依赖于一个抽象,它只需要一个authenticate()方法封装策略行为
    (奇怪的是,服务本身也提供相同的方法,它是适配器实际使用的地方,是适配器方法的包装器,但让我们忽略它:-p)。

    所以服务不需要关心如何验证是如何完成的,它只需要一个结果。
    进入AdapterInterface 合约,作为策略接口 来定义行为的要求,即从authenticate() 返回一个Result 实例。

    AuthenticationService 因此可以被视为战略背景

    所有这些主要是为了允许插入和退出不同的AdapterInterface 实现,而不必更改AuthenticationService 本身,因此Open/Closed Principle 也受到尊重。

    由于AdapterInterface 是一个抽象依赖项,并且它可以通过getter 访问,因此getter 也必须返回一个抽象,返回任何不同的东西都是没有意义的!

    是的,依赖关系可能完全被混淆了,但是拥有访问器有助于单元测试(您可能必须访问模拟的依赖关系来模拟不同的行为),以及服务的当前实现,正如您在自己的内部看到的那样例如,需要服务使用者(在您的情况下为控制器)在调用身份验证过程之前在适配器上手动设置一些上下文(这实际上破坏了Law of Demeter,但这是另一回事)。

    现在,据我了解,“适配器”这个词有点被滥用,因为在我看来,这与 Adapter Pattern 无关,后者用于不兼容接口之间的互操作。各种“适配器”不应引起任何不兼容性的担忧,它们实际上是通过提供由其合同强制执行但封装在自身内部的共同行为而相互兼容的。

    抛开任何批评,可能值得注意的是整个Zend\Authentication组件基本上是旧ZF1Zend_Auth的一个端口。

    【讨论】:

      【解决方案2】:

      定义说它可以返回任何 AdapterInterface(意味着任何实现它的类),而不是直接返回 AdapterInterface。由于它由 DbTableAuthAdapter 扩展,您可以使用 DbTable 方法。

      【讨论】:

      • 好的,我想我明白了。它返回的对象必须实现接口。但我仍然很困惑——要求对象 returned 来实现接口有什么好处?除了要求传递给 setAdapter() 的对象来实现接口之外,您不需要做任何进一步的事情,因为此时您将知道 getAdapter() 检索到的任何适配器都必须实现它。这似乎是多余的。
      • 这就是所谓的“策略”模式(也可能以不同的名称找到)。它基本上允许身份验证服务与 AdapterInterfaces 的不同实现一起工作,除了在接口中定义的少数特殊情况函数之外,无需了解其实现的任何内容。更多信息:phptherightway.com/pages/Design-Patterns.html(最后)
      • 信息来自 DocBlock:github.com/zendframework/zf2/blob/master/library/Zend/…。这不是必需的,它只是文档 - 它有助于 IDE 之类的东西知道如果某些代码调用 ->getAdapter(),会返回什么样的对象
      • 嗯,现在事情有点清楚了。这是否也能解释为什么使用 ctags 在 Zend 库中跳转的行为不像我预期的那样?
      • 我的 2 美分 - 首先一个接口永远不能被实例化;他们的工作是对任何实现它们的类强制执行公共 API;在 auth 适配器的情况下,它是强制执行的 authenticate() 方法 - 这意味着任何想要进行身份验证的类都需要该方法,而 auth 服务不知道它是如何实现的。该设计也被称为Adapter Pattern
      【解决方案3】:

      PHP 是一种松散类型的语言,这意味着您可以在任何对象上调用任何方法,无论有什么类型提示。如果该方法最终不存在,您将收到运行时错误。另一方面,即使在类型安全的语言中,函数也可以返回任何作为文档类型子类型的对象。这是 Liskov 替换原则的一个特例。

      【讨论】:

        猜你喜欢
        • 2017-09-20
        • 2021-04-19
        • 2019-04-12
        • 1970-01-01
        • 2021-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-27
        相关资源
        最近更新 更多