【深入PHP 面向对象】读书笔记(七)

来源:互联网 发布:足彩数据分析推荐 编辑:程序博客网 时间:2024/05/17 23:31

【简介】

单例模式:生成一个且只生成一个对象实例的特殊类。

工厂模式:构建创建者类的继承层级。

抽象模式:功能相关产品的创建。

原型模式:使用克隆来生成对象。

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 sqlserverclass 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.";  }}