09 php面向对象高级03
1. 设计模式
设计模式(Design pattern) 是软件开发人员在软件开发过程中面临的一般问题的解决方案
-
设计模式是一类问题的通用解决方案
-
解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
-
设计模式有很多,在PHP中通常只用两种设计模式:单例模式、工厂模式
1.1 单例模式
单例模式singleton,是一种类的设计,只会最多产生一个对象的设计思想
-
单例模式的类,只能在一次运行中产生一个对象
-
单例模式为了解决如何只产生一个对象,需要使用以下的解决方案,简称 三私一共
- 私有化构造方法:不让在外部产生多个对象
- 私有化克隆方法:不允许对象被克隆产生新对象
- 私有化静态属性:保存已经产生的对象
- 公有化静态方法:运行进入类内部产出对象
-
单例模式的目的是为了保护资源的唯一性
-
单例模式如果还有其他功能诉求,可以在类中增加相应的其他类成员
- 首先定义一个空类,叫做Singleton
class Singleton
{
}
- 对象的产生是通过实例化产生的,而实例化是一种不可控行为,即可以产生无限多个对象,所以应该禁止:即”禁止实例化“,之所以是引号,是因为只能禁止在类外部实例化对象,私有化构造方法
//在上述类中增加私有化构造方法
class Singleton{
private function __construct(){}
}
//尝试外部实例化
$s = new Singleton(); //致命错误:不能访问私有方法
- 一旦外部不能实例化对象了,那就意味着根本“不可能”产生对象了,此时就只能想办法在还没有产生对象的时候就进入到“类内部”,意味着只能通过
静态方法
让类直接进入到类的内部
//在上述类中增加公有静态方法
public static function getInstance(){
}
- 进入类的内部依然还是没有对象,此时需要在静态方法内部进行对象实例化,并且把得到的对象返回到外部
//修改公有静态方法:获取对象,并返回给外部调用出
public static function getInstance(){
return new self();
}
//外部获取对象
$s = Singleton::getInstance();
- 此方法开启了实例化对象的窗口,但是此时新问题出现:无限调用静态方法依然可以得到多个对象。如果想要该方法只返回一个对象,就得保证类内部有办法存着某个产生的对象,第一次产生新的,后面返回旧的。此时需要使用静态属性
//增加静态属性:私有,不允许外部修改,否则外部修改之后就没有意义了
private static $object = NULL; //初始化为NULL,没有对象
//修改静态方法
public static function getInstance(){
//判断内部属性是否存在对象(is_object函数):最好的判定是存的对象是当前类的 instanceof
if(!(self::$object instanceof self)){
//当前保存的内容不是当前类的对象
self::$object = new self();
}
//返回对象给外部
return self::$object;
}
- 此时可以保证外部无论多少次调用公有静态方法获取实例,都会只得到一个对象。但是此时外部对象依然可以产生新的对象:因为克隆,所以还必须禁止对象的克隆,即在类内部私有化克隆方法
//在Singleton类中增加私有化的__clone()方法
private function __clone(){}
单例模式完整的案例:
<?php
/**
* 单例模式:实现步骤
* 1. 定义空类
* 2. 杜绝能够产生多个对象的方式:构造方法私有化
* 3. 想办法产生对象:进入类内部产生对象
* 4. 想办法控制内部对象生产:只生产一个
* 5. 杜绝外部拿着已有对象产生新对象:克隆方法私有化
*/
// 1. 创建一个空类
class Singleton
{
// 4. 增加静态属性:私有,不允许外部修改,否则外部修改之后就没有意义了
private static $object = NULL; //初始化为NULL,没有对象
// 2. 私有化构造方法:防止外部无限实例化对象
private function __construct()
{
// 如果没有初始化需求,可以为空
}
// 3. 进入类内部,产生对象,供外部使用:公有静态方法
public static function getInstance()
{
//判断内部属性是否存在对象(is_object函数):最好的判定是存的对象是当前类的 instanceof
if (!(self::$object instanceof self)) {
//当前保存的内容不是当前类的对象
self::$object = new self();
}
//返回对象给外部
return self::$object;
}
//5. 在Singleton类中增加私有化的__clone()方法
private function __clone() {}
}
//外部获取对象
$s1 = Singleton::getInstance();
$s2 = Singleton::getInstance();
var_dump($s1, $s2); // 是同一个对象
1.2 工厂模式factory
工厂模式factory,是指像工厂一样流水线生产对象,由一个地方生产对象,其他位置就不需要额外实例化对象,从而可以方便后期代码统一的维护,而且工厂模式下可以方便隐藏真实的类结构,因此也更加安全
- 工厂模式是一种按需生产对象的模式
- 工厂模式通常是需要在大型项目中,会出现很多的相同功能的类,此时可以使用工厂产生对象
- 工厂模式的优点是能够方便后期对类的维护
- 工厂模式的缺点是随着功能增加,会需要增加很多开发量(开发多个工厂)
<?php
/**
* 工厂模式:需生产对象
* 1. 创建不同的类
* 2. 创建生产对象的工厂类
* 3. 调用工厂类生成对象
*/
// 1. 创建不同的类
// 工厂模式针对的是“相同模型”的统一产出,就是使用工厂模式产出对象对应的类都有相同的结构或者功能。所以,首先要有一批具有类似功能的类(其实本质是同样的大类下的小类)
class Man
{
public function display()
{
echo '这是男人<br/>';
}
}
class Woman
{
public function display()
{
echo '这是女人<br/>';
}
}
class Ladyboy
{
public function display()
{
echo '这是人妖<br/>';
}
}
// 2. 以前访问这些类的话都需要通过new 类名来实现,多出使用就多次new。如果使用工厂模式的话,就需要增加一个工厂类:HumanFactory
// 工厂类:生产对象
class HumanFactory
{
// 静态工厂
public static function getInstance($flag)
{
//$flag可以是m代表Man,w代表Woman,L代表Ladyboy
switch ($flag) {
case 'm':
return new Man();
case 'w':
return new Woman();
case 'L':
return new Ladyboy();
default:
return null; //匹配失败,返回空
}
}
}
// 3. 调用工厂类生成对象
$m = HumanFactory::getInstance('m');
$m->display(); //这是男人
$w = HumanFactory::getInstance('w');
$w->display(); //这是女人
$L = HumanFactory::getInstance('L');
$L->display(); //这是人妖
2. 命名空间namespace
2.1 基础
命名空间namespace,是指人为的将内存进行分隔
,让不同内存区域的同名结构共存。从而解决在大型项目中可能出现的重名结构问题
- 基本语法:namespace 空间名字;
//定义空间
namespace my_space; // 定义一个叫做my_space的空间
- 命名空间的命名规则
- 由字母、下划线和数字构成
- 可以以字母和下划线开头
- 较少出现多单词空间名,一般使用下划线法
- 命名空间的作用:能够创建同名结构,包含函数、常量和类
namespace space1;
function display(){
echo __NAMESPACE__,'<br/>';
}
const PI = 3;
class Human{}
namespace space2;
function display(){
echo __NAMESPACE__,'<br/>';
}
const PI = 3.14;
class Human{}
- 命名空间里的内容
- 命名空间里可以定义同名的函数、常量和类(结构):因为此类结构不允许同名,这些是命名空间规范的目标(称为空间元素)
- 命名空间里可以有其他代码
- 命名空间里可以书写任意代码
namespace space;
class Human{}
function display(){}
const PI = 3.14;
$a = 100;
echo $a;
- 命名空间的定义必须在脚本的最前面:命名空间的声明(第一次)必须在所有代码之前
- 一个脚本中通常只会定义一个空间
7, 命名空间其实就好比是磁盘上划分的不同文件夹,用来保存同名文件
2.2 子空间
子空间,即在已有空间之上,再在内部进行空间划分,让每个小空间独立起来。
-
子空间也是通过namespace实现,用namespace+
\
区分上下级空间名 -
基于一个脚本中通常只有一个空间名,所以子空间的创建可以直接创建(不用一定先创建一级空间)
-
子空间理论上可以创建无限多层,但是实际层次根据项目需求确定(一般不超过四层)
- 命名空间子空间是直接通过namespace+路径符号
\
实现
namespace space; //创建一个一级空间
function display(){}
//创建子空间
namespace space\space1; //在space空间下创建一个叫做space1的子空间
function display(){}
- 子空间的创建不一定非要在前面创建了上级空间,即可以直接在某个脚本中创建子空间
//脚本最上面
namespace space\space2;
function display(){}
2.3 访问
命名空间访问,是指访问不同空间里的结构元素
在PHP命名空间中,提供了三种空间元素的访问方式:非限定名称
、限定名称
和完全限定名称
- 非限定名称访问:即直接访问空间元素的名字,此类访问访问的是当前代码所属空间内的元素
namespace space1;
function display(){
echo 'space1';
}
namespace space2;
function display(){
echo 'space2';
}
//非限定名称访问:就好比是访问当前自己文件夹下的所有文件
display(); //输出space2,因为当前display函数调用所属空间为space2
- 限定名称访问,即在访问元素的前面使用相应的空间名字,非限定名称的访问是基于子空间来实现的
//定义子空间
namespace space\space1;
function display(){
echo 'space\space1<br/>';
}
//定义子空间
namespace space\space2;
function display(){
echo 'space\space2<br/>';
}
//所属父空间
namespace space;
function display(){
echo 'space<br/>';
}
//非限定名称访问
display(); //space:当前向上所属空间
// 限定名称访问:好比访问当前文件夹下的子文件夹内容
space1\display(); //space\space1:实际为当前空间space + space1\display()
- 完全限定名称访问,即从根目录(全局空间)开始访问,使用
\
作为全局空间开始符号
//完全限定名称访问
\space\display(); //space空间下的display
\space\space1\display(); //space下space1空间的display
2.4 全局空间
并非所有情况下都需要使用命名空间,命名空间是针对有些可能出现重名的内容时,才会去使用的。另外,如果不使用命名空间,本质上系统会默认使用了空间,这就是全局空间
全局空间就是没有使用namespace定义空间的空间(所有空间本质都是在全局空间下划分)
- 全局空间的元素访问使用完全限定名称访问
- 系统内置的函数、常量和类都属于全局空间
- 系统函数、常量,在空间内访问的时候系统会自动在自己空间找,如果找不到会去全局空间
- 系统类必须使用全局空间访问:\类名
- 没有指定空间的元素所属的空间属于全局空间
//不定义空间:默认就是全局空间
function display(){
echo __NAMESPACE__,'<br/>';
}
- 所有的空间本质都是在全局空间下的划分
//定义空间
namespace space;
function display(){
echo __NAMESPACE__,'<br/>';
}
//space空间属于从全局空间里划分出一部分用于space空间管理
- 全局空间元素的访问:使用完全限定名称访问
//不定义空间
function display(){
echo __NAMESPACE__,'<br/>';
}
display(); //非限定名称访问:本身当前就是全局空间内,所以可以访问
\display(); //完全限定名称访问:全局符号"\"+全局空间元素
- 如果一个文件有空间,包含了一个没有空间的文件,那么要访问文件中的内容,需要使用全局空间
//无空间文件:nospace.php
function display(){ //属于全局空间
echo __FUNCTION__;
}
//有空间文件
namespace space();
function display(){
echo 'space';
}
//包含无空间文件
include_once 'nospace.php';
//访问元素
display(); //访问的是space空间下的display函数
\display(); //正确:访问全局空间的display函数
//注意:如果space空间没有display的话,直接访问display函数也是正确的,因为系统会自动寻找全局空间
2.5 应用
2.5 引入
命名空间引入其实就是将另外一个空间的元素(类、函数和常量)引入到当前空间来,当做当前空间的元素访问,从而可以减少复杂的完全限定名称访问,取而代之的是非限定名称访问
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!