2011-06-02 63 views
6

我正在構建一個類來處理PayPal IPNs作爲項目的一部分,因爲我已經知道我將需要在至少兩個即將到來的工作中再次使用它 - 我希望確保我的結構能夠讓我重新使用它,而不必重新編碼類 - 我只想處理業務邏輯中的更改。PHP結構 - 接口和stdClass vars

問題的第一部分是re。接口。我還沒有完全掌握它們的用處以及何時何地部署它們。如果我有我的類文件(「class.paypal-ipn.php」),我是否在該文件中實現了接口?

這裏就是我與迄今爲止的工作(功能列表是不完整的,但它只是爲了舉例說明):

CLASS.PAYPAL-IPN-BASE.PHP

interface ipn_interface { 

    //Database Functions 
    // Actual queries should come from a project-specific business logic class 
    // so that this class is reusable. 

    public function getDatabaseConnection(); 
    public function setDatabaseVars($host="localhost",$user="root",$password="",$db="mydb"); 
    public function dbQuery($SQL); 

    //Logging Functions 

    public function writeLog($logMessage); 
    public function dumpLogToDatabase(); 
    public function dumpLogToEmail(); 
    public function dumpLogToFile(); 

    //Business Logic Functions 

    private function getTransaction($transactionID); 

    //Misc Functions 

    public function terminate(); 
} 

class paypal_ipn_base { 

    //nothing to do with business logic here. 

    public function getDatabaseConnection() { 
    } 

    public function setDatabaseVars($host="localhost",$user="root",$password="",$db="mydb") { 
    } 

    public function dbQuery($SQL) { 
    } 

} 

CLASS .PAYPAL-IPN.PHP

final class paypal_ipn extends paypal_ipn_base implements ipn_interface { 

    //business logic specific to each project here 

    private function getTransaction($transactionID) { 

     $SQL = "SELECT stuff FROM table"; 
     $QRY = this->dbQuery($SQL); 

     //turn the specific project related stuff into something generic 

     return $generic_stuff; //to be handled by the base class again. 

    } 

} 

使用

在這個項目中:

  • 需要持續的基礎,而業務邏輯類的類文件。
  • Instatiate * paypal_ipn *
  • 編寫代碼

在其他項目:

  • 複製在基座的IPN類
  • 編輯/重寫業務邏輯類* paypal_ipn *的限制範圍內的界面。
  • 實例化* paypal_ipn *
  • 編寫代碼

所以你可以即時從字面上只是用它來定義的相關功能組並添加註釋見。它使得它更容易閱讀,但它對我來說有什麼(如果有的話)其他好處 - 是否可以將擴展器和基類拉到一起,並在缺少某些東西時強制執行錯誤?

stdClass的問題

問題的第二部分上的可讀性方面構建。在類本身中,存儲變量的數量不斷增加,有些是在構造函數中設置的,有些是由其他函數設置的 - 它們與諸如保存數據庫連接變量(和連接資源本身),代碼是否應該運行在測試模式下,日誌和日誌本身的設置,等等...

我已經開始剛剛建立他們按通常(再次,下面不完整的&用於說明):

$this->dbConnection = false; 
$this->dbHost = ""; 
$this->dbUser = ""; 
$this->enableLogging = true; 
$this->sendLogByEmail = true; 
$this->sendLogTo = "[email protected]"; 

但後來我想通了日益增長的名單可以與一些結構做的,所以我適應它於:

$this->database->connection = false; 
$this->database->host = ""; 
$this->database->user = ""; 
$this->logging->enable = true; 
$this->logging->sendByEmail = true; 
$this->logging->emailTo = "[email protected]"; 

,給了我的變量更容易閱讀列表,當我轉儲整個班級出來,因爲我的代碼&測試。

一旦完成,我打算寫一個項目特定的擴展到泛型類,我將保留查詢的實際SQL - 從一個項目到另一個項目,Paypal的IPN過程和邏輯不會改變 - 但每個項目的數據庫結構都是這樣的,所以擴展到這個類將會把所有東西都清理回單一格式,所以基類不需要擔心它,並且一旦寫入就不需要修改。

所以總而言之只是一個理智的檢查 - 在我走得太遠這條路之前,它看起來像是正確的方法嗎?

回答

5

如果您使用的類自動加載器,我強烈建議您不要將接口和類保留在同一個文件中,以便接口可以自動加載而無需首先加載實現它的這個類。

有關自動載入的詳細信息: http://php.net/manual/en/language.oop5.autoload.php

你可能要考慮的另一件事是,一個給定的類可以impliment多個接口,以及多種類型的可以實現相同的接口。

接口主要用於各種設計模式,執行規則以及從任何相關類中分離類。當你將一個類與它的依賴關係分開時,它使得以後修改代碼變得更加容易。例如,假設您有一個類A,它接受另一個類B作爲參數,並且這個類遍佈在您的代碼中。你想強制只有一個具有特定方法子集的類可以被接受爲這個參數,但是你不想將輸入限制爲一個具體的類並且它是後代。在將來,您可能會編寫一個完全不同的類,它不會擴展B類,但可以用作類A的輸入。這就是您使用接口的原因。它是類之間的可重用合約。

有人會爭辯說,由於PHP是一種動態語言,接口是一個不必要的複雜因素,可以使用鴨式打字。然而,我發現在大型多用戶代碼庫中,接口可以節省大量時間,讓您更多地瞭解一個類如何使用另一個類,而無需深入研究代碼。

如果您發現自己需要在對象或函數之間傳遞的變量的大量列表,它們通常最終會得到一類自己的變量,但每種情況都不相同。

- 依賴注入例如 -

class A implements AInterface { 
    public function foo($some_var) {} 
} 

interface AInterface { 
    public function foo($some_var); 
} 

class B { 
    protected $localProperty; 

    // inject into the constructer. usually used if the object is saved in a property and used throughout the class 
    public function __construct(AInterface $a_object) { 
     $this->localProperty = $a_object; 
    } 

    // inject into a method. usually used if the object is only needed for this particular method 
    public function someMethod(AInterface $a_object) { 
     $a_object->foo('some_var'); 
    } 
} 

你現在可以看到,你可以寫另一類impliments一個Foo方法(和A接口),並使用該B類內爲好。

作爲一個真實世界的例子(經常使用),假設你有一個數據庫類,它具有與數據庫交互的特定方法(getRecord,deleteRecord)。現在讓我們稍後再說,你會發現切換數據庫rdbms的理由。您現在需要使用完全不同的SQL語句來實現相同的目標,但是由於您使用了類型提示的接口,因此您可以簡單地創建一個新類來實現該接口,但在交互時以完全不同的方式實現這些相同的方法與不同的rdbms。當創建這個新類時,您將確切知道需要爲這個新類編寫哪些方法,以便適合需要使用數據庫對象的相同對象。如果使用用於創建對象並將它們注入其他對象的容器類,則無需更改太多應用程序代碼即可切換數據庫類,因此可以切換數據庫rdbms。你甚至可以使用一個工廠類,這可能會限制你對一行代碼做出的修改(理論上)進行這種修改。

+0

我編輯了我原來的問題,詳細闡述了我應該如何設置。如果這不正確,你會介意提供一個真實世界例子的描述嗎?我不需要特定的代碼,只是我可以涉及的東西:)(如果我正確理解你的答案,接口可能是我設置純粹處理會話的類時一直在尋找的答案,一個純粹處理數據庫連接 - 在邏輯上,其他類不是這些的擴展,但它們確實需要它們存在,即,使日誌可以寫入數據庫 - 所以接口就是這裏的答案?) – Codecraft 2011-06-02 16:18:37

+0

似乎每個會話,數據庫)應該得到自己的類,並且如果其他類依賴於它們,則至少有一個接口。那麼你會想將這些類派生的對象注入到依賴它們的對象中。然後可以使用type-hinting來確保只有實現了特定接口的對象才能被接受爲參數。 – dqhendricks 2011-06-02 16:36:47

+0

我真的可以使用一個代碼示例來說明這一點,但我腦海中的燈泡閃爍......我會繼續考慮您的答案,並看看我是否可以永久性地發佈它,謝謝:-) – Codecraft 2011-06-02 16:51:22