在数据库访问时,数据库对外提供的连接数是有限的,因此一个用户只需创建一个链接就可以,采用“单例模式”设计,判断某个类是否已经创建过实例,可实现这个目的。下面演示“单例模式”的原理
一、单例模式的原理
<?php
//文件: src\inc\Singleten.php;
//没有传入参数的情况
namespace src\inc;
class Singleten{
private static $selfObj;
//禁用构造方法
private function __construct()
{
//...
}
//禁用clone
private function __clone()
{
//...
}
//判断是否已创建过类实例
public static function getInstance()
{
if (is_null(static::$selfObj)) {
static::$selfObj=new self();
}
return static::$selfObj;
}
}
$pdo=Singleten::getInstance();
$pdo1=Singleten::getInstance();
var_dump($pdo===$pdo1);
结果
在编程过程中,某个.php
文件常常要通过(new)关键字引用多个类的实例,当某个类被多个.php
文件调用,而突然不得不改名时,意味着所有调用了该类的文件全部都得重新更正调用的类名称。采用工厂模式
编程,可解决这个问题。下面演示工厂模式
的两种模式:普通工厂 和 抽象工厂 。
二、工厂模式的实现(以购物分类:线上支付
和线下支付
为例)
1. 普通工厂
线上支付类
<?php
//文件:src\inc1\Online.php;
namespace src\inc1;
class Online
{
public function purchase(){
echo '线上转帐';
}
}
线下支付类
<?php
//文件:src\inc1\Offline.php;
namespace src\inc1;
class Offline
{
public function purchase(){
echo '线下现金支付';
}
}
普通工厂类
<?php
//文件:src\inc1\Factory.php;
namespace src\inc1;
class Factory
{
private static $shopWays;
public static function getInstance(string $shopWays)
{
switch (strtolower($shopWays)):
case 'online':
static::$shopWays = new Online();
break;
case 'offline':
static::$shopWays = new Offline();
break;
endswitch;
return static::$shopWays;
}
}
普通工厂调用
<?php
//文件:src\inc1\Shopping.php;
namespace src\inc1;
class Shopping
{
public static function purchase($shopWays){
Factory::getInstance($shopWays)->purchase();
}
}
自动加载类(略)
客户端调用
<?php
//文件:src\inc1\Main.php;
namespace src\inc1;
require '..\autoload.php';
$shopWays=Shopping::purchase('online');
echo '<br>';
$shopWays=Shopping::purchase('offline');
结果
===================================================================
2. 抽象工厂
线上支付类
<?php
//文件:src\inc2\Online.php;
namespace src\inc2;
class Online implements IShopping
{
public function purchase(){
echo '线上转帐';
}
}
线下支付类
<?php
//文件:src\inc2\Offline.php;
namespace src\inc2;
class Offline implements IShopping
{
public function purchase(){
echo '线下现金支付';
}
}
抽象工厂(接口)
<?php
//文件: src\inc2\IShopping.php
namespace src\inc2;
interface IShopping
{
public function purchase();
}
抽象工厂调用
<?php
//文件: src\inc2\Shopping.php
namespace src\inc2;
class Shopping
{
public static function purchase(IShopping $ishopping){
$ishopping->purchase();
}
}
自动加载类(略)
客户端调用
<?php
//文件: src\inc2\Main.php
namespace src\inc2;
require '..\autoload.php';
$shopWays=Shopping::purchase(new Online());
echo '<br>';
$shopWays=Shopping::purchase(new Offline());
结果
===================================================================
下面以数据库访问为例结合单例模式演示抽象工厂的原理:分别以PDO和Mysqli方式进行数据库查询select()
操作。
三、抽象工厂的原理(接口实现)
数据库接口(抽象工厂)
<?php
//文件: src\inc\IDataBase.php;
namespace src\inc;
interface IDataBase
{
public function select(): array;
}
Mysqli类
<?php
//文件: src\inc\Mysqli.php;
namespace src\inc;
class Mysqli implements IDataBase {
private static $mysqli=null;
private static $selfObj=null;
private function __construct(...$arguments)
{
static::$mysqli = new \Mysqli(...$arguments);
}
//类实例
public static function getInstance(...$argument)
{
if (is_null(static::$selfObj)) {
static::$selfObj=new self(...$argument);
}
return static::$selfObj;
}
//禁用clone
private function __clone()
{
//...
}
//设置变量
public function __set(string $name, $value)
{
$this->{$name}=$value;
}
//查询
public function select(): array
{
return static::$mysqli->query($this->sql)->fetch_All(MYSQLI_ASSOC);
}
}
PDO类
<?php
//文件: src\inc\PDO.php;
namespace src\inc;
class PDO implements IDataBase{
private static $pdo=null;
private static $selfObj=null;
private function __construct(...$arguments)
{
static::$pdo = new \PDO(...$arguments);
}
//类实例
public static function getInstance(...$argument)
{
if (is_null(static::$selfObj)) {
static::$selfObj=new self(...$argument);
}
return static::$selfObj;
}
//禁用clone
private function __clone()
{
//...
}
public function __set(string $name, $value)
{
$this->{$name}=$value;
}
public function select(): array
{
return static::$pdo->query($this->sql)->fetchAll(\PDO::FETCH_ASSOC);
}
}
接口调用
<?php
//文件: src\inc\DataBase.php;
namespace src\inc;
class DataBase
{
public static function select(IDataBase $idatabase): array
{
return $idatabase->select();
}
}
自动加载类
<?php
//文件 src\autoload.php;
spl_autoload_register(function ($clasName) {
$file = str_replace('\\', DIRECTORY_SEPARATOR, (dirname(__DIR__) . '\\' . $clasName)) . '.php';
file_exists($file) ? require $file : '加载失败,文件不存在';
});
客户端调用
<?php
//文件: src\inc\Main.php;
namespace src\inc;
require '..\autoload.php';
//Mysqli查询`phpedu`.`staffs`表中第1条记录
$mysqli = Mysqli::getInstance('db.io', 'root', 'root', 'phpedu');
$mysqli->sql='SELECT * FROM `staffs`;';
echo "数据库(`phpedu`).表(`staffs`)首条记录:";
echo '<pre>' . print_r(DataBase::select($mysqli)[0],true) . '</pre>';
//PDO查询`phpedu`.`users`表中第1条记录
$pdo = PDO::getInstance('mysql:host=db.io;dbname=phpedu', 'root', 'root');
$pdo->sql='SELECT * FROM `users`;';
echo "数据库(`phpedu`).表(`users`)首条记录:";
echo '<pre>' . print_r(DataBase::select($pdo)[0],true) . '</pre>';
结果(数据库表格略)
总结
单例模式
,通过将构造方法(construct())、克隆方法(clone())从外部禁用,而从内部控制实例化的方式,有效避免类的重复实例化.工厂模式
是将原构造方法中实例化依赖对象的过程,交给工厂类完成。其中,普通工厂
模式,通过判断条件,根据输入不同的类别实例化不同的类,将原来的依赖多个类变成依赖1个类; 抽象工厂
模式,运用接口的方式来实现,与普通工厂
相比,代码量更少,灵活性更高,出错概率低 ,维护更方便。