Correcting teacher:天蓬老师
Correction status:qualified
Teacher's comments:作业应该尽可能与当前进度一致, 现在在学js, 之前的作业可以在双休无课时再提交
单例模式这个很好理解,顾名思义就是只实例化对象一次,这个思想就和面向对象思想有一点点小冲突,本身面向对象就是为了代码复用,但这不重要。毕竟你看数据库连接声明我们只需要实例化一次即可!毕竟数据库连接数是有限的。
单例模式的实现思路
首先是创建一个类给他整一个随机数来看看咱实例化了几次
class Single
{
public $rand;
//来个构造方法
public function __construct(){
return $this->rand = mt_rand(1000,9999);
}
}
var_dump(new Single());
var_dump(new Single());
来看看一下是否能正常运行
那么接下来就是想一个办法只让对象实例化一次,那么就得想办法不然它在外部被调用那么我们改写一下
class Single
{
public $rand;
//来个构造方法
protected function __construct(){
return $this->rand = mt_rand(1000,9999);
}
}
var_dump(new Single());
var_dump(new Single());
好这样子就成功报错了!因为构造方法不允许被在外部被调用,所以实例化会出错。
那么下一步就是控制权转移使用静态成员
class Single
{
public $rand;
//来个构造方法
protected function __construct(){
return $this->rand = mt_rand(1000,9999);
}
//写一个控制器来控制构造方法想在不实例化中使用那就必须用静态成员
static public function getinfo()
{
return new Single();
}
}
var_dump(Single::getinfo());
var_dump(Single::getinfo());
OK这样子我们就成功转移了控制权
接下来我们只需要给控制加一个判断对象有无实例化即可
class Single
{
//搞一个静态成员flag
static public $flag = null;
public $rand;
//来个构造方法
protected function __construct(){
return $this->rand = mt_rand(1000,9999);
}
//写一个控制器来控制构造方法想在不实例化中使用那就必须用静态成员
static public function getinfo()
{
//判断flag是不是null
if(Single::$flag === null){
Single::$flag = new Single();
}
return Single::$flag;
}
}
var_dump(Single::getinfo());
var_dump(Single::getinfo());
单例模式写一个数据库连接(PDO)
namespace chapter7;
//单例模式 单例模式就是只允许实例化一次(就是整个项目只实例化一次不会重复实例);
//最直接的就是数据库连接(因为数据库的连接数是有限的);
class Single
{
//写一个数据库连接
protected static $pdo = null;
//单例模式最直接的一点就是不让客户端多次实例化
protected function __construct(...$arr)
{
list($dsn,$user,$psd) = $arr;
//构造方法用来实例化PDO对象
self::$pdo = new \PDO($dsn,$user,$psd);
}
//留给外部的接口
public static function conn(...$arr)
{
if(is_null(self::$pdo)){
new self(...$arr);
}
// 当前类已经实例化过了, 就不要重复实例化,直接返回
return self::$pdo;
}
protected function __clone()
{
}
}
$conn = ['mysql:host=localhost;dbname=xiaoyu', 'root', 'root'];
$pdo = Single::conn(...$conn);
// var_dump($pdo->query('SELECT * FROM users')->fecthAll(\PDO::FETCH_ASSOC));
print_r($pdo->query('select * from users')->fetchAll(\PDO::FETCH_ASSOC));
工厂模式呢是为了解决接口调用的问题(说起来哈个人感觉非常像那个服务容器)
来给大家看看一个小案例
首先在base目录下面有test test2两个目录
然后一个加载器!
autoload.php
spl_autoload_register(function($class){
$path = str_replace('//',DIRECTORY_SEPARATOR,$class);
require dirname(__DIR__) . DIRECTORY_SEPARATOR . $path . '.php';
});
然后test目录里面有俩文件一个AMD.php 一个 Intel.php
namespace base\test;
class AMD
{
public function go()
{
return 'AMD Yes';
}
}
namespace base\test;
class Intel
{
public function go()
{
return '灯~灯↗灯↘灯↗灯↘';
}
}
然后我们先看一下如果我们想实现实例化相同的类更具提交参数的不同而实例化所相对应的类要如何写
namespace chapter7;
//导入限定名称
use base\test\AMD;
use base\test\Intel;
//引入 自动加载类
require __DIR__.DIRECTORY_SEPARATOR.'autoload.php';
class Cpu
{
//CPU
private $cpu;
public function __construct($class)
{
//声明不同的CPU 实例化相对应的对象
switch($class){
case 'AMD':
$this->cpu = new AMD;
break;
case 'Intel':
$this->cpu = new Intel;
break;
}
}
//使用!
public function Assemble()
{
return '组装电脑的CPU用'.$this->cpu->go();
}
}
echo (new Cpu('AMD'))->Assemble();
echo '<hr>';
echo (new Cpu('Intel'))->Assemble();
大家可以发现我们虽然说实现了这个效果但是耦合性非常高需要依赖内部的两个实例类所以我们增加了一个工厂类进行了更改
namespace chapter7;
//导入限定名称
use base\test\AMD;
use base\test\Intel;
use Factory;
//引入 自动加载类
require __DIR__.DIRECTORY_SEPARATOR.'autoload.php';
//声明一个工厂类
class Factory1
{
//申明一个成员暂存对象后返回给CPU类
private static $cpu;
public static function getCpu($cpu)
{
switch($cpu){
case 'AMD':
self::$cpu = new AMD;
break;
case 'Intel':
self::$cpu = new Intel;
break;
}
return self::$cpu;
}
}
class Cpu1
{
//CPU
private $cpu;
public function __construct($class)
{
//这样子从依赖3个外部类变成只依赖一个工厂类
$this->cpu = Factory1::getCpu($class);
}
//使用!
public function Assemble()
{
return '组装电脑的CPU用'.$this->cpu->go();
}
}
echo (new Cpu1('AMD'))->Assemble();
echo '<hr>';
echo (new Cpu1('Intel'))->Assemble();
我们可以发现从依赖两个类变成了只依赖工厂类(是不是和服务容器非常相似这种写法?)
那么如果我们不想依赖这个工厂类该如何操作?答就是使用接口进行更进一层的抽象化
test2文件里面有三个文件 1.接口Cpu.php 2. 实现接口的AMD.php 3.实现接口的Intel.php
namespace base\test2;
interface Cpu
{
public function go();
}
namespace base\test2;
use base\test2\Cpu;
class AMD implements Cpu
{
public function go()
{
return 'AMD Yes';
}
}
namespace base\test2;
use base\test2\Cpu;
class Intel implements Cpu
{
public function go()
{
return '灯~灯↗灯↘灯↗灯↘';
}
}
namespace chapter7;
//导入限定名称
use base\test2\AMD;
use base\test2\Intel;
//导入接口
use base\test2\Cpu;
//引入 自动加载类
require __DIR__.DIRECTORY_SEPARATOR.'autoload.php';
//声明一个工厂类
class Cpu3
{
//CPU
private $cpu;
//这边使用接口
//成功的从依赖工厂类到依赖更加抽象的工厂接口
public function __construct(Cpu $Cpujk = null)
{
$this->cpu = $Cpujk;
}
//使用!
public function Assemble()
{
return '组装电脑的CPU用@@'.$this->cpu->go();
}
}
echo (new Cpu3(new AMD()))->Assemble();
echo '<hr>';
echo (new Cpu3(new Intel()))->Assemble();
使用工厂接口抽象了之后我们就可以将这个代码实现的非常的灵活!只需要改动接口即可!