PHP设计模式
自动加载
__autoload();
魔术方法,当加载不存在的类的时候会使用这个方法,每个文件中只能存在一次,而且抛出的Exception是不可catch的。
spl_autoload_register();
可以调用用户自己的ClassLoader, Composer就是使用这种方法实现类的自动加载
每个文件中可有多个spl_autoload_register();方法,调用更加灵活;
抛出的Exception可以catch;
而且可以使用 spl_autoload_unregister();进行自动加载关闭,节省内存空间.
PSR-0规范
PSR-0 (Autoloading Standard) 自动加载标准
PSR-1 (Basic Coding Standard) 基础编码标准
PSR-2 (Coding Style Guide) 编码风格向导
PSR-3 (Logger Interface) 日志接口
PSR-4 (Improved Autoloading) 自动加载的增强版,可以替换掉PSR-0了。
一个完全合格的namespace和class必须符合这样的结构:“\< Vendor Name>(< Namespace>)*< Class Name>”
每个namespace必须有一个顶层的namespace(”Vendor Name”提供者名字)
每个namespace可以有多个子namespace
当从文件系统中加载时,每个namespace的分隔符(/)要转换成 DIRECTORY_SEPARATOR(操作系统路径分隔符)
在类名中,每个下划线(_)符号要转换成DIRECTORY_SEPARATOR(操作系统路径分隔符)。在namespace中,下划线(_)符号是没有(特殊)意义的。
当从文件系统中载入时,合格的namespace和class一定是以 .php 结尾的
verdor name,namespaces,class名可以由大小写字母组合而成(大小写敏感的)
PSR-4 规范
废除了支持PHP5.3代码以前的规范,并且去掉的下划线的意义。
八个常用设计模式
单例模式 Eg。数据库连接
要求:
-
$_instance必须为静态私有变量
-
构造函数 析构函数 拷贝函数必须为私有 防止外界实例化对象
-
getInstance()方法必须公有,返回实例一个引用。
节省内存 避免频繁new
工厂模式:生产对象使用
工厂模式可以避免类改名字之后,其他生成对象的代码都要一一修改,使用工厂可以避免频繁修改。
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 31 32 33
|
Test1.php <?php class Test1{ static function test(){ echo __FILE__; } }
Factory.php <?php class Factory{ /* * 如果某个类在很多的文件中都new ClassName(),那么万一这个类的名字 * 发生变更或者参数发生变化,如果不使用工厂模式,就需要修改每一个PHP * 代码,使用了工厂模式之后,只需要修改工厂类或者方法就可以了。 */ static function createDatabase(){ $test = new Test1(); return $test; } }
Test.php <?php spl_autoload_register('autoload1');
$test = Factory::createDatabase(); $test->test(); function autoload1($class){ $dir = __DIR__; $requireFile = $dir."\\".$class.".php"; require $requireFile; }
|
注册模式:解决全局局部共享对象使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
<?php
class Register { protected static $objects; function set($alias,$object)//将对象注册到全局的树上 { self::$objects[$alias]=$object;//将对象放到树上 } static function get($name){ return self::$objects[$name];//获取某个注册到树上的对象 } function _unset($alias) { unset(self::$objects[$alias]);//移除某个注册到树上的对象。 } }
|
适配器模式,类似于下面的决策模式
将各种不同的操作封装成接口API 比如数据库操作有MySQL,MySQLi,PDO。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
接口 IDatabase <?php namespace IMooc; interface IDatabase { function connect($host, $user, $passwd, $dbname); function query($sql); function close(); }
MySQL <?php namespace IMooc\Database; use IMooc\IDatabase; class MySQL implements IDatabase { protected $conn; function connect($host, $user, $passwd, $dbname) { $conn = mysql_connect($host, $user, $passwd); mysql_select_db($dbname, $conn); $this->conn = $conn; }
function query($sql) { $res = mysql_query($sql, $this->conn); return $res; }
function close() { mysql_close($this->conn); } }
MySQLi <?php namespace IMooc\Database; use IMooc\IDatabase; class MySQLi implements IDatabase { protected $conn;
function connect($host, $user, $passwd, $dbname) { $conn = mysqli_connect($host, $user, $passwd, $dbname); $this->conn = $conn; }
function query($sql) { return mysqli_query($this->conn, $sql); }
function close() { mysqli_close($this->conn); } }
|
策略模式:IOC思想 DI实现
也就是控制反转 依赖注入
防止种类过多时候发生太多if导致代码维护艰难
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
UserStrategy.php <?php /* * 声明策略文件的接口,约定策略包含的行为。 */ interface UserStrategy { function showAd(); function showCategory(); }
MaleUser.php <?php require_once 'Loader.php'; class MaleUser implements UserStrategy { function showAd(){ echo "IPhone6s"; } function showCategory(){ echo "电子产品"; } }
Page.php//执行文件 <?php require_once 'Loader.php'; class Page { protected $strategy; function index(){ echo "AD"; $this->strategy->showAd(); echo "<br>"; echo "Category"; $this->strategy->showCategory(); echo "<br>"; } function setStrategy(UserStrategy $strategy){ $this->strategy=$strategy; } }
$page = new Page(); if(isset($_GET['male'])){ $strategy = new MaleUser(); }else { $strategy = new FemaleUser(); } $page->setStrategy($strategy); $page->index();
|
观察者模式
适用于一带多频繁更新的操作,比如业务逻辑中一个数据更改导致其他表的数据也要跟着更改,直接硬编码更改的操作的不太好的,而且后期的维护也很不利,使用观察者模式可以进行事件发布,然后进行foreach进行更新操作。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
EventGenerator.php <?php require_once 'Loader.php'; abstract class EventGenerator{ private $observers = array(); function addObserver(Observer $observer){ $this->observers[]=$observer; } function notify(){ foreach ($this->observers as $observer){ $observer->update(); } } }
Observer.php <?php require_once 'Loader.php'; interface Observer{ function update();//这里就是在事件发生后要执行的逻辑 }
//一个实现了EventGenerator抽象类的类,用于具体定义某个发生的事件 require 'Loader.php'; class Event extends EventGenerator{ function triger(){ echo "Event<br>"; } } class Observer1 implements Observer{ function update(){ echo "逻辑1<br>"; } } class Observer2 implements Observer{ function update(){ echo "逻辑2<br>"; } } $event = new Event(); $event->addObserver(new Observer1()); $event->addObserver(new Observer2()); $event->triger(); $event->notify();
|
原型模式
和工厂模式类似,适合创建大对象的时候,可以节约一下内存开销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
Index.php <?php require 'Loader.php'; $c = new Canvas(); $c->init(); / $canvas1 = new Canvas(); // $canvas1->init(); $canvas1 = clone $c;//通过克隆,可以省去init()方法,这个方法循环两百次 //去产生一个数组。当项目中需要产生很多的这样的对象时,就会new很多的对象,那样 //是非常消耗性能的。 $canvas1->rect(2, 2, 8, 8); $canvas1->draw(); echo "-----------------------------------------<br>"; // $canvas2 = new Canvas(); // $canvas2->init(); $canvas2 = clone $c; $canvas2->rect(1, 4, 8, 8); $canvas2->draw();
|
MySQL 性能索引
索引数据结构
- B+ Tree
key放在叶子节点内的节点,叶子节点存放value 这样可以保证放更多的key,减少树的高度,树的高度的减少导致磁盘IO减少,可以优化性能。叶子节点处有相互链接的指针。InnoDB采用B+Tree可以将随机IO转换为顺序IO来提升效率。
- 搜索树
父节点大于左孩子节点,右孩子节点大于父节点
- 平衡二叉树
二叉树,但是要求任意一个节点的左右孩子节点高度差不大于1
- Hash Struct
哈希结构仅能满足 Exist In 等查询,不能使用范围查询。无法进行排序查询。不支持部分索引
聚集索引和非聚集索引
聚集索引Primary
InnoDB的数据文件本身就是索引文件,B+Tree的叶子节点上的data就是数据本身,key为主键,这是聚簇索引
非聚簇索引,叶子节点上的data是主键(所以聚簇索引的key,不能过长)。
索引的优点
-
变随机IO为顺序IO
-
避免全表扫描
-
可以帮助服务器避免排序或者临时表
索引对中小型表会比较高效,大型表需要考虑一下分区。
索引的创建方法
1 2 3 4 5 6 7 8
|
create index `idx_img` on newuser(`img`);
alter table newuser add index `idx_extra_img` (`isDeleted`, `img`)
drop index `idx_img` on newuser;
// 强制走索引的方式 select * from newuser force index where xxxx;
|
索引的注意
-
不鼓励使用like 不可以全部模糊查询 可以 55kai% 这样查
-
不可以进行列上操作 比如count sum等都会全表扫描 性能很差
-
不适用Not in <> 操作。
-
尽可能使用分解关联查询 这样分解后 sql简单,利于MySQL缓存、减少锁竞争 更好的扩展和维护性。
HTTP 超文本传输协议
协议格式
三部分:
请求起始行、消息头、消息体
分块传送
当浏览器想服务器请求一个资源,这个资源是一个动态资源,服务器无法预知资源的大小,就应该采用分块传送
服务器先生成一个thunk 发送这个chunk 再生成 在发送 直到发送完成。
分块发送需要在请求头增加一个transfer-encoding:thunked
持久链接
HTTP早期版本因为链接不可复用,性能很差,TCP1.5的RTT(三次握手)和慢启动会拖慢速度。
1.1版本引入keep-alive的connection头,如果服务器和客户端都确定keep-alive的话,在一个TCP上面可以传送多个请求。
持久链接也不应该一直保持,每个人都会占用服务器资源,如果PV太高,服务器资源也会jinzhang
应该配置KeepAliveTimeout和KeepAliveRequests两个参数 限时限量
PipeLine管线化
也就是一个TCP上面可以产送多个HTTP请求,但是请求的顺序必须和返回的顺序一致
无状态性
指的是协议层无状态性 两次请求并没有任何关系。通过会话控制可以保证访问的状态。