单例模式
单例模式(也叫单件模式)是让一个类在内存中仅有一个实例。(也就是类的访问,不实例化对象new)
单例模式的优势
单例模式保证在整个应用程序的声明周期中,任何一个时刻,单例类的实例都只存在一个,从而更加高效地利用系统资源。
技术点要求
- 不能用
new
类名的方式来创建一个对象; - 禁止类的构造方法被重写;
- 禁止类的实例被外界克隆;
<?php
class Db{
public function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
}
$db1=new Db();//在PHP面向对象二 说道new 个对象就代表在内存中分配个地址
$db2=new Db();
那么我们把构造函数私有化(private
)这样的话,因为是私有属性外部无法访问,也就阻止了实例化。
实现技术点1
<?php
class Db{
public static $db;//保存类的唯一实例对象
private function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
static function getDb(){
if(self::$db==null && !(self::$db instanceof self)){
//self::$db instanceof self 这句话的意思就是如果 $db 是不是这个类的本身
self::$db=new self();
}
return self::$db;
}
}
Db::getDb();
Db::getDb();
以上要注意的点
<?php
class Db{
private static $db; //保存类的唯一实例对象
// public static $db; public 修饰符不推荐使用
private function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
static function getDb(){
if(self::$db==null && !(self::$db instanceof self) ){
//self::$db instanceof self 这句话的意思就是如果 $db 是不是这个类的本身
self::$db=new self();
}
return self::$db;
}
public function test(){
echo "我是test方法<br/>";
}
}
Db::getDb();
Db::getDb();
$db=Db::getDb();
//如果上面的getDb方法不判断这个对象是否是类的本身会出现一个漏洞 这是修饰符是public 的情况下
//最好 是 修饰符为 private
//测试 && !(self::$db instanceof self) 去掉 Db::$db = 111; 在外边赋值 会出现错误
$db->test();
//当然 现在 也有缺陷
class myDb extends Db{
public function __construct()
{
echo "myDb类有新对象创建<br/>";
}
public function test1(){
echo "我是子类中的test1方法";
}
}
$myDb= new MyDb(); //单例的类被继承重写了 单例模式的类不完全
被继承的类(还能被new)就不是单类了,怎么解决重写? 加关键字final
class Db{
private static $db; //保存类的唯一实例对象
//加上关键字final 修饰 禁止重写
final private function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
}
第二条技术点解决。
这样被修饰在继承扩展也只能在扩展类声明静态的方法。
因为用的时候myDb并没有实例化、虽然子类重写但并不能实例化、而单例模式是只实例化一个对象。
链式操作是对象操作,所以扩展的子类只能是用静态方法扩展。
技术点3
<?php
class Db{
private static $db; //保存类的唯一实例对象
// public static $db; public 修饰符不推荐使用
final private function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
static function getDb(){
if(self::$db==null && !(self::$db instanceof self) ){
//self::$db instanceof self 这句话的意思就是如果 $db 是不是这个类的本身
self::$db=new self();
}
return self::$db;
}
public function test(){
echo "我是test方法<br/>";
}
}
$db=Db::getDb();
$db1= clone $db;
if($db1==$db){
echo "克隆出的对象属性和方法一致<br/>";
}
if($db1===$db){
echo "是一个对象";
}else{
echo "不是一个对象";
}
结果这个类被克隆出来了、并不符合单例模式的设计。
解决方法就是在类的内部声明__clone()魔术方法设置私有(private)这样外部访问克隆,直接报错。
__clone()
魔术方法是当对象被克隆是自动调用的方法
<?php
class Db{
private static $db; //保存类的唯一实例对象
// public static $db; public 修饰符不推荐使用
final private function __construct()
{
echo "有新的Db类的对象创建了<br/>";
}
static function getDb(){
if(self::$db==null && !(self::$db instanceof self) ){
//self::$db instanceof self 这句话的意思就是如果 $db 是不是这个类的本身
self::$db=new self();
}
return self::$db;
}
public function test(){
echo "我是test方法<br/>";
}
// public function __clone(){
// echo "我被克隆了<br/>";
// }
private function __clone(){
echo "克隆会出错的<br/>";
}
}
$db=Db::getDb();
$db1= clone $db;
if($db1==$db){
echo "克隆出的对象属性和方法一致<br/>";
}
if($db1===$db){
echo "是一个对象";
}else{
echo "不是一个对象";
}
扩展个Db类
<?php
class Db{
private $link;
private static $db;
protected static $sql=[
];
private function __construct($host,$uname,$upass,$Dbname)
{
$link=mysqli_connect($host,$uname,$upass,$Dbname) or die('数据厍连接失败');
mysqli_set_charset($link,'utf-8');
mysqli_select_db($link,$Dbname);
$this->link=$link;
return $this;
}
public static function configDb($host,$uname,$upass,$Dbname){
if(self::$db==null && !(self::$db instanceof self) ){
self::$db=new self($host,$uname,$upass,$Dbname);
}
return self::$db;
}
public static function name($name){
self::$sql['name']=$name;
return self::$db;
}
public function order($order='desc')
{
self::$sql['order']=$order;
return self::$db;
}
function find(){
$sqlArr=self::$sql;
$sql="select * from {$sqlArr['name']} limit 1";
$resArr=null;
$res=mysqli_query($this->link,$sql);
if(mysqli_num_rows($res)>0){
$resArr=mysqli_fetch_assoc($res);
}
return $resArr;
}
function select()
{
$sqlArr=self::$sql;
$sql="select * from {$sqlArr['name']} order by b_id ASC";
$resArr=[];
$res=mysqli_query($this->link,$sql);
if(mysqli_num_rows($res)>0){
while ($arr=mysqli_fetch_assoc($res)){
array_push($resArr,$arr);
}
}
return $resArr;
}
}
Db::configDb('127.0.0.1','root',123456,'dongli');
$res=Db::name('k_about')->find();
var_dump($res);
$res=Db::name('k_banner')->find();
var_dump($res);
$res=Db::name('k_banner')->order()->select();
var_dump($res);