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:工厂方法:
发大水