【简介】
单例模式:生成一个且只生成一个对象实例的特殊类。
工厂模式:构建创建者类的继承层级。
抽象模式:功能相关产品的创建。
原型模式:使用克隆来生成对象。
9.1 生成对象的问题和解决方法
定义一个员工的抽象类及其一个实现类:
abstract class Employee {
protected $name;
function __construct($name) {
$this->name = $name;
}
abstract function fire();
}
class Minion extends Employee {
function fire() {
echo $this->name . "I'll clear my desk.<br>";
}
}
再定义一个 Boss 的调用类:
class NastyBoss {
private $employee = array();
function addEmployee($employeeName) {
$this->employees[] = new Minion($employeeName);
}
function projectFails() {
if (count($this->employees)>0) {
$emp = array_pop($this->employees);
$emp->fire();
}
}
}
$boss = new NastyBoss();
$boss -> addEmployee("harry");
$boss -> addEmployee("bob");
$boss -> addEmployee("mary");
$boss -> projectFails();
并通过 NastBoss 的 addEmployee() 方法接受的名字字符来实例化新的 Minion 对象。NastyBoss 对象通过 projectFails() 方法删除一个 Minion 对象。
在这里我们直接在 NastyBoss 类中实例化 Minion 对象,代码的灵活性受到了限制,因为如果有 Employee 的其他子类的话,就无法直接调用。
如果 NastyBoss 对象可以使用 Employee 类的任何实例,代码的灵活性就会比较好。
在代码中,通过限制参数类型来解决这个问题,将addEmployee改成下面的方式:
function addEmployee(Employee $employeeName) {
$this->employees[] = $employeeName;
}
NastyBoss 类其他内容保持不变,并添加 CluedUp 类,如下:
class NastyBoss {
private $employee = array();
function addEmployee(Employee $employeeName) {
$this->employees[] = $employeeName;
}
function projectFails() {
if (count($this->employees)>0) {
$emp = array_pop($this->employees);
$emp->fire();
}
}
}
class CluedUp extends Employee {
function fire() {
echo $this->name . "I'll clear my lawyer.<br>";
}
}
调用 addEmployee() 方法的时候,需要传入具体的对象参数:
$boss = new NastyBoss();
$boss -> addEmployee(new Minion("harry"));
$boss -> addEmployee(new CluedUp("bob"));
$boss -> addEmployee(new Minion("mary"));
$boss -> projectFails();
更好地解决方案「把对象实例化的工作委托出来」,委托一个独立的类或方法来生成 Employee 对象传递给 NastyBoss 的 addEmployee() 方法。下面给 Employee 类添加一个实现了对象创建策略的静态方法:
abstract class Employee {
protected $name;
private static $types = array('Minion', 'CluedUp', 'WellConnected');
static function recruit($name) {
$num = rand(1, count(self::$types))-1;
$class = self::$types[$num];
return new $class($name);
}
function __construct($name) {
$this->name = $name;
}
abstract function fire();
}
class WellConnected extends Employee {
function fire() {
echo $this->name . "I'll clear my dad.<br>";
}
}
通过一个姓名字符串来随机实例化具体的 Employee 子类,现在可以将实例化的细节委托给委托给Employee 类的 recruit() 方法。
$boss = new NastyBoss();
$boss -> addEmployee(Employee::recruit("harry"));
$boss -> addEmployee(Employee::recruit("bob"));
$boss -> addEmployee(Employee::recruit("mary"));
$boss -> projectFails();
9.2 单例模式
9.2.1 问题
在没有单例模式的情况下,以往的项目开发中,针对需要反复操作数据库的时候,就会产生大量的 new 操作,而每一次 new 操作都会消耗系统和内存的资源。
//初始化一个数据库句柄
$db = new DB(...);
//比如有个应用场景是添加一条用户信息
$db->addUserInfo();
......
//然而我们要在另一地方使用这个用户信息,这时要用到数据库句柄资源,可能会这么做
......
function test() {
$db = new DB(...);
$db->getUserInfo();
......
有些朋友也许会说,可以直接使用global关键字!
global $db;
......
其中一个方案就是通过 global 去解决这个问题,但是 global 存在安全隐患。
通过单例模式,可以确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,就可以在不引入全局变量的情况下,就能产生一个整个系统都能引用的对象。
9.3 工厂模式
9.3.1 简单工厂模式
在调用时看不到具体实例化对象的细节,而是通过一个接口实现实例化对象。
// 三种数据库类型 mysql sqlite sqlserver
class dbMysql {
public function conn() {
echo "connect to mysql.";
}
}
class dbSqlite {
public function conn() {
echo "connect to sqlite.";
}
}
class dbSqlserver {
public function conn() {
echo "connect to sqlserver.";
}
}
// 工厂
class Factory {
public static createDb($type) {
switch ($type) {
case 'mysql':
return new dbMysql();
break;
case 'sqlite':
return new dbSqlite();
break;
case 'sqlserver':
return new dbSqlserver();
break;
default:
throw new Exception("Error db type", 1);
break;
}
}
}
// 调用生成对象
$db = Factory::createDb("mysql");
在上面的例子中,只需要通过Factory::createDb()方法,并传入相应的数据库名称参数即可实例化一个数据库连接对象。
但是该方式扩展比较麻烦,比如还需要实现新增一个oracle的数据库连接对象,就需要修改Factory。
9.3.2 工厂方法模式
将简单工厂模式的工厂类进行抽象化,避免扩展时需要修改简单工厂模式中的工厂类。方便扩展、维护。
/* 定义一个数据库连接接口 */
interface db{
public function conn();
}
/* 所有的数据库实例类都实现连接接口db */
class dbMysql implements db{
public function conn() {
echo "connect to mysql.";
}
}
class dbSqlite implements db{
public function conn() {
echo "connect to sqlite.";
}
}
class dbSqlserver implements db{
public function conn() {
echo "connect to sqlserver.";
}
}
/* 定义一个创建数据库实例的类 */
interface Factory {
public function createDb();
}
/* 所有的工厂类都实现创建数据库的类 */
class mysqlFactory implements Factory {
public function createDb() {
return new dbMysql();
}
}
class sqlite implements Factory {
public function createDb() {
return new dbSqlite();
}
}
class sqlServer implements Factory {
public function createDb() {
return new dbSqlserver();
}
}
假设以上是已经封装好的对三个数据库(mysql、sqlite、sqlserver)的数据库连接类,现在需要扩展一个oralce的数据库的连接类,通过工厂方法模式可以在不改动前面已经封装好的情况下,完成扩展oracle数据库的工作。
/* 新增一个oracle数据库的时候,可以在不改动前面的封装好的代码的情况下,完成新增的需求 */
class oracleFactory implements Factory {
public function createDb() {
return new dbOracle();
}
}
class dbOracle implements db {
public function conn() {
echo "connect to oracle.";
}
}