wxvirus wxvirus
首页
  • Go文章

    • Go语言学习
  • Rust

    • Rust学习
  • Java

    • 《Java》
  • Python文章

    • Python
  • PHP文章

    • PHP设计模式
  • 学习笔记

    • 《Git》
  • HTML
  • CSS
  • JS
  • 技术文档
  • GitHub技巧
  • 刷题
  • 博客搭建
  • 算法学习
  • 架构设计
  • 设计模式
  • 学习
  • 面试
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

无解的lifecycle

let today = new Beginning()
首页
  • Go文章

    • Go语言学习
  • Rust

    • Rust学习
  • Java

    • 《Java》
  • Python文章

    • Python
  • PHP文章

    • PHP设计模式
  • 学习笔记

    • 《Git》
  • HTML
  • CSS
  • JS
  • 技术文档
  • GitHub技巧
  • 刷题
  • 博客搭建
  • 算法学习
  • 架构设计
  • 设计模式
  • 学习
  • 面试
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • C&C++

  • PHP

    • PHP基础或常见方法

      • PHP设计模式
        • 单例模式
        • 观察者模式
        • 简单工厂模式
        • 职责链模式(chain of responsibility)
      • 无限极分类树
      • PHP获取客户端操作系统信息
      • PHP分片上传大文件
    • Laravel

    • ThinkPHP

    • PHP多进程编程

    • swoole

  • Python

  • Go

  • microservice

  • rust

  • Java

  • 学习笔记

  • 后端
  • PHP
  • PHP基础或常见方法
wxvirus
2021-10-21

PHP设计模式

# PHP设计模式

在软件开发过程中,经常出现的典型场景的典型解决方案,称为设计模式是为了程序高内聚、低耦合更深入理解面向对象有利于开发出扩展性强的程序

# 单例模式

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

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

  1. 创建一个普通类

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

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

    class Singleton {
        private function __construct(){}
    
        public static function getInstance()
        {
            return new self();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  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;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  6. 可以私有化__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(){}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  7. 优化后的就是使用!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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

运行结果

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

# 观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖与它的对象都得到通知并被自动更新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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

# 简单工厂模式

这里拿连接多种数据库来当作案例数据库类一般会有一个连接的接口 ,这是每个数据库连接的共同的接口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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

此时我们还是知道服务端有哪些数据库类,对于面向接口编程来说,这里我们要开始认为,客户端现在不知道服务端有什么数据库类!的情况,只知道对方开放了一个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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

此时,如果新增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();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 职责链模式(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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
编辑 (opens new window)
#设计模式
上次更新: 2021/10/21, 20:43:49
makefile修炼
无限极分类树

← makefile修炼 无限极分类树→

最近更新
01
centos7安装redis6文档记录
02-14
02
portainer的安装
02-11
03
gin自定义验证器和翻译器
02-11
更多文章>
Theme by Vdoing | Copyright © 2021-2023 wxvirus
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式