道者编程


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

发大水


最新评论:
我要评论:

看不清楚


链接