PHP常用设计模式
一:单列模式:
三个要点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
这个有什么好处?比如在数据库操作中,连接数据库的时候,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。举个例子说明。
<?php class Uni{ //必须私有 静态变量保存全局实例 static private $getInstance; private $str; private function __construct($str){ $this->str = $str; echo "我被实例化了"; } //防止克隆,如果有克隆报错,因为这里是private私有方法 private function __clone(){ } //静态方法,单例统一访问入口 static public function init($str){ //使用 instanceof 检查对象不是本类的实例,如果没有则实例化 if (!self::$getInstance instanceof self) { self::$getInstance = new self ($str); //实例化本类对象,执行构造函数 } return self::$getInstance; //返回实例化对象 } public function get(){ echo $this->str; } } Uni::init('123')->get(); //这里输出 123 Uni::init('456')->get(); //这里还是输出 123
这个例子是带参数的实例化,实例化的时候带了一个参数,可以看到第二次再运行的时候,返回的还是第一次对象。
上面那个克隆主要防止的操作是:
$uni2 = clone Uni::init('567'); $uni2->get();类似这种
单例模式简单封装个PDO:
<?php class Model { private static $getInstance = null; //必须私有 静态变量保存全局实例 public $pdo; private function __construct(){ //必须私有 $this->connect($this->parameter()); } //静态方法,单例统一访问入口 static public function init() { //使用 instanceof 检查对象不是本类的实例,如果没有则实例化 if (!self::$getInstance instanceof self) { self::$getInstance = new self (); //实例化本类对象 } return self::$getInstance; //返回实例化对象 } //防止克隆对象 private function __clone(){ } private function connect($o = null){ try{ $dsn = 'mysql:host='.$o['server'].';dbname='.$o['dbname']; $this->pdo = new pdo($dsn,$o['username'],$o['password'],array(PDO::ATTR_AUTOCOMMIT=>0)); //关闭自动提交 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //开启异常处理 $this->pdo->exec('set names utf8'); }catch(PDOException $e){ dir('数据库连接失败,错误信息:'. $e->getMessage()); } } private function parameter() { return [ 'server' => '127.0.0.1', 'username' => 'root', 'password' => '123', 'dbname' => 'sw', 'charset' => 'utf8', 'port'=>'3306' ]; } } Model::init()->pdo->query("select * from js_admin");//这里执行pdo的query方法
PHP单列模式不足:
1:通过代码可以看到每次都需要检查一下是否已经实例化obj,需要一定的开销。
2:PHP是解释性的,也就是每个页面执行完以后,所有资源自动释放(比如上述,打开两个页面运行,还是会实例化2次。)变量是跨页面级的,针对场景,一个页面多次请求的话,还是很有意义。
二:观察者模式
概念:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
什么意思?看一个例子:
//登录 class Eventlogin{ function trigger(){ echo "登录成功"; // 事件触发 echo "登录一次给10个积分"; echo "登录后给用户推送10篇新文章"; echo "登录后推荐广告"; echo "登录后没玩没了了"; } }
以上这个例子,如果用常规方法写,今天加这个,明天加那个,后天又去掉这个,耦合度高,不利于维护,影响核心代码。
这时候就可以用观察者模式,根据概念分析:
第1个概念:一对多的依赖关系:谁是一?谁是多?这里一就是登录,但是登录后还有其他的,比如上面的送积分,推文章,推广告,这些就是这里的多。它们都是依赖登录的。
第2个概念:一个对象改变,依赖都得到通知并更新,谁改变?肯定是用户登录,张三登录了,改变一下,李四又登录了,又改变了一下,那么给张三和李四送积分,推文章都要得到通知和操作。
所以这里的事件触发就是登录,登录后,其他和登录有关的都需要操作,你也可以按照上面的这样堆,当然也可以用观察者模式来干。
观察者模式:这里如果用观察者模式,登录和接下来的其他操作形成了一种依赖,登录以后通知其他的相关操作(送积分,推文章,推广告……)
观察者模式怎么写?
PHP内置的SPL函数可以实现这种模式:https://www.php.net/manual/zh/class.splsubject.php
我们按照这个自己实现一个:
1:定义一个抽象类管理观察者:大概有这么几个方法:添加,删除,通知,PHP内置的SplSubject就有这么几个方法。
实现代码:
<?php //定义一个抽象管理观察者接口 interface Subject{ public function attach ( Observer $observer ); //添加观察者 public function detach ( Observer $observer ); //删除观察者 public function notify (); //通知 } //观察者接口 interface Observer{ public function update(Subject $Subject); //操作更新,实现自己的业务代码 } // 抽象管理类 class User implements Subject{ public $observers=array(); //定义数组存放观察者 public $user_id; //保存用户ID //添加观察者对象 public function attach(Observer $observer){ $this->observers[] = $observer; //存放的是对象 } //删除观察者对象 public function detach(Observer $observer){ //查看观察者对象在数组中是否存在 $key = array_search($observer,$this->observers); if($key === false){ return false; //如果不存在不处理 } unset($this->_observers[$key]); //删除观察者数组对象 return true; } //通知观察者对象 public function notify(){ foreach ($this->observers as $value) { $value->update($this); //执行 } } } //定义三个观察者,实现登录,积分,推送文章 // 观察者Login class Login implements Observer{ public function update(Subject $Subject){ $Subject->user_id = 100; //登录ID号;假如登录成功获取了用户ID号 echo 'ID:'.$Subject->user_id.'用户登录成功 '; } } // 观察者jifen class Jifen implements Observer{ public function update(Subject $Subject){ echo 'ID:'.$Subject->user_id.'用户送10个积分 '; } } // 观察者Article class Article implements Observer{ public function update(Subject $Subject){ echo 'ID:'.$Subject->user_id.'用户推送10篇文章 '; } } $loginEvent = new User; //实例化抽象管理类 // 注册以下三个观察者 $loginEvent->attach(new Login); $loginEvent->attach(new Jifen,$user_id); $loginEvent->attach(new Article,$user_id); $loginEvent->notify(); //执行所有观察者
以上就是我们自己写的。
PHP内置的原理一样:以下程序来源于PHP官方,加了点备注:
<?php //splsubject接口:https://www.php.net/manual/zh/class.splsubject.php //splobserver接口:https://www.php.net/manual/zh/class.splobserver.php // 定义一个抽象管理观察者类 class Newspaper implements \SplSubject{ //继承该接口 private $name; private $observers; private $content; public function __construct($name) { $this->name = $name; $this->observers = new SplObjectStorage(); //实例化对象存储 } //添加 观察者observer public function attach(\SplObserver $observer) { $this->observers -> attach($observer); //上面构造函数已经实例化,然后添加对象 } //删除 观察者observer public function detach(\SplObserver $observer) { $this->observers -> detach($observer); //删除对象 } //当状态发生改变时 触发 通知 notify() public function breakOutNews($content) { $this->content = $content; $this->notify(); } public function getContent() { return $this->content." ({$this->name})"; } //通知观察员(或部分观察员)事件产生了 public function notify() { foreach ($this->observers as $value) { $value->update($this); } } } /** * Observer,观察者 */ class Reader implements SplObserver{ private $name; public function __construct($name) { $this->name = $name; } //执行操作 public function update(\SplSubject $subject) { //写业务逻辑 echo $this->name.' 正在阅读 '.$subject->getContent()." "; } } $newspaper = new Newspaper('人民日报'); //创建一个实现了被观察者的接口类 $lxl = new Reader('李小龙'); $llj = new Reader('李连杰'); $zzd = new Reader('甄子丹'); //添加 三个观察员对象,相同的添加会覆盖 $newspaper->attach($lxl); $newspaper->attach($llj); $newspaper->attach($zzd); //删除 reader //$newspaper->detach($zzd); //报社发送信息,然后上面三个人都得到信息 $newspaper->breakOutNews('今天的人民日报');
三:工厂模式
1:简单工厂(静态化工厂):负责实现创建所有具体产品类的实例。工厂类可以被外界直接调用,创建所需的产品对象,且只有一个静态方法创建所有产品对象!
<?php
/**
简单工厂,静态工厂
**/
//业务接口
interface WorkInterface
{
public function connect();
}
//工厂类
final class Factory {
static public function build(string $type){ // 只有一个静态方法创建所有产品对象
switch ($type) {
case 'mysql':
return new Mysql();
break;
case 'sql':
return new SqlServer();
break;
default:
echo $type.':不存在';
return false;
}
}
}
// mysql产品类
class Mysql implements WorkInterface{
public function connect(){
echo 'Connect Mysql';
}
}
// sql产品类
class SqlServer implements WorkInterface{
public function connect(){
echo 'Connect Sql Server';
}
}
//连接mysql
$mysql = Factory::build('mysql'); //外部直接调用创建mysql(产品)对象
$mysql->connect();
//连接sql
$sql = Factory::build('sql'); //外部直接调用创建sql(产品)对象
$sql->connect();
从代码中可以看到,我们传入mysql,那么就是mysql的实例,传入sql就是sqlserver的实例,很方便切换多个数据库实例,在缓存中也可以实现多个,诸如memcache,redis等等。适合同一类型的操作。工厂模式实现了对象创建和使用的分离,创建对象由工厂负责,但是不够灵活,如果产品过多,工厂比较复杂臃肿。
2:工厂方法:
发大水