Hi,阿金 ...

PHP的设计模式

发表: 2019-04-14 分类:

设计模式

设计模式是开发人员在面向对象编程实践在总结的经验和方案.
PHP中常用到的11种设计模式

  1. 工厂模式
  2. 单例模式
  3. 注册模式
  4. 适配器模式
  5. 策略模式
  6. 数据对象映射模式
  7. 观察者模式
  8. 原型模式
  9. 装饰器模式
  10. 迭代器模式
  11. 代理模式

设计模式参考 :Php设计模式

设计原则

1.单一职责 原则

一个类,只专心做好一件事情

2.开/闭 原则(OCP)

对扩展开放,对修改关闭.使程序易于升级与维护,对程序扩展时尽量不去修改原有代码.

3.里氏代换 原则(LSP)

任何基类可以出现的地方,子类一定可以出现.但不能相反.

4.依赖倒转 原则

开闭原则的基础,针对接口编程,依赖抽象而不依赖具体.

5.接口隔离 原则(ISP)

降低类之间的耦合度,最小单一职责接口,不使用不需要的接口.(这里的接口指抽象类,抽象层)

6.迪米特 法则(LOD)

最小知道原则,
被依赖者:只暴露应该暴露的方法,
依赖者:只依赖应该依赖的对象

7. 配置化

尽量使用配置化,而不是硬编码

8.合成复用 原则

尽量使用合成/聚合方式,而不是使用继承.

php设计模式的基础

了解的设计模式的基础,先了解PHP的基础语法和面向对象的特性,以及每个PHP版本的更新变化.
面向对象的高级特性.

命名空间

php5.3以后新增了命名空间. 命名空间允许同名的类名,避免命名冲突.
命名空间和文件系统中的目录结构原理类似.
可以于类,函数,常量一起使用.方便软件工程管理.

参考: php手册-命名空间

定义命名空间

必须在php文件前申明,以”\”分割多个单词.
注意点:

  1. 命名空间名字为变量名,如不能以数字或特殊字符开头;
  2. 不能使用保留字,如php,PHP,PHP\Classes
  3. 名称本身不能与其它命名重复冲突

要符合PSR-4规范,命名空间要与文件目录和文件名一一关联,注意文件名大小写,从根目录往下层级一一对应.
参考: psr-4自动加载规范>>

[app.php]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
namespace App;

//常量 5.3以后可以使用const 定义常量
const Author="arkin";

// 函数
function index(){
echo "index";
}
// 类
class testClass{
const NAME="testClass";
// 静态方法
static function index(){
echo "testClass.index";
}
}

?>

也可以在同一个文件中定义多个命名空间.
命名空间,之后的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 第一种
<?php
namespace Model1;
class AModel {

}
namespace Model2;
class BModel{
}
?>
// 第二种,使用大括号,这种语句命名空间{}之外不能有任何语句.除了declare.
<?php
namespace Model1{
class AModel {

}
}
namespace Model2{
class BModel{
}
}
// 全局命名空间
namespace {
}
?>

使用命名空间

引用文件后,可以使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require ('app.php');
// 使用常量
echo App\Author;
// 使用函数
echo App\index();
// 使用类
$AClass= new App\testClass();
// 使用类的常量
echo App\testClass::Name;
// 调用类的静态方法
echo App\testClass::index();
// 全局 空间,包含系统自带类和函数
throw new \Exception("有错误");
?>

use 别名

除了以上通过全路径命名空间外,可use别名.
(>PHP5.6)
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
// 类别名,简写 use App\Controller;
use App\Controller as Controller;
// 函数别名
use function App\Controller\out as msg;
// 常量别名
use const App\Controller\Module as MdName;
// 多个连写
use App\Model\A,App\Model\B;
// 子命名空间
use App\Model\{
User,
function bindsql,
const Version as MySQLVer
};

?>

匿名函数

参考: php手册-匿名函数
在php5.3后引用.
又叫闭包函数(closures),其它语言使用lambda关键字.经常用于回调函数.临时创建的一个没名称的函数.和普通函数一样.
PHP匿名函数通过Closure类来实现.

定义

1
2
3
4
5
6
7
8
9
10
11
// 示例1
$fun=function(){
return true;
}
var_dump($fun);// 返回object(Closure)#2 (0)
$fun();

// 示例2
array_map(function ($v1,$v2){
echo $v2.'-'.$v1.PHP_EOL;
},[1,2],['a','b']);

匿名函数 变量传递

通过use来传递变量.变量类型.支持所有变量类型.
注:在PHP7.1以后,不允许传递\$this和内置的超全局变量(如\$_GET,\$_COOKIE),在匿名函数中可以获取到.
也不能与参数名重复.
例子:

1
2
3
4
5
6
7
8
9
10
$userName="arkin";
$local="北京";
// 这里使用的强类型,use ()传递变量.
$show=function (string $msg) use($userName,&$local) :string{
$local="上海";
return $userName.':'.$msg;
};

echo $show('走你!');// 输出,'arkin:走你'
echo $local;// 输出: 上海

绑定对象

可以绑定新的对象到匿名函数.其实就Closure对象.

1
2
3
4
5
6
7
8
// 此时$this为空
$func=function (){
var_dump($this);
};
// 把新的对象 stdClass绑定到 匿名函数
$fun2=$func->bindTo(new stdClass());
// 此时的$this 为 stdClass()
$fun2();

匿名函数在php5.3以后会自动绑定$this,除非申明函数为静态匿名函数.

1
2
3
4
5
6
7
8
9
10
11
12
class A {
function __construct(){
//
$fun=function(){
var_dump($this);// 正常
};
// 静态匿名函数
return static function (){
var_dump($this);// 报错
}
}
}

回调函数

内核自带很多地方需要用到回调函数.
回调函数定义的6种方法.不同的函数可能实现不一样,大部分通用.
下面以array_map()为例;创建回调函数的方法.

1. 普通函数

回调函数字符串的函数名称.

1
2
3
4
5
6
<?php
function callback($v){
echo $v;
}
array_map('callback',[1,2,3]);
?>

2. 匿名函数

使用匿名函数,最常用的方法.

1
2
3
<?php
array_map(function($v){echo $v;},[1,2,3]);
?>

3. 类的方法

支持两种字符类名+方法名与数组类名+方法名;

1
2
3
4
5
6
7
8
9
class MyClass {
static function callback($v){}
}
// 第1种, 静态类字符串
array_map('MyClass::callback',[1,2,3]);
// 第2种, 静态类数组
array_map(['MyClass','callback'],[1,2,3]);
// 第3种, 实例对象方法
array_map([new MyClass(),'callback'],[1,2,3]);

4. 命名空间

和静态方法类似.

1
2
3
namespace My;
static function callback($v){};
array_map('My\callback',[1,2,3]);

5. 函数生成器

使用create_function 函数生成器,有时在代码生成器时需要到.

1
array_map(create_function('$v','echo $v;'),[1,2,3]);

6. 内置函数

1
$arr=array_map('htmlentities',['<a>','<script>','xxxp']);

匿名类

php7.0开始支持匿名类,anonymous class;

定义

和普通类一样定义,也可以继承 ,使用接口

1
2
3
4
5
6
7
8
$class=new class{
function __construct()
{
echo "haha";
}
function lala(){}
}
$class->lala();

stdClass对象

stdClass是一个特殊的基类,不需要定义就可以使用.但是stdClass没有方法,只能使用属性.
由于php没有定义空对象的方法,一般使用stdClass来创建一个空对象.

1
2
3
4
5
6
$obj= new stdClass();
$obj->name="你主宗";
echo json_encode($obj);// {name:"你主宗"}

//在JS中可以简单
var obj={"name":"你大爷"};

类的自动加载

php使用require或include手动引用,在php5.2以后提供php自动引用功能.
自动引用类机制是,当发现未加载的类时会自动执行载入函数.

__autoload()

__authload()函数只可以定义一次.

1
2
3
4
5
6
7
8
9
<?php
Test1::index();
Test2::index();
//$class 为 类名,包含命名空间
function __autoload($class){
echo "自动载入类:",$class;
require __DIR__.DIRECTORY_SEPARATOR.$class.".php";
}
?>

spl_autoload_register()

spl是PHP的标准库的一类.
参考 :

  1. spl标准库
  2. spl_autoload_register

spl_autoload_register好处:

  1. 可以定义多个autoload函数
  2. 支持异常处理
  3. 可以注销已注册的函数

示例简单自动加载,使用了命名空间.
目录结构如下:

Loader.php,作为统一加载类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace Bootstrap;
class Loader
{
private $classMap=[];
public function __construct()
{
// 注册加载函数
spl_autoload_register([$this,'register'],true);
}
function __destruct()
{
spl_autoload_unregister([$this,'register']);
}
// 加载函数
private function register($class){
// 目录分隔符 windows为 '\',linux为 '/'
$sep=DIRECTORY_SEPARATOR;
$file=__ROOT.$sep.str_replace('\\',$sep,$class).'.php';
if(file_exists($file)){
$loaded=include($file);
if($loaded){
$this->classMap[$class]=$file;
}
}
}

public function getClassMap(){
return $this->classMap;
}
}

index.php,入口文件引用

1
2
3
4
5
6
7
8
9
// 根目录
const __ROOT=__DIR__;
require_once __ROOT.'/Bootstrap/Loader.php';
$autoload=new \Bootstrap\Loader();

// 使用类
(new App\Controller\Home())->index();

var_dump($autoload->getClassMap());

PHP标准库(SPL)

参考 : SPL文档
常用到的是:SPL函数,数据结构和迭代器

PHP常用的数据结构

PHP常见的数据结构,数组,队列,堆,栈

1.数组

数组: 连续方式存储数据的结构,可以通过索引访问.注意不是指PHP的数组.
特点: 固定长度,索引只能为数字,性能快
使用SplFixedArray类

1
2
3
4
$arr= new SplFixedArray(3);//0,1,2
$arr[2]="abc";
$arr[3]="ccdd";// 这里会报错,超时索引最大值
$arr->getSize(4);// 可以修改数组长度

PHP内置数组底层使用的是HashTable实现.

2.队列

一种特殊的线性表,特性是先进先出.
主要支持:入队(enqueue),出队(dequeue)

1
2
3
4
5
$que = new SplQueue();
$que->enqueue("1");
$que->enqueue("2");
echo $que->dequeue();//1
echo $que->dequeue();//2

3.栈

特性: 先进后出
主要方法: 入栈(push)/出栈(pop)

1
2
3
4
5
que = new SplStack();
$que->push("1");
$que->push("2");
echo $que->pop();//2
echo $que->pop();//1

4.堆

不会.

序列化/反序列化

序列化的目的,是将变量转成可存储和传输的字符串的过程.
PHP常用的序列化方式,

  1. json_encode/json_decode
  2. serialize/unserialize

serialize 对象

对象在序列化时会先尝试调用sleep(),unserialize时尝试调用wakeup();
serialize不可以序列化匿名类.
// 第一种,使用sleep和wakup
__sleep用来做返回属性名称的数组,不能返回父类属性.
注意:反序列化时,类必须存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class myClass{

public $name="arkin";
protected $sex="1";
const Local="beijin";
public function __construct()
{
$this->name="zhangesan";
$this->sex=2;
}

function out(){
echo $this->name,$this->sex,self::Local;
}
function __wakeup(){
$this->out();
}

function __sleep(){
return ['name'];
}
};

$str=serialize(new myClass());
echo $str,PHP_EOL;

$obj=unserialize($str);
$obj->name;

应用场景

在反序列化对象时,需要加载对应的类.
序列化,常用于缓存对象,异构系统之间传输对象.
常见的异步处理消息队列,在消息体中会存储依赖类的路径和序列化的数据;在消费时会加载依赖的类,在反序列化.
反顺序化时会调用自动加载类函数.__autoload或spl_autoload_register;

PSR规范

无规矩没方圆, PHP规范是PHP软件工程的基础.

  1. PSR-0 和 PSR-4: 自动加载标准,命名规范,现在使用PSR-4代替.
  2. PSR-1 和 PSR-2: 编码风格规范
  3. PSR-3 : 日志规范

PSR-0 ~ PSR-4

psr-4

  1. 全部使用命名空间,命名空间与目录结构一致;
  2. 所有PHP文件必须使用自动载入,不能有Include/require;
  3. 只有一个单一入口;
  4. 类名要与文件名一致,一个文件只有一个类,不能含有其他运行代码;

大部分主流的PHP的框架都遵守PSR规范.
其它规范参考官方文档,与设计模式无关.

PHP链式操作

在一些框架,如Laravel中经常使用链式操作.
实现原理:通过调用方法中 返加 \$this;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    class BaseModel
{

function __construct()
{
}

function where(){
echo "条件",PHP_EOL;
return $this;
}

function order(){
echo "排序",PHP_EOL;
return $this;
}

function find(){
echo "查询",PHP_EOL;
return $this;
}
}

(new BaseModel())->where()->order()->find();

魔术方法

get/set

对象属性不存在时;
set: 当外部设置赋值一个不存在的属性时,会触set方法;该方法有两个参数:\$name:属性名称,\$value:属性值;

1
2
3
4
5
6
7
8
class MyClass{

function __set($name, $value){
$this->$name=$value;
}
}
$obj= new MyClass();
$obj->name="haha";// 不存在name属性

get: 当调用一个不存在的属性时,会触发 \get方法;

1
2
3
4
5
6
7
8
class MyClass{

function __get($name){
return $name."不存在";
}
}
$obj= new MyClass();
$obj->name="haha";// 不存在name属性

call/callStatic

对象和类方法不存在时.
当调用不存在的方法时,会触发 call方法 call有两个参数: \$func:方法名,\$params:参数
返回: 当作不存在方法的返回值.

__callStatic: 为静态方法

1
2
3
4
5
class MyClass{
static __callStatic($func,$params){
return 123;
}
}

__toString

将对象转字符串.
在 echo 一个对象时.
可以使用 __toString(){ return “classsxx”;} 返回字符串.

__invoke

将对象当成函数来执行;

1
2
3
4
5
6
7
class MyClass{
function __invoke(){
return "把对象当成函数来执行";
}
}
$obj= new MyClass();
echo $obj();

浅拷贝与深拷贝

这个在JS中也是一个注意点,拷贝本身是一个耗资源的过程.
为了利用内存空间,程序语言设计时都在拷贝做优化,在文件系统中快捷方式类似.
浅拷贝:指共享内存空间地址,起了个别名,修改一个很影响到另一个.
深拷贝:指把内存空间复制一份出来,另起存储,从拷贝的那一刻起,大家一刀两断,互不影响.

普通变量

“=”赋值为深拷贝,使用”=&”赋值为浅拷贝.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$a=100;
// 深拷贝
$b=$a;
// 浅拷贝
$c=&$a;
$a=200;
var_export([
'$a'=>$a,//200
'$b'=>$b,//100
'$c'=>$c,//200
]);

$c=300;
var_export([
'$a'=>$a,//300
'$b'=>$b,//还是100
'$c'=>$c,//300
]);

对象

普通”=”赋值为浅拷贝,使用”clone”为深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$obj=new stdClass();
$obj->name="阿金";
// 浅拷贝,相当于起了别名
$boy=$obj;
// 深拷贝,clone了一模一样的我
$girl=clone $obj;
$obj->name="Arkin";

echo $obj->name.'--'.$boy->name,PHP_EOL;//Arkin - Arkin
$boy->name="Kim";
echo $obj->name.'--'.$boy->name,PHP_EOL;//Kim - Kim

echo $obj->name.'--'.$girl->name,PHP_EOL;//Kim - Arkin
$girl->name="丽娜";
echo $obj->name.'--'.$girl->name,PHP_EOL;//Kim - 丽娜

函数参数

与变量赋值一样,普通参数为深拷贝,带”&”为浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ren="红";
// 出淤泥不染
function lianhua($color){
$color="黑";
echo $color;
}
// 变了
function shehui(&$color){
$color="黑";
echo $color;
}

lianhua($ren);
echo $ren,PHP_EOL;// 保持本心,"红"
shehui($ren);
echo $ren,PHP_EOL;// 被传染,"黑"

PHP设计模式

常用的工厂模工,单例模式,注册机模式;

工厂模式

使用统一的类或方法去new 对象,而不是手工new 对象;
优点:
单一创建对象入口;当项目中有很多地方需要new 一个对象时,假如对象的参数,或对象名变化,就需要批量修改 new 的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DataBase(){
function __construct($host,$database,$user,$password){
}
}

class Factory{
static function createDatabase(){
$db=new DataBase("127.0.0.1",'test','root','123456');
return $db;
}
}

$db1=Factory::createDatabase();
$db2=Factory::createDatabase();
$db3=Factory::createDatabase();

单例模式

避免资源浪费,减少连接数.如数据库连接,共用一个即可.
如上,已经new 过的对象,后面就不需要再次new了.
主要实现步骤:

  1. 把 __construct 析构函数变成私有private.就不可以直接new 实例化.
  2. 申明一个获取实例的静态方法, 如getInstance()方法
  3. 申明一个私有的属性来存储实例.
  4. 当发现未实例化过,实例化 new self();,已实例化则直接返回该实例.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DataBase(){
protected static $db=null;
private function __construct($host,$database,$user,$password){
}
static function getInstance($host,$database,$user,$password){
if(self::$db){
return self::$db;
}
self::$db= new self();
return self::$db;
}
}

class Factory{
static function createDatabase(){
$db=DataBase::getInstance("127.0.0.1",'test','root','123456');
// 下面模式 Register,注册到db1
Register::set('db1',$db);
return $db;
}
}
$db1=Factory::createDatabase();
$db2=Factory::createDatabase();

注册机模式

全局统一注册,其它地方获取而不是用实例化.
主要两个方法: set(‘别名’,’实例化对象’),remove(‘别名’),get(‘别名’);
统一set和remove,其它统一get.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 
class Register
{
// 对象库
protected static $objects=[];
static function set($alias,$object){
self::$objects[$alias]=$object;
}

static function remove($alias){
unset(self::$objects[$alias]);
}

static function get($alias){
return self::$objects[$alias];
}
}

适配器模式

将截然不同的函数接口封装成统一的API
比如,数据库将mysql,mysqli,pdo封装成成统一操作接口,比如缓存:统一redis,memecache,mongodb

实现步骤:

  1. 约定统一接口
  2. 分别创建实例,实现约定的方法
1
2
3
4
5
6
// 统一接口
interface IDatabase{
function connect($host,$user,$password,$dbName);
function query($sql);
function close();
}

其它实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 实现接口
class Mysqli implements IDatabase
{

protected $conn;

function connect($host,$user,$password,$dbName){
$conn=mysqli_connect($host,$user,$password,$dbName);
$this->conn=$conn;
}

function query($sql){
return mysqli_query($this->conn,$sql);
}

function close(){
mysqli_close($this->conn);
}

}

策略模式

将一组特定的行为和算法封装成类,以适应某些特定的上下文环境.

比如, 不同的用户角色类型,展示不同的广告.不同的角色使用不同的策略.如果很多页面,则需要很多次硬编码.
使用策略模式,可以减少工作量和解耦.

实现方式:

  1. 定义接口类
  2. 每种策略实现一种接口
  3. 在调用的提供一个设置策略接口
  4. 全局统一策略,把策略传递给需要实现策略的地方

广告策略类:
1定义接口

1
2
3
4
5
interface UserStrategy
{
function showAd();
function showCategory();
}

2.实现多种策略接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Male implements UserStrategy
{
function showAd(){
echo "男装";
}
function showCategory(){}
}

class Female implements UserStrategy
{
function showAd(){
echo "女装";
}
function showCategory(){}
}

3页面

1
2
3
4
5
6
7
8
9
10
class Home {
protected $strategy;
// 设置
function setStrategy(\App\Strategy\UserStrategy $strategy){
$this->strategy=$strategy;
}
function index{
echo "广告",$this->strategy->show();
}
}

4入口,根据不同条件,传递不同接口实现

1
2
3
4
5
6
7
if(isset($_GET['female'])){
$strategy= new \App\Strategy\Female();
}else{
$strategy= new \App\Strategy\Male();
}
$page->setStrategy($strategy);
$page->index();

数据对象映射模式

将对象和数据存储映射,对一个对象的操作会映射为对数据的操作;
场景: 比如Model的属性和表的字段映射起来.常用在ORM上.
简单实现方式:

  1. 创建一个类,属性与数据库表字段一致.
  2. construct时连接数据库表
  3. destruct量把model的属性更新到数据表
    示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class User {
public $id;
public $name;
public $mobile;

function __construct($id)
{
$this->db= new \App\Database\Mysqli();
$this->db->connect('127.0.0.1','root','123456','Test');
$res= $this->db->query("select * from users where id=$id");

$data=$res->fetch_assoc();
$this->id=$id;
$this->name=$data['name'];
$this->mobile=$data['mobile'];
}

function __destruct()
{
$sql="update users set name='{$this->name}',mobile='{$this->mobile}' where id={$this->id}";
$this->db->query($sql);
}
}

// 使用X22 `
$model=new User(1);
$model->name="小画"
$model->13800013800;

观察者模式

当一个对象状态发生变化时,依赖它的对象全部收到通知,并自动更新.
场景: 一个事件发生后,要执行一连串更新操作.
传统方法是,直接在事件中加入处理逻辑,当需要加入或更新更多的处理逻辑时就需要修改事件主体本身.
观察者模式使用:通知 与 更新机制
即是: 订阅 –> 通知 模型
使用之前:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace App\Events;
class OrderEvent
{
// 触发器
function trigger() {

echo "事件",PHP_EOL;
echo "处理逻辑1",PHP_EOL;
echo "处理逻辑2",PHP_EOL;
echo "处理逻辑3",PHP_EOL;
}
}
use App\Events;
$event=new Events\OrderEvent();
$event->trigger();

使用观察者:

分 事件部分 和 观察者部分.
事件部分:
1步:事件发生 抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace App\Events;

use App\Observers\Observer;
abstract class EventGenerator {

/** @var array 观者者列表 */
private $observers=[];

/**
* 增加观察者
* @param Observer $observer 观察者对象
*/
function addObserver(Observer $observer){
$this->observers[]=$observer;
}

/**
* 触发通知
*/
function notify(){
foreach ($this->observers as $observer){
$observer->update();
}
}
}

2步,观察者接口

1
2
3
4
5
6
7
8
9
10
namespace App\Observers;

interface Observer
{
/**
* @param null $event 事件信息
* @return mixed
*/
function update($event=NULL);
}

(PS,由于markdown编辑器代码太多,出现卡顿,下面使用图片代替代码)

3步,实现具体事件

4步,创建观察者,一个或多个

5步,事件中,添加观察者

运行结果.

事件观察者和事件监听者一样.稍微区别.
事件观察: 被观察者 –> 观察者(多个)
事件监听: 事件源(多个)–> 事件 –> 监听器(多个)
盗用图:

原型模式

与工厂模式类似,都是统一创建对象.
原型类是JS中的原型,先创建好一个原型对象,然后clone 原型对象来创建新的对象.
这样免去了类实例化操作,减少内存开销.
先理解对象的深拷贝和浅拷贝.

使用场景: 初始实例化类消耗大量资源时

步骤:1. 创建一个原型对象 2. clone 原型对象

装饰器模式

实际上,可以动态和修改 类的功能.
场景: 需要灵活动态添加和修改类的功能时.
也可以理解为中间件.钩子

如: 控制器 增加调用前和调用后两个装饰器,就可以动态添加改变内容了.
1步,绑定勾子,寻增加装饰函数,执行函数

2步,调用时可以动态添加装饰函数

除了装饰函数,也可以使用装饰类.
类似CI框架通过大量的勾子实现中间件的功能.
Laravel框架中间件.

注意点,勾子和中间件函数,都提供一个类似 next()方法,来传递是否进行.

迭代器模式

在不需要了解内部的前提下,遍历一个聚合对象.
php 中已经有很多迭代器.其它可以简单理解为可以循环的对象.
通过实现 Iterator类.
主要实现5个方法:current返回当前数据,key返回当前索引,next下一个索引,rewind重置索引,valid验证数据
创建迭代器类.

然后就可以循环了.

1
2
3
4
$users= new Users();
foreach ($users as $key=>$user){
echo $key,'=>',$user,PHP_EOL;
}

代理模式

和我们理解的代理一样,网络代理,代购,代理人等一样.
分为: 主题–>代理人–>实际对象

和适配器模式一样,但代理人实现一样的方法.
场景: 大数据对象,按需要加载,延迟加载,读写分离,权责分离,控制等

大数据场景:
一个大的对象,先实例化一个轻量的对象,当需要某部分数据才实例化.

读写分离:orm类读数据库从从库实例获取数据,写数据时写入到master实例.

常用设计结构

MVC 结构

mvc是一种常用的 C/S或B/S软件工程的组织方式.
M: 模型(Model),数据和存储的封装
V: 视图(View),展现层的封装
C: 控制器(Controller),逻辑层的封装

本文作者:阿金

本文链接:http://www.hi-arkin.com/2019/04/14/PHP/design_parttern/

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

扫描二维码,分享此文章