单例模式
$_instance必须声明为静态的私有变量
构造函数和析构函数必须声明为私有,防止外部程序new 类从而失去单例模式的意义
getInstance()方法必须设置为公有的,必须调用此方法 以返回实例的一个引用
::操作符只能访问静态变量和静态函数
new对象都会消耗内存
使用场景:最常用的地方是数据库连接。
使用单例模式生成一个对象后, 该对象可以被其它众多对象所使用。
私有的__clone()方法防止克隆对象
单例模式,使某个类的对象仅允许创建一个。构造函数private修饰,
申明一个static getInstance方法,在该方法里创建该对象的实例。如果该实例已经存在,则不创建。比如只需要创建一个数据库连接。
示例
<?php
namespace App\Http\Controllers\Admin;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\ValidationData;
class JwtAuth
{
private $token;
private $user_id;
private $secrect="#awadakvcabzijvjvsuvr";
private $aud="www.test.com";
private $iss="www.test.com";
private $decodeToken;
/**
* JTW句柄
* @var
*/
private static $instance;
public static function getInstance(){
if (is_null(self::$instance)){
self::$instance =new self();
}
return self::$instance;
}
private function __construct()
{
}
private function __clone(){
}
public function getToken(){
return (string)$this->token;
}
public function setToken($token){
$this->token = $token;
return $this;
}
public function setUid($user_id){
$this->user_id =$user_id;
return $this;
}
public function encode(){
$time= time();
$this->token = (new Builder())
->setHeader('alg','HS256')
->setIssuer($this->iss)
->setAudience($this->aud)
->setIssuedAt($time)
->setExpiration($time+3600)
->set('user_id',$this->user_id)
->sign(new Sha256(), $this->secrect)
->getToken();
return $this;
}
/**
* @return \Lcobucci\JWT\Token
*/
public function decode(){
if (!$this->decodeToken){
$this->decodeToken = (new Parser())->parse((string)$this->token);
$this->user_id = $this->decodeToken->getClaim('user_id');
}
return $this->decodeToken;
}
/**
* @return bool
*/
public function validate(){
$data = new ValidationData();
$data->setIssuer($this->iss);
$data->setAudience($this->aud);
return $this->decode()->validate($data);
}
/**
* @return bool
*/
public function verify(){
$result = $this->decode()->verify(new Sha256() , $this->secrect);
return $result;
}
public function getUid(){
return (new Parser())->parse((string)$this->token)->getClaim('user_id');
}
}
调用
$jwtAuth = JwtAuth::getInstance();
$token = $jwtAuth->setUid($id)->encode()->getToken();
工厂模式
工厂模式,工厂方法或者类生成对象,而不是在代码中直接new。
使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数。
示例
// 最基本的工厂模式
class Myname{
public function OutPutMyName(){
return 'hello world~';
}
}
class NameFactory{
public static function Namefunc(){
return new Myname();
}
}
$obj=NameFactory::Namefunc();
echo $obj->OutPutMyName();
?>
抽象工厂模式
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
<?php
//定义一个抽象类
abstract class operation
{
protected $_numA = 0;
protected $_numB = 0;
protected $_result = 0;
public function __construct($a, $b)
{
$this->_numA = $a;
$this->_numB = $b;
}
//抽象方法所有子类必须实现该方法
protected abstract function getResult();
}
//加法运算
class operationAdd extends operation
{
public function getResult()
{
$this->_result = $this->_numA + $this->_numB;
return $this->_result;
}
}
//减法运算
class operationSub extends operation
{
public function getResult()
{
$this->_result = $this->_numA - $this->_numB;
return $this->_result;
}
}
//乘法运算
class operationMul extends operation
{
public function getResult()
{
$this->_result = $this->_numA * $this->_numB;
return $this->_result;
}
}
//除法运算
class operationDiv extends operation
{
public function getResult()
{
$this->_result = $this->_numA / $this->_numB;
return $this->_result;
}
}
//定义工厂类
class operationFactory
{
//创建保存示例的静态成员变量
private static $obj;
//创建实例的静态方法
public static function CreateOperation($type, $a, $b)
{
switch ($type) {
case '+':
self::$obj = new operationAdd($a, $b);
break;
case '-':
self::$obj = new operationSub($a, $b);
break;
case '*':
self::$obj = new operationMul($a, $b);
break;
case '/':
self::$obj = new operationDiv($a, $b);
break;
}
//最后返回这个实例
return self::$obj;
}
}
//最后我们使用工厂模式
$obj = operationFactory::CreateOperation('+', 100, 20);
echo $obj->getResult();
如果还是理解不了这两者的区别,可以看看这个帖子: https://www.zhihu.com/question/20367734
注册模式
单例模式保证了一个类中只有一个实例被全局访问,当你有一组全局对象被全局访问时可能就需要用到注册者模式 (registry),它提供了在程序中有条理的存放并管理对象 (object)一种解决方案。一个“注册模式”应该提供get() 和 set()方法来存储和取得对象(用一些属性key)而且也应该提供一个isValid()方法来确定一个给定的属性是否已经设置。 注册模式通过单一的全局的对象来获取对其它对象的引用
示例
class Registry {
protected static $store = array();
private static $instance;
public static function instance() {
if(!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
public function isValid($key) {
return array_key_exists($key,Registry::$store);
}
public function get($key) {
if (array_key_exists($key,Registry::$store))
return Registry::$store[$key];
}
public function set($key, $obj) {
Registry::$store[$key] = $obj;
}
}
//数据库链接类
class ConnectDB {
private $host;
private $username;
private $password;
private $conn;
public function __construct($host, $username, $password){
$this->host = $host;
$this->username = $username;
$this->password = $password;
}
public function getConnect() {
return mysql_connect($this->host,$this->username,$this->password);
}
}
$reg = Registry::instance();
$reg->set('db1', new ConnectDB('localhost', 'root', '123456'));
$reg->set('db2', new ConnectDB('127.0.0.2', 'test', 'test'));
print_r($reg->get('db1'));
print_r($reg->get('db2'));
注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。
示例
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]);
}
}
适配器模式(interface)
将各种截然不同的函数接口封装成统一的API。
PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API。类似的场景还有cache适配器,可以将memcache,redis,file,apc等不同的缓存函数,统一成一致。
首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数,统一成一致的方法。
示例
// 接口 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);
}
}
PDO
<?php
namespace IMooc\Database;
use IMooc\IDatabase;
class PDO implements IDatabase
{
protected $conn;
function connect($host, $user, $passwd, $dbname)
{
$conn = new \PDO("mysql:host=$host;dbname=$dbname", $user, $passwd);
$this->conn = $conn;
}
function query($sql)
{
return $this->conn->query($sql);
}
function close()
{
unset($this->conn);
}
}
示例二
/**
* 目标角色
*/
interface Target {
/**
* 源类也有的方法1
*/
public function sampleMethod1();
/**
* 源类没有的方法2
*/
public function sampleMethod2();
}
/**
* 源角色
*/
class Adaptee {
/**
* 源类含有的方法
*/
public function sampleMethod1() {
echo 'Adaptee sampleMethod1 <br />';
}
}
/**
* 类适配器角色
*/
class Adapter extends Adaptee implements Target {
/**
* 源类中没有sampleMethod2方法,在此补充
*/
public function sampleMethod2() {
echo 'Adapter sampleMethod2 <br />';
}
}
class Client {
/**
* Main program.
*/
public static function main() {
$adapter = new Adapter();
$adapter->sampleMethod1();
$adapter->sampleMethod2();
}
}
策略模式
策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。
eg:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告。在传统的代码中,都是在系统中加入各种if else的判断,硬编码的方式。如果有一天增加了一种用户,就需要改写代码。使用策略模式,如果新增加一种用户类型,只需要增加一种策略就可以。其他所有的地方只需要使用不同的策略就可以。
首先声明策略的接口文件,约定了策略的包含的行为。然后,定义各个具体的策略实现类。
示例
UserStrategy.php
<?php
/*
* 声明策略文件的接口,约定策略包含的行为。
*/
interface UserStrategy
{
function showAd();
function showCategory();
}
FemaleUser.php
<?php
require_once 'Loader.php';
class FemaleUser implements UserStrategy
{
function showAd(){
echo "2016冬季女装";
}
function showCategory(){
echo "女装";
}
}
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();
php策略模式和适配器模式的区别
PHP中策略模式和适配器模式的区别是:策略模式是通过定义算法,并将算法封装起来,使算法独立于客户而独立变化;适配器模式是使用已经存在的类,将其适配成自己需要的形式。
观察者模式(Observer)
(Observer),首先要有一个被观察的角色,但ta是【唯一的】。虽然”表演者”只有一个但是”观众”有很多,既一群”人”围观一个”人”。既然有无数个观察者,那么我们需要知道都有哪一些”人”。所以我们需要一个“容器”来记录这些”人”,一个类似于数组一样来储存所有观察者的容器。
一个”演员”(被观察者),一群”观众”(观察者),一台”摄影机”(记录容器)
1.抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。 抽象主题提供了增加和删除观察者对象的接口。
2.抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
3.具体主题(ConcreteSubject)角色:存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
4.具体观察者(ConcretedObserver)角色:存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致
主题接口
interface Subject{
public function register(Observer $observer);
public function notify();
}
观察者接口
interface Observer{
public function watch();
}
Subject就是被观察者,Observer就是观众,也就是观察者
被观察者
class Action implements Subject{
public $_observers=array();
public function register(Observer $observer){
$this->_observers[]=$observer;
}
public function notify(){
foreach ($this->_observers as $observer) {
$observer->watch();
}
}
}
Action实现了被观察者接口,他现在就是被观察者,再定义一个$_observers数组,他就是记录观众的容器了。
首先实现register方法,用它传入一个观察者,然后塞到数组里,再实现notify()方法,它会遍历容器数组,执行每个观察者的watch()方法。
观察者
class Cat implements Observer{
public function watch(){
echo "Cat watches TV<hr/>";
}
}
class Dog implements Observer{
public function watch(){
echo "Dog watches TV<hr/>";
}
}
class People implements Observer{
public function watch(){
echo "People watches TV<hr/>";
}
}
这里定义了三个观察者,全都实现了Observer接口,前面的Subject会循环调用每个观察者的watch()方法,所以我们需要实现每个观察者的watch()方法。
调用
$action=new Action();
$action->register(new Cat());
$action->register(new People());
$action->register(new Dog());
$action->notify();
首先new被观察者对象,执行它的register()方法,把每个观察者都放入容器数组,最后执行notify()方法,通知所有观察者执行自己的方法。
原型模式
为了避免创建对象的成本,使用标准方法 new foo() 创建一个原型并克隆它。
场景
大数据量 ( 例如:通过 ORM 模型一次性往数据库插入 1,000,000 条数据 ) 。
示例
<?php
abstract class BookPrototype
{
/*
* @var string
*/
protected $title;
/*
* @var string
*/
protected $category;
abstract public function __clone();
public function getTitle(): string
{
return $this->title;
}// getTitle() end
public function setTitle($title)
{
$this->title = $title;
}// setTitle() end
}// BookPrototype{} end
class BarBookPrototype extends BookPrototype
{
/*
* @var string
*/
protected $category = 'Bar';
public function __clone()
{
}
}// BarBookPrototype{} end
class FooBookPrototype extends BookPrototype
{
/*
* @var string
*/
protected $category = 'Foo';
public function __clone()
{
}
}// FooBookPrototype{} end
$fooPrototype = new FooBookPrototype();
$barPrototype = new BarBookPrototype();
for ($i = 0; $i < 10; $i++) {
$book = clone $fooPrototype;
$book->setTitle('Foo Book No ' . $i);
echo $book->getTitle() . PHP_EOL;
}
for ($i = 0; $i < 5; $i++) {
$book = clone $barPrototype;
$book->setTitle('Bar Book No ' . $i);
echo $book->getTitle() . PHP_EOL;
}
结果
<?php
Foo Book No 0
Foo Book No 1
Foo Book No 2
Foo Book No 3
Foo Book No 4
Foo Book No 5
Foo Book No 6
Foo Book No 7
Foo Book No 8
Foo Book No 9
Bar Book No 0
Bar Book No 1
Bar Book No 2
Bar Book No 3
Bar Book No 4
PHP 对象复制 : 对象复制可以通过 clone 关键字来完成
装饰器模式
动态地给一个对象添加一些额外的职责,就增加功能来说,Decorator模式相比生成子类更为灵活
例子
短信模板接口
<?php
interface MessageTemplate
{
public function message();
}
假设有很多模板实现了上面的短信模板接口
// 下面这个是其中一个优惠券发送的模板实现
class CouponMessageTemplate implements MessageTemplate
{
public function message()
{
return '优惠券信息:我们是全国第一的牛X产品哦,送您十张优惠券!';
}
}
我们来准备好装饰上面那个过时的短信模板
abstract class DecoratorMessageTemplate implements MessageTemplate
{
public $template;
public function __construct($template)
{
$this->template = $template;
}
}
过滤新广告法中不允许出现的词汇
class AdFilterDecoratorMessage extends DecoratorMessageTemplate
{
public function message()
{
return str_replace('全国第一', '全国第二', $this->template->message());
}
}
使用我们的大数据部门同事自动生成的新词库来过滤敏感词汇,这块过滤不是强制要过滤的内容,可选择使用
class SensitiveFilterDecoratorMessage extends DecoratorMessageTemplate
{
public $bigDataFilterWords = ['牛X'];
public $bigDataReplaceWords = ['好用'];
public function message()
{
return str_replace($this->bigDataFilterWords, $this->bigDataReplaceWords, $this->template->message());
}
}
客户端,发送接口,需要使用模板来进行短信发送
class Message
{
public $msgType = 'old';
public function send(MessageTemplate $mt)
{
// 发送出去咯
if ($this->msgType == 'old') {
echo '面向内网用户发送' . $mt->message() . PHP_EOL;
} else if ($this->msgType == 'new') {
echo '面向全网用户发送' . $mt->message() . PHP_EOL;
}
}
}
$template = new CouponMessageTemplate();
$message = new Message();
// 老系统,用不着过滤,只有内部用户才看得到
$message->send($template);
// 新系统,面向全网发布的,需要过滤一下内容哦
$message->msgType = 'new';
$template = new AdFilterDecoratorMessage($template);
$template = new SensitiveFilterDecoratorMessage($template);
// 过滤完了,发送吧
$message->send($template);