大话PHP设计模式
最常见的设计模式 工厂模式 单例模式 注册模式 这是最基本的PHP设计模式
还会有 适配器模式 、策略模式、数据映射模式(ORM)、观察者模式、原型模式、装饰器模式
设计模式并不是单独出现的,有时设计项目会用到多种设计模式,相结合,完成项目。
设计模式简要素:
1、单一类只做一件事
2、使用扩展来增加功能,而不可修改
3、通过依赖注入,不要直接写在类中 解耦合
4、尽可能的使用配置化,而不是硬编码
5、面向接口编程,面向对象编程。
公共方法:
function.php 代码实现:
//自动加载类
function __autoload($class){
if(file_exists($class.".php")){
include_once $class.".php";
}
}
function P($str){
echo "<pre style='font-size:22px;color:blue'>";
print_r($str);
echo "</pre>";
}PSR-0 要求
1、要求命名空间与文件名保持一致
2、类名首字母必须大写
3、不能有执行文件
PHP面向对象高级特性
SPL库的使用(PHP标准库)
1、SplStack、SplQueue、SplHeap、SplFixedArray 数据结构类
栈 SplStack
//栈 后进先出 实现了 Iterator , ArrayAccess 相关接口
$stack=new SplStack();
$stack->push("abc");
$stack->push("def");
var_dump($stack->count());
$stack->add(1,"99999"); //可以动态向栈中添加数据,但是这个不太常用 1 是offset
var_dump($stack->count());
// var_dump($stack->pop());
// var_dump($stack->pop());
// var_dump($stack->pop());
//可以动态获取到栈中所有的数据
$stack->rewind();
while($stack->valid()){
echo $stack->current(),"\n";
$stack->next();
}
var_dump($stack->count());队列 SplQueue SplQueue 类通过使用一个双向链表来提供队列的主要功能。
//队列 后进先出 实现了 Iterator , ArrayAccess 相关接口
$queue=new SplQueue();
$queue->push("abc");
$queue->push("def");
var_dump($queue->count());
//$queue->add(1,"99999"); //可以动态向栈中添加数据,但是这个不太常用 1 是offset
var_dump($queue->count());
var_dump($queue->pop());
var_dump($queue->pop());
// var_dump($queue->pop());
//可以动态获取到栈中所有的数据
/*$queue->rewind();
while($queue->valid()){
echo $queue->current(),"\n";
$queue->next();
}*/
var_dump($queue->count());堆
将数据insert到堆中
堆分2种,最小堆和最大堆,其实最大堆的实现和最小堆基本一样。最小堆的根是最小值,而最大堆的根是最大值,最小堆在某些判断上符号改变下就是最大堆了
$heap=new SplMinHeap(); $heap->insert(3); $heap->insert(2); $heap->insert(8); $heap->insert(1); var_dump($heap->extract()); var_dump($heap->extract());
int(1)
int(2)
最大堆
$heap=new SplMaxHeap(); $heap->insert(3); $heap->insert(2); $heap->insert(8); $heap->insert(1); var_dump($heap->extract()); var_dump($heap->extract()); var_dump($heap->extract()); var_dump($heap->extract()); var_dump($heap->isEmpty()); ===================== int(8) int(3) int(2) int(1) bool(true) =====================
声明固定长度的数组
//不管有没有设置值,都会分配每一个的内存空间 // $array=new SplFixedArray(5); // $array[0]=1; // $array[1]=10; $data=[0=>"abc",9=>"ccc"]; $array=SplFixedArray::fromArray($data,true); var_dump($array); var_dump($array->getSize()); var_dump($array->count()); var_dump($array->toArray());
2、ArrayIterator、AppendIterator、Countable、ArrayObject
3、SPL提供的函数
工厂模式
工厂方法 或类生成对象 而不是在代码中使用new关键字来创建
接口文件Food.php:
abstract class Food{
abstract function cooking();
}实现接口 面条类:
class Noodle extends Food{
function cooking()
{
// TODO: Implement cooking() method.
return "做面条";
}
}实现接口 饺子类:
class Dumpling extends Food{
function cooking()
{
// TODO: Implement cooking() method.
return "包饺子";
}
}工厂类:
require_once("../function.php");
class Factory{
//做饭
public static function cook($type){
switch($type){
case 1:
return new Noodle();
break;
case 2:
return new Dumpling();
break;
default:
exit("传值错误");
break;
}
}
}调用实现:
require_once("../function.php");
$noodle=Factory::cook(1);
P($noodle);
P($noodle->cooking());
$dumpling=Factory::cook(2);
P($dumpling);
P($dumpling->cooking());单例模式
使某个类的对象仅允许创建一次
单例类 Mysql:
class Mysql{
public static $instance;
public static function getInstance(){
if(self::$instance instanceof self){
return self::$instance;
}else{
$_obj=new Mysql();
self::$instance=$_obj;
return $_obj;
}
}
//私有不能外部调用
private function __construct(){
}
public function p($str){
echo $str;
}
}调用类:
//测试单例模式
require_once("../function.php");
$first=Mysql::getInstance();
$first->p("123");
$second=Mysql::getInstance();
$second->p("456");
P($first==$second); //这是相等的 相同的一个类注册模式
全局共享和交换对象
抽象类或接口:
abstract class Food{
abstract function cooking();
}实现类 面条类 Noodle.php
class Noodle extends Food{
function cooking()
{
// TODO: Implement cooking() method.
return "做面条";
}
}实现类 饺子类 Dumpling.php
class Dumpling extends Food{
function cooking()
{
// TODO: Implement cooking() method.
return "包饺子";
}
}工厂new新对象 Factory.php
class Factory{
//生产一个noodle
public static function noodle(){
return new Noodle();
}
//生产一个饺子
public static function dumpling(){
return new Dumpling();
}
}初始化类 init.php
Register::set("noodle",Factory::noodle());
Register::set("dumpling",Factory::dumpling());使用类 index.php
include_once("../function.php");
//把noodle注册到Register上 初始化工作
include_once("init.php");
var_dump(Register::get("noodle"));
var_dump(Register::get("noodle"));
//这两个是同一个对象
//那么 noodle 这个对象直接获取就可以使用了
$noodle=Register::get("noodle");
P($noodle->cooking());
//与第二个不同
var_dump(new Noodle());适配器模式
将所有的接口修改成统一的一个接口类型,就是适配
犹如 手机充电器接口
策略模式
不管声明哪个类,都是统一的API 操作
接口文件 strtaegy.php
interface Strategy{
public function ad();
public function faviator();
}实现类 Man.php
class Man implements Strategy{
public function ad()
{
// TODO: Implement ad() method.
P("男士 广告");
}
public function faviator()
{
// TODO: Implement faviator() method.
P("电脑 手机 游戏");
}
}实现类 Female.php
class Female implements Strategy{
public function ad()
{
// TODO: Implement ad() method.
P("女士广告");
}
public function faviator()
{
// TODO: Implement faviator() method.
P("购物 裙子 内衣");
}
}调用 index.php
/**
*
* 使用说明:比如说 一个页面 根据不同的访问者 显示不同的爱好 那么怎么做?
*
*
* 如果按照默认的方法 我们通过 if else 来进行注册 执行不同的代码
*
* 那么如何进行改进呢,这就是策略模式 ,我们通过注册对象的形式 进行操作
*
*看代码:
*
*
* 不同的策略 实现同一个接口 API 如同 适配器模式
*/
require_once "../function.php";
class Page{
protected $strategy;
public function index(){
$this->strateg->ad();
$this->strateg->faviator();
}
public function set(Strategy $strategy){
$this->strateg=$strategy;
}
}
$page=new Page();
if(isset($_GET['t']) && $_GET['t']=="female"){
$page->set(new Female());
}else{
$page->set(new Man());
}
$page->index();数据映射模式
操作一个对象映射成对另外一个事物的操作。 比如说 操作 orm 对象 关系到 mysql数据库
ps:创建orm对象 有 增删改查 数据库连接 对一组对象的操作
观察者模式 又名:订阅模式
当一个对象状态发生变化是,依赖它的对象都会受到通知,并自动更新。
怎么算是依赖呢?
ps:一个时间发生后,要执行一连串的更新操作,传统的编程方式就是在事件代码后直接加入处理逻辑。当更新的逻辑增多后,就变得不好维护了。
简单举例说明: 同志们大家喝水 所有人都喝水
同志们大家喝水 就是一个事件处理
所有人喝水 就是一个一个的人 并 喝水
这就是简单的例子 ,观察者模式
观察者模式多用于实现订阅功能的场景,例如微博的订阅,当我们订阅了某个人的微博账号,当这个人发布了新的消息,就会通知我们
当目标对象有变化的时候,观察者 就更新
下面我们举例来说下:
有两位老师 李老师 和 张老师 是目标对象
两位学生 是观察者
当老师 说 起立 的时候 观察者 就起立
当老师说 跑步 的时候 ,观察者就跑步
那么代码如何实现呢?
ObServer 这是观察者 就是学生 同学
观察者API
//这是观察者 API
interface ObServer{
public function update($who,$message);
}观察者实例1
//首先实现了第一个API 观察者1
class ObServerCustom1 implements ObServer{
protected $name="同学1";
public function update($who,$message){
P($this->name.":{$who} ".$message);
}
public function __get($name){
return $this->$name;
}
}观察者实例2
class ObServerCustom2 implements ObServer{
protected $name="同学2";
public function update($who,$message)
{
// TODO: Implement update() method.
P($this->name.":{$who} ".$message);
}
public function __get($name){
return $this->$name;
}
}ObjectEvent 这是目标对象
目标对象API
//这是一个对象 API 所有的都要实现这个API 才可以作为目标对象
interface ObjectEvent{
public function setObServer(ObServer $obServer);
public function notify($message);
}目标 老师 TeacherEvent.php
//实现了所有的方法 可以作为一个目标对象使用
class TeacherEvent implements ObjectEvent{
protected $_observer;
protected $_name;
public function __construct($_name){
$this->_name=$_name;
}
public function setObServer(ObServer $obServer)
{
// TODO: Implement setObServer() method.
$this->_observer[]=$obServer;
}
public function notify($message)
{
// TODO: Implement notify() method.
if(sizeof($this->_observer)>0){
foreach($this->_observer as $_obj){
$_obj->update($this->_name,$message);
}
}
}
}实例index.php
require_once "../function.php";
//创建一个老师 目标对象
$teacher=new TeacherEvent("张老师");
//添加观察者 也就是学生
$stu1=new ObServerCustom1();
$stu2=new ObServerCustom2();
$teacher->setObServer($stu1);
$teacher->setObServer($stu2);
//老师说 起立 所有对象 所有人 收到指令后 更新一下
$teacher->notify("起立");
//创建一个老师 目标对象
$teacher2=new TeacherEvent("李老师");
$teacher2->setObServer($stu1);
$teacher2->notify("去跑步");
把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。
原型模式
适用于大对象的创建。clone 创建知识内存拷贝。
原型模式与工厂模式类似,都是用来创建对象。与工厂模式不同的是,原型模式是先创建好一个原型对象,通过clone 来创建其他对象,免去了创建重复的初始化工作。
那么 clone 创建对象快 还是 通过 单例模式 获取快? 还是先注册好 以后获取快?
class Student{
protected $name;
protected $age;
public function __construct($name,$age){
$this->name=$name;
$this->age=$age;
}
public function __set($name,$val){
$this->$name=$val;
}
public function __get($name){
return $this->$name;
}
}
//这是创建一个对象,那么创建原型对象,就是通过clone的方式来创建了
$stu1=new Student("job",14);
//创建原型对象
$stu2=clone $stu1;等于创建了一个新的对象,做任何操作与stu1 无关
装饰器模式
可以动态添加修改类的功能,一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程方式 就是继承,然后扩展功能。
那么以后再次需要 增加功能,怎么办? 也可以重新打开,再次编辑的。
那么如何在运行时,添加一个装饰器对象,即可实现最大的灵活性。
装饰器API Decorator.php
interface Decorator{
public function beforeDraw();
public function afterDraw();
}两个装饰器 一个文字变大 SizeDecorator.php
class SizeDecorator implements Decorator{
public function beforeDraw()
{
// TODO: Implement beforeDraw() method.
echo "<div style='font-size:30px;'>";
}
public function afterDraw()
{
// TODO: Implement afterDraw() method.
echo "</div>";
}
}ColorDecorator.php
class ColorDecorator implements Decorator{
public function beforeDraw()
{
// TODO: Implement beforeDraw() method.
echo "<div style='color:blue'>";
}
public function afterDraw()
{
// TODO: Implement afterDraw() method.
echo "</div>";
echo "<p>我是添加了颜色</p>";
echo "*********";
}
}实例 Photo.php
class Photo{
public $decorator=[];
//动态的把装饰器类注入进来
public function addDecorator(Decorator $decorator){
$this->decorator[]=$decorator;
}
//循环遍历
public function beforeDraw(){
if(sizeof($this->decorator)){
foreach($this->decorator as $_obj){
$_obj->beforeDraw();
}
}
}
public function afterDraw(){
//这里需要把 decorator 翻转一下,先进 后出 和上边的匹配成一对
if(sizeof($this->decorator)){
$decorator=array_reverse($this->decorator);
foreach($decorator as $_obj){
$_obj->afterDraw();
}
}
}
//这就叫做装饰器模式
public function draw(){
$this->beforeDraw();
for($i=1;$i<10;$i++){
for($j=1;$j<8;$j++){
echo "*";
}
echo "<br>";
}
$this->afterDraw();
}
}运行实例 index.php
include_once "../function.php"; $photo=new Photo(); //装饰器执行的顺序按照 添加的顺序执行的。 //增加装饰器 加大 $photo->addDecorator(new SizeDecorator()); //颜色 $photo->addDecorator(new ColorDecorator()); $photo->draw();
以上就是装饰器的编码。
代理模式
客户端与实体之间建立一个代理对象,客户端对实体的进行操作,全部委托给代理对象,隐藏实体具体细节。
这里最突出的代理模式是 Mysql主从数据库 读写的分离。
业务代码中 RPC来委派任务 proxy可以与业务代码分离 部署到另外服务器上
传统编程是:
//这是写操作
$mysql=mysql_connect($root,$username,$password);
mysql_select_db("a1");
mysql_query("set names utf8");
$sql="update user set username='123' where id=1;";
mysql_query($sql);
mysql_close();
//那么读操作
$mysql=mysql_connect($root,$username,$password);
mysql_select_db("b1");
mysql_query("set names utf8");
$sql="select * from user order by id desc limit 0,10;";
$result=mysql_query($sql);
$_objects=[]
while($_obj=mysql_fetch_object($res)!=false){
$_objects[]=$_obj;
}
mysql_close();