我正在建立一个类来处理Paypal IPN作为项目的一部分,因为我已经知道我将需要在至少两个即将到来的工作中再次使用它 - 我想确保我以某种方式构建它这将允许我重新使用它而无需重新编写类 - 我只想处理业务逻辑中的更改.
问题的第一部分是重新.接口.我还没有完全掌握它们的用处以及何时/何地部署它们.如果我有我的类文件("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) {
}
}
Run Code Online (Sandbox Code Playgroud)
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.
}
}
Run Code Online (Sandbox Code Playgroud)
用法
在这个项目中:
在其他项目中:
所以你可以看到我只是用它来定义相关函数组并添加注释.它使得阅读更容易,但是对我来说有什么(如果有的话)其他好处 - 是否可以将扩展器和基类拉到一起并在出现缺失时强制出错?
stdClass问题
问题的第二部分是建立在可读性方面.在类本身中存在越来越多的存储变量,一些是在构造函数中设置的,一些是由其他函数设置的 - 它们与诸如保存数据库连接变量(和连接资源本身)之类的东西有关,代码是否应该运行在测试模式下,日志记录和日志本身的设置,等等......
我已经开始像往常一样构建它们(再次,下面不完整和插图):
$this->dbConnection = false;
$this->dbHost = "";
$this->dbUser = "";
$this->enableLogging = true;
$this->sendLogByEmail = true;
$this->sendLogTo = "user@domain.com";
Run Code Online (Sandbox Code Playgroud)
但后来我认为不断增长的列表可以用一些结构,所以我适应它:
$this->database->connection = false;
$this->database->host = "";
$this->database->user = "";
$this->logging->enable = true;
$this->logging->sendByEmail = true;
$this->logging->emailTo = "user@domain.com";
Run Code Online (Sandbox Code Playgroud)
当我在编码和测试时将整个类转储出来时,这使我更容易阅读变量列表.
一旦完成,我就计划为泛型类写一个项目特定的扩展,我将保留查询的实际SQL - 从一个项目到另一个项目,Paypal的IPN过程和逻辑不会改变 - 但是每个项目的数据库结构因此,对类的扩展会将所有内容清理成单一格式,因此基类不必担心它,并且一旦编写就永远不需要更改.
总而言之,这只是一个健全性检查 - 在我走这条路太远之前,它看起来是正确的做法吗?
如果您使用的是类自动加载器,我强烈建议您使用,您不希望将接口和类保留在同一个文件中,以便接口可以自动加载,而无需首先加载实现它的这一个类.
有关自动加载的更多信息:http: //php.net/manual/en/language.oop5.autoload.php
您可能想要考虑的另一件事是,给定的类可以实现多个接口,并且多个类可以实现相同的接口.
接口主要用于各种设计模式,强制执行规则,以及将类与任何依赖类分离.当您将一个类与其依赖项分离时,它会使以后修改代码变得更加容易.
例如,假设你有一个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');
}
}
Run Code Online (Sandbox Code Playgroud)
你现在可以看到你可以编写另一个实现foo方法(和AInterface)的类,并在类B中使用它.
作为一个真实世界的例子(经常使用),假设您有一个数据库类,其中包含与数据库交互的特定方法(getRecord,deleteRecord).现在让我们说以后你找到切换数据库rdbms的原因.你现在需要使用完全不同的SQL语句来实现相同的目标,但是由于你使用了一个接口来进行类型提示,你可以简单地创建一个新的类来实现这个接口,但是它会以完全不同的方式实现这些相同的方法.与不同的rdbms.在创建这个新类时,您将确切地知道需要为这个新类编写哪些方法,以便适合需要使用数据库对象的相同对象.如果您使用用于创建对象并将其注入其他对象的容器类,您不需要更改太多的应用程序代码来切换数据库类,因此切换数据库rdbms.您甚至可以使用工厂类,这可能会限制您对一行代码的更改以进行此类更改(理论上).