The Singleton and Factory Patterns in PHP: Building object-oriented forms (建立面向对象的表单)
The Singleton and Factory Patterns in PHP: designing an object factory (实现工厂)
The Singleton and Factory Patterns in PHP: a rendering-capable factory class (认识工厂模式)
The Singleton and Factory Patterns in PHP: Working With Singletons (使用单例模式)
The Singleton and Factory Patterns in PHP: Building a Form Generator Class (建立表单生成类)
rollenc笔记:
1。单例:在系统中只需要一个实例是可以使用单例模式。你一天可能需要吃多个苹果,而你只需要一把水果刀。而且,没有水果刀就去拿一把,已经有的话就直接用,不需要忽略以前的再拿新的。那么水果刀就可以作为一个单例。
在建立数据库连接是常常需要使用单例模式。在MVC系统中,也常常使用单例来调用module。在简单的代码如下:
class Db
{
/**
* @access private
*/
function Db()
{
$this->conn = mysql_connect(DB_HOST, DB_USER, DB_PWD);
if(!$this->conn)
{
die("Connect DB error!");
}
mysql_select_db(DB_NAME);
}
/**
* 使用 $db = & Db::getInstance() 来构建一个DB连接,而不是使用$db = new Db();
* 如果使用php5,可以把Db的构造函数__construct声明为private,这样在程序级强行在程序只能使用getInstance来构建,
* 但考虑兼容的代码,在php4中就只能简单的靠程序员自己来自觉遵守了。
*/
function & getInstance()
{
if (!DB :: $instance)
{
DB :: $instance = new Db();
}
return DB :: $instance;
}
/**
*其他的函数等等
*/
function query($query)
{
//Your code here
}
}
?>
2。简单工厂:文中提到的工厂属于简单工厂模式的范畴。有几个类继承于同一个类或者实现了相同的接口(文中并没有接口和继承,但是可以知道几个)。而在运行前,你并不知道你需要那一个类,那么,。你可能需要一个工厂。比如:
<?php
$fruitName = $_GET['name'];
$fruit = FruitFactory::getFruit($fruitName);
$fruit->output();
?>
在运行前,我们并不知道,我们需要建立一个什么样的Fruit对象,那么我们就把参数$_GET['name']给FruitFactory,让他来生产一个相应的Fruit对象出来。然后,我们再继续进行下面操作。我提到上面的接口的概念,也是因为后面的调用是相同的,只有具备了这些接口的对象(类)才能继续正确的运行。
功能非常棒。赶快下载使用吧:
Firefox的Performancing插件下载地址
Part1:
PHP 5 made significant improvements on the Object Orientated programming model of PHP 4 bringing it more in line with languages such as Visual Basic .NET and Java. The improved object model in PHP 5 makes developing applications using OOP much easier and gives you the programmer, greater flexibility.
In this series of articles I will demonstrate the new features of the PHP 5 object and show you how to create a database abstraction layer similar to PEAR DB. I will also introduce you to a few design patterns that can be applied to common OOP related problems.
In this article, I introduce you to some of the features of the PHP 5 object model. You will see how to create a database abstraction layer similar to the Pear DB abstraction layer. You'll also learn how to adapt this abstraction layer for your own uses.
Introduction
One of PHP's stronger areas is its support for database connectivity. It is able to connect to and talk to just about any database server or interface you can imagine. However, with this comes a few inherent problems; each database system has its own features, functions and in most cases they have their own versions of SQL. Although the functions used to access these databases are similar they do vary subtly meaning that if you were to want to port an application written for MySql to MS SQL server, the refactoring would require manually changing all calls to mysql_query() to mssql_query().
While we are not going to go as far as developing a database independent version of SQL, the new features of PHP 5 will be us build a consistent database API with support for stored procedures (where it is not already present). This will then make switching from one database system to another as painless as changing one line of code.
Interfaces Abstract Classes and the Adapter Pattern
The first features new to PHP 5 to be covered in this article are abstract classes and interfaces. These concepts are nothing more than features added to OOP, which help the programmer follow good coding standards.
Abstract Classes
An abstract class is a class that is only partially implemented by the programmer. It may contain one or more abstract methods. An abstract method is simply a function definition that serves to tell the programmer that the method must be implemented in a child class.
To create an abstract class we use the code shown in Listing 1:
Listing 1: An Abstract PHP class
<?php
abstract class Weapon
{
private $SerialNumber;
abstract public function fire();
public function __construct($SerialNumber)
{
$this->SerialNumber = $SerialNumber;
}
public function getSerialNumber()
{
return $this->SerialNumber;
}
}
?>
The abstract class in Listing 1 contains some of the methods required for a weapon. The fire() method however, cannot be implemented because each different weapons use different firing different mechanisms. The method is therefore declared as abstract, meaning it will be implemented in a more specific child class.
Because the class is abstract, an instance of it can never be created (remember, it is only a partial implementation). Instead a child class must be created using inheritance and implement the fire method in itself. Failure to do so will result in a fatal error. Listing 2 shows a child class being created from the Abstract Weapon class.
Listing 2: Extending the Abstract Weapons class
<?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;
}
?>
An instance of the Cannon and Gun classes can now be created because they now fully implemented subclasses of weapon.
Interfaces
An interface is similar to an abstract class; indeed interfaces occupy the same namespace as classes and abstract classes. For that reason, you cannot define an interface with the same name as a class. An interface is a fully abstract class; none of its methods are implemented and instead of a class sub-classing from it, it is said to implement that interface.
An interface will be used in the database abstraction layer you create. This ensures that every time you create a class for a particular database, the same API is exposed. When using an interface, you can then rely on the methods defined for the interface to be part of the class because, if they are not, PHP will not parse it.
The MySql functions will be used as an example because they are the most commonly used amongst PHP programmers. The most commonly used functions are:
- 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()
If all the database class APIs you create can expose the same methods with the same return types then you can be sure that changing from one database to another, such as from MySql to Postgre SQL, will be painless. As such, the interface in listing 3 can be determined for your API.
Listing 3: An Abstracted Database Interface
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();
}
Any class implementing the interface must define each method that is declared in the interface, and each method must have at least the parameters identified in their interface definitions. It may have more parameters as long as they are optional, but it cannot have less.
Part2
Look at a class in Listing 4 that implements the database interface. You should recall that I mentioned the adapter pattern earlier. This is an example of the adapter pattern, which is used by programmers in order to adapt one API. The API you are adapting from could be another object-based API or as being done here, an adaptation from a modular API. If you want to read more about the adapter pattern, you can find a more detailed explanation and examples here.
Notice how the escape_string() method is included as a static method. This method does not require an active connection to a database and should not require and instance of any object which implements the DB interface. In my opinion, this is the single most important method of any database implementation; a poorly implemented escape string method could make your applications vulnerable SQL injection.
Listing 4: Implementing the database interface
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);
}
}
?>
You'll notice that there are many more mysql functions than methods that are adapted in the interface in listing 3. However, this small subset of functions is sufficient to meet the needs of most applications requiring trivial data storage and retrieval. The additional functions can be implemented and I have done this in the attached example file, you may also choose to add additional functionality to the class and the interface.
Listing 5: Creating a database class
$db = new MySqlDb;
$db->connect('host', 'username', 'password');
$db->query('use users'); // we could also use $db->select_db here but it is not consistent with the interface
$result = $db->query("SELECT username FROM users");
while($row = $db->fetch_assoc($reuslt))
{
echo($row['username']);
}
As shown in listing 5, you can now create a class for each database you want and as long as it implements the DB interface, switching from one to another is as easy as changing one line of code:
[geshi lang=php]
$db = new MsSqlDb;
[/geshi
Conclusion
In this first article, you've seen how to create an abstraction layer to access a database. Using this, you saw how creating an interface helps you to isolate your application so that you can easily switch from one database to anther without rewriting your own applications.
Downloads
About the Author
Adam Delves is a university student and web programmer from the UK who is studying computing. He has been a PHP programmer for over 3 years and now runs two small websites and writes articles for PHP builder.com.
rollenc总结
将本文标题相对应,MySqlDb实现了DB接口(interface),也对mysql_*函数进行了适配(Adapter)(我就很喜欢进行这种适配,php中有很多带‘_’字符的函数(这也被PHP反对者们发现,并抨击PHP的一大有力证据),与我喜欢的骆驼编程风格很不协调,所以,为了隐藏它们,我常常进行适配)。
抽象类只在weapon上出现到了,为了增加代码的灵活性,建议使用接口,而不是继承。(完)
类图如下:
顺序图如下:
从类的模式及其构建一个DBManager的流程来看,这使用了单例模式,而其中是创建哪一个类,则使用桥梁模式来封装。但是在代码中我还看到了一些地方直接使用了DBManager::getInstance()方法来建造(获取)数据库实例,虽然使用上调用DBManagerFactory::getInstance()和DBManager::getInstance()这两个静态函数并无差别,DBManagerFactory::getInstance只是简单的委托给DBManager::getInstance,但是在顺序图流程上就不好解释了。这打乱了整个设计模式的结构。
当然,如果单单说是从速度上解释的话可以理解,使用DBManager::getInstantce可以减少一次函数调用。但这样混乱了整个设计模式,我觉得这代价是不是大了。
希望有高人指点一二,说说其中我忽略和分析错误了的道理。
比如如下代码段:
[PHP]
class DBManager
{
var $helper;
var $tableName;
var $database = null;
var $dieOnError = false;
var $encode = true;
var $query_time = 0;
var $lastmysqlrow = -1;
var $last_error = '';
var $lastResult = array ();
function DBManager()
{
global $sugar_config;
$my_db_helper = 'MysqlHelper';
if ($sugar_config['dbconfig']['db_type'] == "oci8")
{
}
if ($sugar_config['dbconfig']['db_type'] == "mssql")
{
$my_db_helper = 'MssqlHelper';
}
static $helper;
if (empty ($helper))
{
$helper = new $my_db_helper ();
}
$this->helper = $helper;
}
function & getInstance($instanceName = '')
{
global $sugar_config;
static $count;
static $old_count;
global $dbinstances;
$instanceName = 'db';
$config = $sugar_config['dbconfig'];
if (!isset ($dbinstances))
{
$dbinstances = array ();
}
if (!isset ($dbinstances[$instanceName]))
{
$my_db_manager = 'MysqlManager';
if ($config['db_type'] == "oci8")
{
}
else
if ($config['db_type'] == "mssql")
{
$my_db_manager = 'MssqlManager';
}
if (!empty ($config['db_manager']))
{
$my_db_manager = $config['db_manager'];
}
DBManagerFactory :: load_db_manager_class($my_db_manager);
$dbinstances[$instanceName] = new $my_db_manager ();
$dbinstances[$instanceName]->connect($config, true);
$dbinstances[$instanceName]->count_id = $count;
$dbinstances[$instanceName]->references = 0;
$dbinstances[$instanceName]->helper->db = $dbinstances[$instanceName];
}
else
{
$old_count++;
$dbinstances[$instanceName]->references = $old_count;
}
return $dbinstances[$instanceName];
}
//MORE
}
[/PHP]
DBmanager是一个抽象类,也是一个单例类,故DBmanager类声明应该是abstract class, 构造函数好像不该出现或者是private,getInstance函数也该是static等等,这样,对于设计模式的理解更方便些。
我准备对其重构了,不是为了使用或者速度,仅仅是把代码改一改,使它更象一个面向对象的系统。
当然,一开始改,PHP4支持肯定就挂掉了。
其中使用了以下的工具和组件等:
- Eclipse & PHPEclipse。
我对比了一下DreamWeaver(DW),Zend Studio(ZS),Eclipse & PHPEclipse(E&PE)三种开发工具,厌倦DW的全站搜索功能,各个文件间的关联很松散。ZS强在逐步调试,但好像我并不需要,从大局把握整个系统才是最重要的。另外就是ZS的速度让我的256机子疯掉了。E&PE是最佳的选择,整个系统完整的融合在一起,不需要ctrl+F就可以在相关联的函数,类中跳转。
- Simple Test
试着以测试的方式去思考。不过我更希望是测试驱动开发。这样更有趣些。唯一的遗憾是Simple Test for eclipse的插件没有安装好,各个文件间的包含关系处理不正常,看了一下readme文件,有这么一句:
Future Features
These are features that should eventually make it into this plugin
* Allow different "include" file on each runner (override the "master" include)
* Handle fatal errors better
所以,只能等待下一个版本出来了。现在先在WEBPAGE里面用Simple Test吧。
- 重构
我没有准备去重构这个系统,几天的代码阅读,也没有问到“代码臭味”,但是我会在代码中去理解,重构的目标:设计模式。 - 设计模式
这是我一眼看中这个系统并把它作为我的学习对象的原因。先在再看DB部分,对整个的构建模式有了很大的体会。不过,完全理解这个系统的代码对现在的我来说还是有困难的,在复杂的工厂模式中还是会迷路,找不到系统运行的流向。
这不是读后感,因为我现在还在慢慢的读。有点惊讶,有点昏头,也有点。。。什么感觉都有,想骂人的感觉都有。
我很佩服阎宏了,再一本程序员阅读的书里面可以写这么多的哲学,古文,和一些其他的知识。第一章就看的我昏,从形而上学──我一直没弄明白第一次说这个词的人是不是中国人──开始论,涉及
1、道家观点:无名大地之始,有名万物之母──老子云
2、量子物理中波和粒子我一个理科本科差生看不懂的东西云云
3、马王堆出土道德经贴图
4、成玄英(601~690)之《庄子疏》:气聚而有其形,气散而归于无形也。
5、建筑风水学:气乘风则散,界水则止;
等等云云。。
后面不少西游记例子,唐僧和徒弟的一对多的关系,徒弟是抽象类,悟空,八戒是徒弟的继承;孙大圣的身外身法术。还有鲁智深为什么是和尚的道理。
昏昏头。我已经对阎宏佩服的五体投地了,简直想把往上留传的那段赞扬楼主发帖的回帖记下来,背给阎宏大哥听。
说回来,这本书也是我看到的最浅显易懂的涉及模式的书了。哲学古文相关的东西不解,但程序部分还是很容易理解的。
谢谢阎宏老师了,在教我程序的同时,也让我补回了一点小学初中高中大学里面一直没学习好的中国古代文化和哲学。
附记:
哲学:比这句话“你是人,会制作和使用劳动工具的人”的回答更没用的知识。 (摘自:rollenc无知词典)