今日目标
-
能够掌握面向对象的高级特性 -
能够具备设计纯面向对象框架和系统的能力 -
能够遵循PSR-0规范开发一个基础框架 -
能够掌握单例模式 -
能够掌握工厂模式 -
能够掌握注册树模式
一、什么是设计模式?
1.概念
设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。
2.举例
如果我们把面向对象编程当成一本武功秘籍,设计模式就是其中的每一招每一式,如果我们可以把设计模式式活学活用,那在面向对象编程方面那一定是一个非常厉害的高手了!
二、开发环境准备
一、编辑器的选址
1.下载PHPStorm
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOKGLj5f-1630374301900)(5EF3F5E51F2A498B9761DC8F77B26F3F)]
2.选择编程字体
- 必须选择等宽字体
- 常见的等宽变成自提包括Courier Nes,Consolas
- 个人推荐大家使用Soure Code Pro,是由Adobe公司专为程序员设计,免费开源
默认字体展示出来的效果: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QKYQHTr5-1630374301903)(960E663F536A4DA7B80F73A3A74C0453)]
设置等宽字体: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W5G9xq1o-1630374301906)(94AA4316BB044905A9408B4864F0B384)]
设置玩字体展示出来的效果: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RkvcFxXB-1630374301907)(51062DC69EC142BA807E1D223192CB26)]
三、命名空间与类自动载入
一、 命名空间的介绍
概念:
namespace即“命名空间”,也称“名称空间” 。各种语言使用的一种代码组织的形式 通过名称空间来分类,区别不同的代码功能
基本介绍:
命名空间是用来组织和重用代码的。如同名字一样的意思,NameSpace(名字空间),之所以出来这样一个东西,是因为人类可用的单词数太少,并且不同的人写的程序不可能所有的变量都没有重名现象,对于库来说,这个问题尤其严重,如果两个人写的库文件中出现同名的变量或函数(不可避免),使用起来就有问题了。为了解决这个问题,引入了名字空间这个概念,通过使用 namespace xxx;你所使用的库函数或变量就是在该名字空间中定义的,这样一来就不会引起不必要的冲突了。
2.命名空间的使用
a)不使用命名空间,案例演示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pm551hnV-1630374301910)(7DCC5A905E57409CBEE4C2CD9771D14C)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNKJ7yDj-1630374301913)(C97227C0730C49F3BC1BBB96227C6577)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NfkddGLJ-1630374301917)(E049473790A141998E7D521A787480C0)]
运行报错(无法重新声明方法) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uLWvTYwm-1630374301918)(D820330A62E14D05AA8A0F941A9B20AB)]
b)使用命名空间案例演示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w7pgar7G-1630374301921)(FA32844B2C9340FE80333D5585F58210)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LA7doMmp-1630374301923)(C478CFB29D244C89AB1759436021BC12)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNThscA1-1630374301925)(725A3E396BA645F1B4B83CD42FBC2679)]
正常运行(输出当前的文件名称) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c7S9S4SD-1630374301930)(941C5DF24D934A9487A0FD25F6C5DDFE)]
3.Phpstorm在控制台中输出运行结果
参考链接:https://blog.csdn.net/weixin_37185329/article/details/77390005
1、先打开菜单Run->Edit Configurations选项
2、然后在打开的窗口中点击左上角的+号
3、在打开的菜单中选择PHP Script
4、在打开的界面中找到Configuration->File
5、选择你要在控制台运行的文件
6、填入工作目录的路径(可填可不填),完成配置后点击右下角的OK
7、回到主界面,点击菜单Run->Run
二、类自动载入
1.概念
在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。
spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p2Tv3aGj-1630374301931)(8FDF9970BC264E1E99BC36495BBC5E93)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ftNEEPlG-1630374301932)(298CA3B2E1DA464ABBFA7AC0D8446221)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BPFZucPg-1630374301934)(06FFD00382CC49A2BAF148F1C479ABD8)]
Note
尽管 __autoload() 函数也能自动加载类和接口,但更建议使用 spl_autoload_register() 函数。 spl_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载(同一个应用中,可以支持任意数量的加载器,比如第三方库中的)。因此,不再建议使用 __autoload() 函数,在以后的版本中它可能被弃用。
参考链接:https://www.php.net/manual/zh/function.spl-autoload-register.php
三、开发一个PRS-0的基础框架
1.PRS-0规范
- 命名空间必须与绝对路径一致
- 类名首字母必须大写
- 除入口文件外,其他".php"必须只有一个类
2.开发符合PSR-0规范的基础框架
- 全部使用命名空间
- 所有PHP文件必须自动载入,不能有include/require
3.进行开发框架
进行带领同学们共同封装一个基础框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gVUR44w-1630374301935)(21D9EA6D71214C30B9F0A52A6D280C21)]
进行测试框架
入口文件自动加载所有的类文件
五、PHP面向对象高级特性
一、SPL库的使用(PHP标准库)
参考链接:https://www.php.net/spl
SPL库的使用(PHP标准库)
- SplStack、SplQueue、SplHeap、SplFixedArray等数据结构
- ArrayItertor、AppendIterator、Countable、ArrayObject
- SPL提供的函数
1.栈操作
概念:SplStack类通过使用一个双向链表来提供栈的主要功能。
特性:先进后出
$stack = new SplStack();
$stack->push("data1\n");
$stack->push("data2\n");
echo $stack->pop();
echo $stack->pop();
2.队列操作
概念:SplQueue 类通过使用一个双向链表来提供队列的主要功能。
特性:先进先出
$queue = new SplQueue();
$queue->enqueue("data1\n");
$queue->enqueue("data2\n");
echo $queue->dequeue();
echo $queue->dequeue();
3.最小堆
概念:SplMinHeap类提供堆的主要功能,将最小值保持在顶部。
$heap = new SplMinHeap();
$heap->insert("data1\n");
$heap->insert("data2\n");
echo $heap->extract();
echo $heap->extract();
4.最小长度的数组 固定尺寸数组
概念:SplFixedArray类提供了数组的主要功能。SplFixedArray与普通PHP数组之间的主要区别在于,必须手动调整SplFixedArray的大小,并且只允许范围内的整数作为索引。优点是它比标准阵列使用更少的内存。
$array = new SplFixedArray(10);
$array[0] = 123;
$array[9] = 1234;
var_dump($array);
二、PHP链式操作的实现
1.如何实现数据库链式操作
$db->where()->limit()->order();
2.实现代码
文件:Vendor/Database.php
<?php
namespace Vendor;
class Database
{
function where($where)
{
return $this; //链式操作的核心就是返回当前对象
}
function order($order)
{
return $this;
}
function limit($limit)
{
return $this;
}
}
文件:index.php
//不是链式操作
//$db = new Vendor\Database();
//$db->where("id=1");
//$db->where("name=1");
//$db->where("id desc");
//$db->limit(10);
//链式操作
$db->where("id=1")->where("name=2")->order("id desc")->limit(10);
三、PHP魔术方法的使用
1.常见的魔术方法
- __get/__set
- __call/__callStatic
- __toString
- __invoke
2.魔术方法的实现
__get/__set
- __set 当对对象不存在的属性进行赋值时会进行调用该方法
- __get 当读取一个对象不存在的属性时会调用该方法
index.php 文件
//$obj = new Vendor\Obj();
//调用对象不存在的属性时
//$obj->title = 'hello';
//echo $obj->title;
Vendor/Obj.php 文件
protected $array = [];
//当对对象不存在的属性进行赋值时会进行调用该方法
function __set($key, $value)
{
var_dump(__METHOD__);
$this->array[$key] = $value;
}
//当读取一个对象不存在的属性时会调用该方法
function __get($key)
{
var_dump(__METHOD__);
return $this->array[$key];
}
__call/__callStatic
- __call 当调用对象中不存在的方法时自动调用
- __callStatic 当调用对象中不存在的静态方法时自动调用
index.php 文件
//调用对象不存在的方法时
//echo $obj->test('hello',123);
//调用对象不存在的静态方法时
//echo $obj::test('hello',123);
Vendor/Obj.php 文件
//当调用一个不存在的方法是会自动调用
function __call($name, $arguments)
{
// TODO: Implement __call() method.
var_dump($name,$arguments);
return "magic function\n";
}
//当调用不存在的静态方法时会自动调用
public static function __callStatic($name, $arguments)
{
// TODO: Implement __callStatic() method.
var_dump($name,$arguments);
return "magic static function\n";
}
__toString
当把对象当成字符串使用时,会自动调用该方法
index.php 文件
//将对象转成成字符串,会自动调用该方法
//echo $obj;
Vendor/Obj.php 文件
//将对象转成成字符串,会自动回调该方法
public function __toString()
{
// TODO: Implement __toString() method.
//返回一个字符串
return __CLASS__;
}
__invoke
index.php 文件
echo $obj('test1');
Vendor/Obj.php 文件
//把对象当成一个函数去用,会调用该方法
public function __invoke($params)
{
var_dump($params);
// TODO: Implement __invoke() method.
return 'invoke';
}
四、三种基础设计模式
一、基础设计模式简介
- 工厂模式(工厂方法或者类生成对象,而不是代码中直接new)
- 单例模式(使某个类的对象仅允许创建一个)
- 注册模式(全局共享和交换对象)
二、[php]工厂模式
1. 概念
工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。
2.那么何为工厂模式?
从名字来看,似乎看不出什么端倪。工厂模式,和生产有关?还是和生产流程有关?难道还和工厂领导有关?和领导秘书有关?秘书… 好了不卖关子了,所谓工厂模式还真和生产有关。生产什么呢?生产出来的是一个实例对象。通过什么设备生产?通过一个工厂类生产。怎么生产呢?工厂类调用自身静态方法来生产对象实例。
工厂模式有一个关键的构造,根据一般原则命名为Factory的静态方法,然而这只是一种原则,虽然工厂方法可以任意命名这个静态还可以接受任意数据的参数,必须返回一个对象。
3. 为什么要用工厂模式?
很多没接触过工厂模式的人会不禁问,为啥我要费那么大的劲儿去构造工厂类去创建对象呢?不去套用那些易维护,可扩展之类的话,我们可以考虑这样 一个简单的问题。如果项目中,我们通过一个类创建对象。在快完成或者已经完成,要扩展功能的时候,发现原来的类类名不是很合适或者发现类需要添加构造函数 参数才能实现功能扩展。我靠!我都通过这个类创建了一大堆对象实例了啊,难道我还要一个一个去改不成?我们现在才感受到了“高内聚低耦合”的博大精深。没 问题,工厂方法可以解决这个问题。
如果类名发生变化,或者方法的参数发生变化,需要修改所有引入该类的文件,不易于维护,紧耦合。我们要解决这个问题,所以要使用工厂方法。
工厂模式更多的是解决后期拓展(大部分是文件命名修改后)的问题。
3.工厂模式如何实现?
相对于单例模式,上面我们提供了足够的信息,工厂类,工厂类里面的静态方法。静态方法里面new一下需要创建的对象实例就搞定了。当然至于考虑 上面的第二个问题,根据工厂类静态方法的参数,我们简单做个判断就好了。管你用if…else…还是switch…case…,能快速高效完成判 断该创建哪个类的工作就好了。最后,一定要记得,工厂类静态方法返回一个对象。不是两个,更不是三个。
index.php 文件
//工厂模式
//$db = new Vendor\Database();
//$db = Vendor\Factory::createDatabase();
//var_dump($db);
Vendor/Factory.php 文件
<?php
namespace Vendor;
class Factory
{
static function createDatabase()
{
//统一进行实例化数据库连接
$db = new Database();
return $db;
}
}
总结:工厂模式是其设计模式惯用的基础模式,其它高级的设计模式都是依赖于工厂 模式
三、[php]单例模式
1.概念:
作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。
2.为什么要使用单例模式:
php使用单例模式的原因:在使用面向对象的方式开发时,使用单例模式可以避免过多的new操作消耗大量的资源。单例模式可以通过一个类来全局控制某些配置信息。
单例模式更多的是解决内存开销(new 类),同样也是为了代码美观吧
3.案例
php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作,在使用面向对象的方式开发时,如果使用单例模式,则可以避免大量的new操作消耗的资源。如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现。
4.实现的方法
Vendor/Database.php 文件
<?php
namespace Vendor;
class Database
{
private static $db;
private function __construct()
{
}
//获取实例
static function getInstance()
{
if (self::$db){
return self::$db;
}else{
self::$db = new self();
return self::$db;
}
}
function where($where)
{
return $this;
}
function order($order)
{
return $this;
}
function limit($limit)
{
return $this;
}
}
总结:单例模式解决了资源的浪费,因为我们在程序的执行过程中,只需要创建一个数据库的连接即可,所以说我们的数据库一定要使用单例模式来实现。单例模式,无论调用的多少次,只会创建一次对象。
四、[php]注册树模式
1.什么是注册树模式?
注册树模式当然也叫注册模式,注册器模式。之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更容易让人理解。像前两篇一样,我们这 篇依旧是从名字入手。注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法。 这让我想起了小时候买糖葫芦,卖糖葫芦的将糖葫芦插在一个大的杆子上,人们买的时候就取下来。不同的是,注册树模式摘下来还会有,能摘很多次,糖葫芦摘一 次就没了。。。
2.为什么要使用注册树模式?
单例模式解决的是如何在整个项目中创建唯一对象实例的问题,工厂模式解决的是如何不通过new建立实例对象的方法。 那么注册树模式想解决什么问题呢? 在考虑这个问题前,我们还是有必要考虑下前两种模式目前面临的局限。 首先,单例模式创建唯一对象的过程本身还有一种判断,即判断对象是否存在。存在则返回对象,不存在则创建对象并返回。
每次创建实例对象都要存在这么一层判断。
工厂模式更多考虑的是扩展维护的问题。
总的来说,单例模式和工厂模式可以产生更加合理的对象。怎么方便调用这些对象呢?而且在项目内如此建立的对象好像散兵游勇一样,不便统筹管理安排啊。因 而,注册树模式应运而生。不管你是通过单例模式还是工厂模式还是二者结合生成的对象,都统统给我“插到”注册树上。我用某个对象的时候,直接从注册树上取 一下就好。这和我们使用全局变量一样的方便实用。 而且注册树模式还为其他模式提供了一种非常好的想法。
3.如何实现注册树?
通过上述的描述,我们似乎很容易就找到了解决方法。首先我们需要一个作为注册树的类,这毋庸置疑。所有的对象“插入”到注册树上。这个注册树应 该由一个静态变量来充当。而且这个注册树应该是一个二维数组。这个类应该有一个插入对象实例的方法(set()),当让相对应的就应该有一个撤销对象实例 的方法(_unset())。当然最重要的是还需要有一个读取对象的方法(get())。拥有这些,我们就可以愉快地完成注册树模式啦~~~
Vendor/Register.php 文件
<?php
namespace Vendor;
class Register
{
protected static $objects;
/**表示将一个对象注册到全局的注册树上
* @param $alias 映射的名字
* @param $object 对象
*/
static function set($alias,$object)
{
//当调用set方法是将我们的对象映射到注册树上
self::$objects[$alias] = $object;
}
//调用_unset方法是,把它从树上移出
function _unset($alias)
{
unset(self::$objects[$alias]);
}
static function get($name)
{
return self::$objects[$name];
}
}
总结:什么时候注册到树上,什么时候卸载,可以由我们的环境初始化环境工作中把它做了,在我们业务逻辑代码中,只需要把这个对象读取出来即可。
|