2014-04-04 22 views
1

這段代碼是ZF2,但是這樣的感覺就像它可能是一個更普通的OOP問題,因爲它關於接口的一些混淆。這個例子有點長,但我想用我所有的代碼來展示我正在做的一切。PHP 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\Authentication\Adapter\DbTable一些PARAMS第一種方法,將它傳遞給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方法?

對不起,如果這個問題令人困惑,讀我很困惑這裏發生了什麼在一個非常基本的水平,所以它很難讓我更清醒。

回答

1

定義說它可以返回任何AdapterInterface(意味着實現它的任何類),而不是直接返回AdapterInterface。由於它由DbTableAuthAdapter擴展,所以可以使用DbTable方法。

+0

好吧,我想我明白了。它返回的對象必須實現接口。但是我仍然感到困惑 - 需要_returned_來實現接口的優點是什麼?除了要求傳遞給setAdapter()的對象來實現接口之外,您不必再進行任何操作,因爲此時您將知道getAdapter()檢索到的任何適配器都必須實現它。看起來多餘。 – red888

+0

這就是所謂的「策略」模式(可能也可以在不同的名字下找到)。它基本上允許身份驗證服務與AdapterInterfaces的不同實現一起工作,除了在接口中定義的幾個特殊情況函數外,無需知道其實現上的任何內容。更多信息:http://www.phptherightway.com/pages/Design-Patterns.html(最後) – phoops

+1

信息來自DocBlock:https://github.com/zendframework/zf2/blob/master/library /Zend/Authentication/AuthenticationService.php#L49。這不是要求,它只是文檔 - 它幫助像IDEs這樣的東西知道,如果某些代碼調用' - > getAdapter()',返回什麼樣的對象 –

1

PHP是一種鬆散類型的語言,這意味着您可以在任何對象上調用任何方法,而不管它有什麼類型的提示。如果方法最終不存在,則會得到運行時錯誤。另一方面,即使在類型安全的語言中,函數也可以返回任何屬於記錄類型的子類型的對象。這是Liskov替代原理的特例。

2

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組件基本上是舊ZF1 Zend_Auth的一個端口。