前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP八大设计模式

PHP八大设计模式

作者头像
如梦初醒_-
发布2022-10-24 10:06:51
7331
发布2022-10-24 10:06:51
举报
文章被收录于专栏:最后一声蝉鸣

单例模式

$_instance必须声明为静态的私有变量

构造函数和析构函数必须声明为私有,防止外部程序new 类从而失去单例模式的意义

getInstance()方法必须设置为公有的,必须调用此方法 以返回实例的一个引用

::操作符只能访问静态变量和静态函数

new对象都会消耗内存

使用场景:最常用的地方是数据库连接。

使用单例模式生成一个对象后, 该对象可以被其它众多对象所使用。

私有的__clone()方法防止克隆对象

单例模式,使某个类的对象仅允许创建一个。构造函数private修饰,

申明一个static getInstance方法,在该方法里创建该对象的实例。如果该实例已经存在,则不创建。比如只需要创建一个数据库连接。

示例

代码语言:php
复制
<?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');
    }



}

调用

代码语言:php
复制
$jwtAuth = JwtAuth::getInstance();
$token = $jwtAuth->setUid($id)->encode()->getToken();

工厂模式

工厂模式,工厂方法或者类生成对象,而不是在代码中直接new。

使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数。

示例

代码语言:php
复制
// 最基本的工厂模式  

class Myname{  
    public function OutPutMyName(){  
        return 'hello world~';  
    }  
}

class NameFactory{  
    public static function Namefunc(){  
        return new Myname();  
    }  
}

$obj=NameFactory::Namefunc();  
echo $obj->OutPutMyName();  
?>  

抽象工厂模式

抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。

代码语言:php
复制
<?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()方法来确定一个给定的属性是否已经设置。 注册模式通过单一的全局的对象来获取对其它对象的引用

示例

代码语言:php
复制
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'));

注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。

示例

代码语言:php
复制
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等不同的缓存函数,统一成一致。

首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数,统一成一致的方法。

示例

代码语言:php
复制
// 接口 IDatabase
 <?php
 namespace IMooc;
 interface IDatabase
 {
     function connect($host, $user, $passwd, $dbname);
     function query($sql);
     function close();
 }
代码语言:php
复制
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);
    }
}
代码语言:php
复制
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);
    }
}

示例二

代码语言:php
复制
/**
 * 目标角色
 */
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的判断,硬编码的方式。如果有一天增加了一种用户,就需要改写代码。使用策略模式,如果新增加一种用户类型,只需要增加一种策略就可以。其他所有的地方只需要使用不同的策略就可以。

首先声明策略的接口文件,约定了策略的包含的行为。然后,定义各个具体的策略实现类。

示例

代码语言:php
复制
UserStrategy.php
<?php
/*
 * 声明策略文件的接口,约定策略包含的行为。
 */
interface UserStrategy
{
    function showAd();
    function showCategory();
}
代码语言:php
复制
FemaleUser.php
<?php
require_once 'Loader.php';
class FemaleUser implements UserStrategy
{
    function showAd(){
        echo "2016冬季女装";
    }
    function showCategory(){
        echo "女装";
    }
}
代码语言:php
复制
MaleUser.php
<?php
require_once 'Loader.php';
class MaleUser implements UserStrategy
{
    function showAd(){
        echo "IPhone6s";
    }
    function showCategory(){
        echo "电子产品";
    }
}
代码语言:php
复制
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)角色:存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致

主题接口

代码语言:php
复制
interface Subject{
    public function register(Observer $observer);
    public function notify();
}

观察者接口

代码语言:php
复制
interface Observer{
    public function watch();
}

Subject就是被观察者,Observer就是观众,也就是观察者

被观察者

代码语言:php
复制
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()方法。

观察者

代码语言:php
复制
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()方法。

调用

代码语言:php
复制
$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
复制
<?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
复制
<?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
复制
<?php
interface MessageTemplate
{
    public function message();
}

假设有很多模板实现了上面的短信模板接口

代码语言:php
复制
// 下面这个是其中一个优惠券发送的模板实现
class CouponMessageTemplate implements MessageTemplate
{
    public function message()
    {
        return '优惠券信息:我们是全国第一的牛X产品哦,送您十张优惠券!';
    }
}

我们来准备好装饰上面那个过时的短信模板

代码语言:php
复制
abstract class DecoratorMessageTemplate implements MessageTemplate
{
    public $template;
    public function __construct($template)
    {
        $this->template = $template;
    }
}

过滤新广告法中不允许出现的词汇

代码语言:php
复制
class AdFilterDecoratorMessage extends DecoratorMessageTemplate
{
    public function message()
    {
        return str_replace('全国第一', '全国第二', $this->template->message());
    }
}

使用我们的大数据部门同事自动生成的新词库来过滤敏感词汇,这块过滤不是强制要过滤的内容,可选择使用

代码语言:php
复制
class SensitiveFilterDecoratorMessage extends DecoratorMessageTemplate
{
    public $bigDataFilterWords = ['牛X'];
    public $bigDataReplaceWords = ['好用'];
    public function message()
    {
        return str_replace($this->bigDataFilterWords, $this->bigDataReplaceWords, $this->template->message());
    }
}

客户端,发送接口,需要使用模板来进行短信发送

代码语言:php
复制
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;
        }

    }
}
代码语言:php
复制
$template = new CouponMessageTemplate();
$message = new Message();

// 老系统,用不着过滤,只有内部用户才看得到
$message->send($template);

// 新系统,面向全网发布的,需要过滤一下内容哦
$message->msgType = 'new';
$template = new AdFilterDecoratorMessage($template);
$template = new SensitiveFilterDecoratorMessage($template);

// 过滤完了,发送吧
$message->send($template);

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单例模式
  • 工厂模式
  • 注册模式
  • 适配器模式(interface)
  • 策略模式
  • 观察者模式(Observer)
  • 原型模式
  • 装饰器模式
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档