这段代码是ZF2,但这似乎是一个更普遍的OOP问题,因为它涉及到我对接口的一些混淆。这个例子有点冗长,但是我想使用我所有的实际代码来显示我正在做的所有事情。
下面是造成我困惑的代码片段:
// 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\身份验证\Adapter\DbTable,将它传递给Zend\身份验证\身份验证服务的一个实例,然后返回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方法?
很抱歉,如果这个问题让人困惑的话,我很困惑这里发生了什么,在一个非常基本的层面上,所以我很难变得更加清醒。
发布于 2014-04-04 14:00:53
定义说它可以返回任何AdapterInterface (意味着实现它的任何类),而不是直接返回AdapterInterface。由于它是由DbTableAuthAdapter扩展的,所以您可以使用DbTable方法。
发布于 2014-04-04 17:19:55
Zend\Authentication\AuthenticationService是如何使用按合同设计方法和(可以说是) 战略模式实现来尊重实心原则的一个例子(哇,这真是太棒了……)。
类的体系结构有点混乱,但我将尝试将其作为对设计模式的理解的练习。(只要我错了,请务必纠正我)。
在这种情况下,斜率反演原理的第一点是,AuthenticationService不应该依赖于任何低级身份验证过程的实现细节,而应该依赖于抽象,它只需要一个封装策略行为的authenticate()方法。
(奇怪的是,服务本身也提供了相同的方法,它是适配器实际使用的地方,它是适配器方法的包装器,但让我们忽略它:-p)。
因此,服务不需要关心身份验证是如何完成的,它只需要一个结果。
进入AdapterInterface契约,作为一个策略接口来定义行为的需求,即从authenticate()返回一个Result实例。
因此,AuthenticationService可以被视为策略上下文。
所有这些主要是为了允许插入和输出不同的AdapterInterface实现,而不必更改AuthenticationService本身,因此开放/封闭原则也受到尊重。
由于AdapterInterface是一个抽象依赖项,并且可以使用getter访问它,所以getter也必须返回一个抽象,所以返回任何不同的内容都是没有意义的!
是的,依赖关系可能会被完全混淆,但是有访问器有助于单元测试(您可能必须访问模拟的依赖项来模拟不同的行为),再加上您在自己的示例中可以看到的服务的当前实现,需要服务使用者(在您的例子中是Controller )在调用身份验证过程之前手动在适配器上设置一些上下文(这实际上破坏了德米特定律,但这是另一回事)。
现在,据我所知,“适配器”这个词被误用了,因为在我看来,这与适配器模式无关,后者用于不兼容接口之间的互操作。不同的“适配器”不应引起对任何不兼容性的任何关注,它们实际上是相互兼容的,它们通过提供由它们的合同强制执行的共同行为而封装在它们的内部。
撇开任何批评,值得注意的是,整个Zend\Authentication组件基本上是旧ZF1 Zend_Auth的一个端口。
发布于 2014-04-04 14:26:20
PHP是一种松散类型的语言,这意味着您可以在任何对象上调用任何方法,不管有什么类型提示。如果该方法最终不存在,您将得到一个运行时错误。另一方面,即使在类型中,安全语言函数也可以返回任何作为文档类型的子类型的对象。这是Liskov替代原理的一个特例。
https://stackoverflow.com/questions/22864803
复制相似问题