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

【简介】

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

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

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

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

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.";
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值