TypechoJoeTheme

404

PHP

PHP设计模式

PHP设计模式 - virus记载的笔记 - 出自燕十三的PHP设计模式视频

在软件开发过程中,经常出现的典型场景的典型解决方案,称为设计模式

是为了程序高内聚、低耦合

更深入理解面向对象

有利于开发出扩展性强的程序

单例模式

用于场景:经典例子是数据库连接(redis,mongodb,memcache等),通过单例模式来实现只做一次mysql_connect()来节约资源。

==两个对象是一个的时候,才全等==

  1. 创建一个普通类

    class Singleton {
        
    }
    
    $single = new Singleton();
  2. 封锁new操作,将构造函数私有化

    class Singleton {
        private function __construct(){}
    }
  3. 留个静态方法getInstance()new对象

    class Singleton {
        private function __construct(){}
        
        public static function getInstance()
        {
            return new self();
        }
    }
  4. getInstance先顶一个私有静态属性来保存实例,第一次保存之后,后面进行判断这个私有静态属性是否为空,否则就直接返回这个存储实例的私有静态属性,此时还有问题,额外定义一个子类继承这个类,将构造函数改为公有属性,则还是不行。

    class Singleton {
        private static $instance = null;
        
        private function __construct(){}
        
        public static function getInstance()
        {
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }
    }
  5. final防止继承时,被修改权限

==方法前加final,方法不能被覆盖,类前加final,则类不能被继承==

此时,还是有问题,使用clone函数时,又产生了一个对象

class Singleton {
    private static $instance = null;
    
    final private function __construct(){}
    
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}
  1. 可以私有化__clone()函数

    class Singleton {
        private static $instance = null;
        
        final private function __construct(){}
        
        public static function getInstance()
        {
            // 判断有没有实例化
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }
        
        final private function __clone(){}
    }
  2. 优化后的就是使用!self::$instance instanceof self来代替上述的判断和final,这里前者是用来判断保存的对象不是从本身或者其派生类产生的,这样就可以避免继承之后改变构造函数公有化代来的烦恼。也减少了代码量。
<?php

class Singleton
{
    private static $instance = null;

    // 私有 => 防止外部进行实例化 new Singleton
    private function __construct()
    {
        echo __METHOD__;
    }

    public static function getInstance()
    {
        // 判断保存的对象不是从本身或者其派生类产生的
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __clone()
    {
        // TODO: Implement __clone() method.
    }
}

$instance = Singleton::getInstance();
var_dump($instance);

运行结果

Singleton::__constructobject(Singleton)#1 (0) { }

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖与它的对象都得到通知并被自动更新

PHP中提供观察者observer与被观察者subject的接口

<?php
/**
 * Created By basic
 * Author: Virus
 * Date: 2020/5/23
 * Time: 21:33
 * 观察登录的次数
 */

class User implements SplSubject
{
    public $login_num;
    public $hobby;

    protected $observers = null;

    public function __construct($hobby)
    {
        $this->login_num = rand(1, 10);
        $this->hobby     = $hobby;
        $this->observers = new SplObjectStorage();
    }

    public function login()
    {
        // 操作session...

        // 发送通知
        $this->notify();
    }

    /**
     * Attach an SplObserver
     * @link https://php.net/manual/en/splsubject.attach.php
     * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to attach.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function attach(SplObserver $observer)
    {
        // TODO: Implement attach() method.
        $this->observers->attach($observer);
    }

    /**
     * Detach an observer
     * @link https://php.net/manual/en/splsubject.detach.php
     * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to detach.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function detach(SplObserver $observer)
    {
        // TODO: Implement detach() method.
        $this->observers->detach($observer);
    }

    /**
     * Notify an observer
     * @link https://php.net/manual/en/splsubject.notify.php
     * @return void
     * @since 5.1.0
     */
    public function notify()
    {
        // TODO: Implement notify() method.
        $this->observers->rewind();
        while ($this->observers->valid()) {
            $observer = $this->observers->current();
            $observer->update($this);
            $this->observers->next();
        }
    }
}

class Security implements SplObserver
{
    /**
     * Receive update from subject
     * @link https://php.net/manual/en/splobserver.update.php
     * @param SplSubject $subject <p>
     * The <b>SplSubject</b> notifying the observer of an update.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        if ($subject->login_num >= 3) {
            echo '这是第'.$subject->login_num.'次安全登录';
        } else {
            echo '这是第'.$subject->login_num.'次登录,异常';
        }
    }
}

class Ad implements SplObserver
{
    /**
     * Receive update from subject
     * @link https://php.net/manual/en/splobserver.update.php
     * @param SplSubject $subject <p>
     * The <b>SplSubject</b> notifying the observer of an update.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        if ($subject->hobby == 'sports') {
            echo '台球锦标赛预定';
        } else {
            echo '好好学习,天天向上';
        }
    }
}

$user = new User('study');
$user->attach(new Security());
$user->attach(new Ad());

$user->login();

简单工厂模式

这里拿连接多种数据库来当作案例

数据库类一般会有一个连接的接口 ,这是每个数据库连接的共同的接口Db

按照正常面向接口开发来说,我们不知道各种数据库类的具体内部细节,只知道,数据库类都实现了Db接口

<?php
/**
 * Created By basic
 * Author: Virus
 * Date: 2020/5/23
 * Time: 18:03
 */

// 共同接口
interface db
{
    function conn();
}

// 服务端开发(不知道将会被谁调用)
class DbMysql implements db
{
    function conn()
    {
        // TODO: Implement conn() method.
        echo '连接上了mysql';
    }
}

class DbSqlite implements db
{
    function conn()
    {
        // TODO: Implement conn() method.
        echo '连接上了sqlite';
    }
}

$db = new DbMysql();
$db->conn();    // 连接上了mysql

此时我们还是知道服务端有哪些数据库类,对于面向接口编程来说,这里我们要开始认为,客户端现在不知道服务端有什么数据库类!的情况,只知道对方开放了一个SimpleFactory::createDB()方法,并且方法允许传递数据库名称,由此衍生出简单工厂模式!

// ...上述代码还是必须的内容,这里省略

class SimpleFactory
{
    public static function createDB($type)
    {
        if ($type == 'mysql') {
            return new DbMysql();
        } else if ($type == 'sqlite') {
            return new DbSqlite();
        } else {
            return new Exception("Error db type", 1);
        }
    }
}

$mysql = SimpleFactory::createDB('mysql');
$mysql->conn();

$sqlite = SimpleFactory::createDB('sqlite');
$sqlite->conn();

此时,如果新增Oracle类型咋办,一方面,可以直接在上述if/else里再来一个else if,这样在PHP里修改个5、6条还行,但是对于静态类语言,他们是需要重新编译的,所以会很浪费时间。在OOP重,重要的开闭原则,对于修改是封闭的,对于扩展时开放的。这就是要我们将工厂也进行扩展,不要轻易去修改内容。

所以我们新增了一个工厂的接口提供了一个createDB的方法

interface Factory
{
    public function createDB();
}

class MySQLFactory implements Factory
{
    public function createDB()
    {
        // TODO: Implement createDB() method.
        return new DbMysql();
    }
}

class SqliteFactory implements Factory
{
    public function createDB()
    {
        // TODO: Implement createDB() method.
        return new DbSqlite();
    }
}

// 新增Oracle的方式就变了
class Oracle implements db
{
    function conn()
    {
        // TODO: Implement conn() method.
        echo '连接上了Oracle';
    }
}
class OracleFactory implements Factory
{
    public function createDB()
    {
        // TODO: Implement createDB() method.
        return new Oracle();
    }
}

职责链模式(chain of responsibility)

每个对象,存储着对自己上级的引用,如果自己处理不了,就交给上一级

拿举报信息提交给能处理的人的案例来说

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>责任链模式</title>
</head>
<body>
<h1>责任链模式</h1>
<form action="action.php" method="post">
    <select name="jubao" id="">
        <option value="1">粗口</option>
        <option value="2">黄赌毒</option>
        <option value="3">分裂国家</option>
    </select>
    <button type="submit">提交</button>
</form>
</body>
</html>

action.php

<?php
/**
 * Created By basic
 * Author: Virus
 * Date: 2020/5/23
 * Time: 22:13
 */

header('content-type: text/html;charset=utf-8');

// 版主类
class Board
{
    // 权限
    public $power = 1;
    // 上级
    protected $top = 'Admin';

    public function process($level)
    {
        // 如果当前的处理者权限不够,就引导去它的上级去处理
        if ($level <= $this->power) {
            echo '版主删帖子';
        } else {
            $top = new $this->top;
            $top->process($level);
        }
    }
}

class Admin
{
    protected $power = 2;
    protected $top = 'Police';
    public function process($level)
    {
        if ($level <= $this->power) {
            echo '管理员封锁账号';
        } else {
            $top = new $this->top;
            $top->process($level);
        }
    }
}

class Police
{
    protected $power;
    protected $top = null;
    public function process($level)
    {
        echo '警察抓起来';
    }
}

/*
 * 这里还是偏向面向过程的解决方式
if ($level == 1) {
    $processer = new Board();
    $processer->process();
} else if ($level == 2) {
    $processer = new Admin();
    $processer->process();
} else {
    $processer = new Police();
    $processer->process();
}
*/

// 责任链模式来处理举报问题
// 接收举报等级
$level = $_POST['jubao'] + 0;

$judge = new Board();
$judge->process($level);
赞 · 0
评论 (0)

人生倒计时

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月

标签云

暂无标签