PHP 5
:接口抽象类与适配模式
相对于PHP 4,PHP 5在面向对象编程(OOP)模式方面作出了巨大的改进,使它看起来更加像Visual Basic .NET和Java,PHP 5中改进的对象模式加上OOP,现在开发起程序来更加容易,并且也给予了程序员更大的灵活性。
在本文中,将介绍PHP 5对象模式的一些新特性,也会提及怎样创建一个类似于Pear DB的数据库抽象层,并且还将进一步讲解如何修改此抽象层以作他用。
简介
PHP其中一个最擅长的领域,是在于对数据库连接的支持,它可以连接并与任何你能想像到的数据库服务器或接口进行通讯;然而,这也带来了一些与生俱来的问题,每种数据库都有其自身的特性与功能,并且,大多数都有各自的SQL版本。尽管访问这些数据库的函数彼此类似,但还是存在一些细小差别,当你要把一个为MySql编写的应用程序移植到MS SQL服务器上来时,将要把所有对mysql_query()的调用,手工调整为mssqp_query()。
虽然我们不至于开发一个独立于数据库的SQL版本,但PHP 5中的新特性已可用于构建一套一致的数据库API,并且对存储过程也提供了支持,这样就不会在从一种数据库系统切换到另一种数据库系统时,痛苦得要一行一行修改代码了。
接口抽象类与适配模式
下面将要介绍PHP 5的第一个新特性是抽象类与接口,这些概念只不过是面向对象编程的一些东西,将有助于程序员遵循良好的编码标准。
抽象类
抽象类是一种只由程序员实现了一部分的类,它可以包含一个或多个的抽象方法,一个抽象方法仅仅是一个函数定义,只是告诉程序员此方法必须要在子类中实现。
要创建一个抽象类,可如例1所示:
例1:一个抽象PHP类
<?php
abstract class Weapon
{
private $SerialNumber;
abstract public function fire();
public function __construct($SerialNumber)
{
$this->SerialNumber = $SerialNumber;
}
public function getSerialNumber()
{
return $this->SerialNumber;
}
}
?>
例1中的抽象类Weapon(武器)包含了某些方法,但其中未实现fire()(开火)方法,是因为不同的“武器”,“开火”方法也不同,在此处声明为abstract,意味着将在一个更详细的子类中实现。
因为类是抽象的,所以不能创建一个实例(因为它只实现了一部分),而后的子类必须通过继承创建,并实现其自己的fire(开火)方法。例2演示了一个自抽象Weapon类创建的子类:
例2:扩展抽象Weapon类
<?php
class Gun extends Weapon
{
public function fire()
{
if($this->SafetyOff) {
return $this->CurrentBullet;
}
}
}
class Cannon extends Weapon
{
public function fire()
{
$this->NeedsLoading = true;
return $this->CurrentCanon;
}
?>
现在可以创建Cannon与Gun类的一个实例了,因为它们已是一个完全实现的Weapon类的子类。
接口
接口与抽象类非常类似,实际上接口使用了和类与抽象类相同的命名空间,正因为此,所以你不能定义一个与某个类同名的接口。接口是一个完全抽象的类,除了可对它进行子类化之外,它未实现任何方法。
接口可用于创建数据库抽象层,以确保每次在为特定数据库创建一个类时,都有相同的API。因为PHP程序员通常会使用MySql,所以在此我们以MySql的函数作为示例,以下是经常使用的MySql函数列表:
mysql_connect()
mysql_error()
mysql_errno()
mysql_query()
mysql_fetch_array()
mysql_fetch_row()
mysql_fetch_assoc()
mysql_fetch_object()
mysql_num_rows()
mysql_close()
如果你创建的所有数据库类API,都使用相同的方法,并且有相同的返回类型,那么可以保证,在从一种数据库切换到另一种数据库时,如MySql到Postgre SQL,将是毫无困难的。请看例3:
例3:一个抽象数据库接口
interface DB
{
public function connect();
public function error();
public function errno();
public static function escape_string($string);
public function query($query);
public function fetch_array($result);
public function fetch_row($result);
public function fetch_assoc($result);
public function fetch_object($result);
public function num_rows($result);
public function close();
}
任何实现此接口的类,必须定义在其中声明的每一个方法,且每个方法至少都具有它们接口定义中的相应参数,如果有需要,参数可以更多些,但不能更少。
适配模式
来看一下例4中实现了数据库接口的类,这是关于适配模式的一个范例,程序员通常是为了顺应某个API而使用适配模式,而这个API可能是出于另一个面向对象的API,或者是在此处没有完成的API,或者改编自某个模块化API。
请注意escape_string()方法是怎样被作为一个静态方法包含进来的,这个方法并不需要一个数据库的活动连接,也不需要任何实现了此DB接口对象的实例。根据个人观点,这是任何数据库实现中,最重要的一个方法,如果实现得不好,将会使你的程序易受SQL注入攻击。
例4:实现数据库接口
class MySqlDB implements DB
{
private $link;
public function connect($server='', $username='', $password='', $new_link=true, $client_flags=0)
{
$this->link = mysql_connect($server, $username, $password, $new_link, $client_flags);
}
public function errno()
{
return mysql_errno($this->link);
}
public function error()
{
return mysql_error($this->link);
}
public static function escape_string($string)
{
return mysql_real_escape_string($string);
}
public function query($query)
{
return mysql_query($query, $this->link);
}
public function fetch_array($result, $array_type = MYSQL_BOTH)
{
return mysql_fetch_array($result, $array_type);
}
public function fetch_row($result)
{
return mysql_fetch_row($result);
}
public function fetch_assoc($result)
{
return mysql_fetch_assoc($result);
}
public function fetch_object($result)
{
return mysql_fetch_object($result);
}
public function num_rows($result)
{
return mysql_num_rows($result);
}
public function close()
{
return mysql_close($this->link);
}
你可能已经注意到了,此处较多地使用了mysql函数,而不是修改自例3中接口的方法,不管怎样,这些函数,足够满足大多数普通数据存储及检索程序的需要了。请看例5:
例5:创建一个数据库类
$db = new MySqlDb;
$db->connect('host', 'username', 'password');
$db->query('use users'); //在此我们也可以使用$db->select_db,但它与接口不一致。
$result = $db->query("SELECT username FROM users");
while($row = $db->fetch_assoc($reuslt)) {
echo($row['username']);
}
如例5中所示,你现在可以为每一个数据库创建一个类,只要它实现了DB接口,在从一种数据库切换到另一种数据库时,只需修改一行代码:
$db = new MsSqlDb;
结论
在本文中,初步讨论了怎样创建一个访问数据库的抽象层,并据此创建一个有助于隔离程序的接口,以在将来方便地从一种数据库切换到另一种数据库,而不用重写整个程序。