搜索
首页后端开发php教程PHP常用设计模式单例, 工厂, 观察者, 责任链, 装饰, 策略,适配,桥接模式

// 多态, 在JAVA中是这样用的, 其实在PHP当中可以自然消除, 因为参数是动态的, 你传什么过来都可以, 不限制类型, 直接调用类的方法
abstract class Tiger {
    public abstract function climb();
}

class XTiger extends Tiger {
    public function  climb() {
        echo '摔下来';
    }
}

class MTiger extends Tiger {
    public function climb() {
        echo '爬到树顶';
    }
}

class Client {
    public static function call(Tiger $animal) {
        $animal->climb();
    }
}

Client::call(new XTiger());
Client::call(new MTiger());
// 面向对象里面有一个面向接口开发, 就是一个共同的规格, 你生产插座, 我生产插头

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

// 服务端开发(不知道将会被谁调用)
class dbmysql implements db{
    public function conn() {
        echo '连上了MySQL';
    }
}

class dbsqlite implements db{
    public function conn() {
        echo '连上了sqlite';
    }
}

// 客户端, 看不到dbmysql, dbsqlite的内部细节的, 只知道上两个类实现了db接口.

$db = new dbmysql();
$db->conn();          // 因为知道这个类实现了db接口, 所以知道有这个conn类

$sqlite = new dbsqlite();
$sqlite->conn();

// 我连我有哪些类我都不希望告诉客户端, 那么怎么进一步进行封装
// 发生连接的双方知道的越少越好, 你还知道我有两个类
// 简单工厂

// 面向对象里面有一个面向接口开发, 就是一个共同的规格, 你生产插座, 我生产插头

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

// 服务端开发(不知道将会被谁调用)
class dbmysql implements db{
    public function conn() {
        echo '连上了MySQL';
    }
}

class dbsqlite implements db{
    public function conn() {
        echo '连上了sqlite';
    }
}

// 简单工厂
class Factory {
    public static function createDB($type) {
        if($type == 'mysql') {
            return new dbmysql();
        } elseif ($type=='sqlite') {
            return new dbsqlite();
        } else {
            throw new Exception("Error db type", 1);
        }
    }
}

// 客户端现在不知道对方到底有哪些类名了
// 只知道对方开放了一个Factory::createDB方法
// 方法允许传递数据库名称

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

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

// 原本你知道服务器端的两个类名, 觉得你知道的太多, 再封装起来, 只给一个通道, 更好的适应变化
// 如果以后新增oracle类型, 怎么办?
// 我们PHP随手改了就行, 像JAVA打包一次很麻烦, 又要修改服务端内容
// 服务端要修改Factory的内容(在java, c++中, 改后还得再编译)
// 这个时候就想如何来改进呢
// 在OOD(面向对象设计)的法则中, 有重要的开闭原则--对于修改是封闭的, 对于扩展是开放的.
// 你可以新增一个源代码, 不要修改旧的代码

//现在用第二种办法, 叫工厂方法
// 共同接口
interface db {
    function conn();
}

interface Factory {
    function createDB();
}

// 服务端开发(不知道将会被谁调用)
class dbmysql implements db{
    public function conn() {
        echo '连上了MySQL';
    }
}

class dbsqlite implements db{
    public function conn() {
        echo '连上了sqlite';
    }
}

class mysqliFactory implements Factory{
    public function createDB() {
        return new dbmysql();
    }
}

class sqliteFactory implements Factory {
    public function createDB() {
        return new dbsqlite();
    }
}

// 服务器端添加oracle类
// 前面的代码不用改
// 我就新增一个数据库驱动类和一个工厂, 这样就避免了对源代码的修改
class dboracle implements db {
    public function conn() {
        echo '连接上了oracle';
    }
}

class oracleFactory implements Factory {
    public function createDB() {
        return new dboracle();
    }
}


// 客户端开始, 对方给了两个api, 一个db的api, 一个是创造数据库的api
$fact = new mysqliFactory();
$db = $fact->createDB();
$db->conn();

$fact = new sqliteFactory();
$db = $fact->createDB();
$db->conn();
// 单例
// 一般来说一个中小型网站一个db去连接数据库就行了
// 想想, 能不能只有一个db类, 一个upload类, 一个cookie类
// 类肯定只有一个, 如何保证类的实例也只有一个

// 2个对象是同一个的时候才全等

// 单例模式

class single {
    protected static $ins = null;

    // 控制权限, 封锁new操作, 把大门关上了, 需要留一个小窗户
    // 方法前加final, 则方法不能被覆盖, 类前加final, 则类不能被继承
    final protected function __construct() {}
    // 防clone
    final protected function __clone(){}
    
    //留一个接口来new对象
    public static function getIns() {
        if(self::$ins == null) {
            self::$ins = new self();
        }
        return self::$ins;
    }
}

$s1 = single::getIns();
$s2 = single::getIns();

if ($s1 === $s2) {
    echo '是一个对象';
} else {
    echo '不是一个对象';
}



    <title>colour_blue</title>
    <meta name="description" c>
    <meta name="keywords" c>
    <meta http-equiv="content-type" c>
    <style>
        div{
            margin:10px;
            width:500px;
            height:200px;
            border: 1px solid green;
        }
        #content {
        }
        #ad {
        }
    </style>
    <script>
        function t() {
            var sel = document.getElementsByTagName('select')[0];
//            alert(sel.value);
            if(sel.value == 'male') {
                document.getElementById('content').style.backgroundColor = 'gray';
                document.getElementById('ad').innerHTML = '汽车';
            } else if(sel.value=='female') {
                document.getElementById('content').style.backgroundColor = 'pink';
                document.getElementById('ad').innerHTML = '减肥';
            }
        }

        /**
         *
         * 观察上述代码, 如果新增了一个study区, 在切换时, 学习内容也要随之切换, 那么, 就需要改动T函数! 对于修改又开放了.
         * 思考---如果需要新监听footer区, 如何能不改原来的代码, 而只增加
         */
    </script>



    <h1 id="面向过程-不用任务来切换">面向过程, 不用任务来切换</h1>
    <select name="">// 比如登录, 在登录的时候出于安全考虑要记录上次登录时间
// 出于商业考虑, 给你推荐商品

class user implements SplSubject {
    public $lognum;
    public $hobby;

    protected $observers = null;

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

    public function login() {
        // 操作session
        $this->notify();
    }

    public function attach(SplObserver $observer) {
        $this->observers->attach($observer);
    }

    public function detach(SplObserver $observer) {
        $this->observers->detach($observer);
    }

    public function notify() {
        $this->observers->rewind();
        while($this->observers->valid()) {
            $observer = $this->observers->current();
            $observer->update($this);
            $this->observers->next();
        }
    }
}

class secrity implements SplObserver {
    public function update(SplSubject $subject) {
        if($subject->lognum lognum.'次安全登录';
        } else {
            echo '这是第'.$subject->lognum.'次登录,异常';
        }
    }
}

class ad implements SplObserver {
    public function update(SplSubject $subject) {
        if($subject->hobby == 'sports') {
            echo '台湾英锦赛';
        } else {
            echo '好好学习天天向上';
        }
    }
}

// 实施观察
$user = new user('sports');
$user->attach(new secrity());
$user->attach(new ad());

$user->login();</select>



    <title>colour_blue</title>
    <meta name="description" c>
    <meta name="keywords" c>
    <meta http-equiv="content-type" c>
    <style>
        div{
            margin:10px;
            width:500px;
            height:200px;
            border: 1px solid green;
        }
        #content {
        }
        #ad {
        }
    </style>



<h1 id="观察者模式">观察者模式</h1>
<select name="">
    <option value="male">男式风格</option>
    <option value="female">女式风格</option>
</select>
<input type="button" value="观察尾部">
<input type="button" value="别观察尾部了">
<div>我是内容</div>
<div>我是广告</div>
<div>学习</div>


<script>
    // 服务器端
    var sel = document.getElementsByTagName('select')[0];
    sel.observers = {};

    sel.attach = function(key, obj) {
        sel.observers[key] = obj;
    }
    sel.detach = function(key) {
        delete this.observers[key];
    }
    sel.onchange = sel.notify = function(){
        for(var key in this.observers) {
            this.observers[key].update(this);
        }
    }

    // 客户端
        var content = document.getElementById('content');
    content.update = function(observer) {
        if(observer.value == 'male'){
            this.style.backgroundColor = 'gray';
        } else if(observer.value=='female'){
            this.style.backgroundColor = 'pink';
        }
    }
    sel.attach('content', content);

    var ad = document.getElementById('ad');
    ad.update = function(observer) {
        if(observer.value == 'male'){
            this.innerHTML = '汽车';
        } else if(observer.value=='female'){
            this.innerHTML = '减肥';
        }
    }
    sel.attach('ad', ad);

    // 客户端和服务端实现了解耦
    // 后面有一个学习区, 如果状态改变了, 学习区也要变化

    var study = document.getElementById('study');
    study.update = function(observer) {
        if(observer.value == 'male'){
            this.innerHTML = '学习计算机';
        } else if(observer.value=='female'){
            this.innerHTML = '学习美容';
        }
    }
    sel.attach('study', study);


    //如果想不观察尾部了
    function t1() {
        sel.atach('study', study);
    }
    function t2() {
        sel.detach('study');
    }
</script>

.html




    <title>colour_blue</title>
    <meta name="description" c>
    <meta name="keywords" c>
    <meta http-equiv="content-type" c>
    <style>
        div{
            margin:10px;
            width:500px;
            height:200px;
            border: 1px solid green;
        }
        #content {
        }
        #ad {
        }
    </style>



<h2 id="责任链模式举报过程">责任链模式举报过程</h2>
.php power) { echo '版主删贴'; } else { $top = new $this->top; $top->process($lev); } } } class admin { protected $power = 2; protected $top = 'police'; public function process($lev) { if($lev power) { echo '管理员封账号'; } else { $top = new $this->top; $top->process($lev); } } } class police { protected $power; protected $top = null; public function process($lev) { echo '抓起来'; } } $lev = $_POST['jubao']+0; $judge = new board(); $judge->process($lev);
.html




    <title>colour_blue</title>
    <meta name="description" c>
    <meta name="keywords" c>
    <meta http-equiv="content-type" c>
    <style>
        div{
            margin:10px;
            width:500px;
            height:200px;
            border: 1px solid green;
        }
        #content {
        }
        #ad {
        }
    </style>



<h2 id="面向过程完成举报过程">面向过程完成举报过程</h2>
.php process(); } elseif($lev==2) { $judge = new admin(); $judge->process(); } elseif($lev==3) { $judge = new police(); $judge->process(); } // 处理的不够优雅, 现在还是面向过程的写法, 面向过程和面向对象混合了 // 如果以后在两个等级中间要添加一个等级应该怎么办
.html




    <title>colour_blue</title>
    <meta name="description" c>
    <meta name="keywords" c>
    <meta http-equiv="content-type" c>
    <style>
        div{
            margin:10px;
            width:500px;
            height:200px;
            border: 1px solid green;
        }
        #content {
        }
        #ad {
        }
    </style>



<h2 id="策略模式-和工厂方法的区别只是在逻辑上的区别-也叫聚合">策略模式 和工厂方法的区别只是在逻辑上的区别, 也叫聚合</h2>
.php calc = new $calc(); } public function calc($op1, $op2) { return $this->calc->calc($op1, $op2); } } $type = $_POST['op']; $cmath = new CMath($type); echo $cmath->calc($_POST['op1'], $_POST['op2']);
// 装饰器模式 decorator

class article {
    protected $content;

    public function __construct($content)
    {
        $this->content = $content;
    }

    public function decorator() {
        return $this->content;
    }
}

$art = new article("好好学习");
echo $art->decorator();

// 文章需要小编加摘要

class BianArticle extends article {
    public function summary() {
        return $this->content.'小编加了摘要';
    }
}

$art = new BianArticle('好好学习');
echo $art->summary();

// 又请了SEO人员, 要对文章进行description处理...
class SEOArticle extends BianArticle {
    public function seo() {
        //...
    }
}

// 又有了广告部
class ADArticle extends  SEOArticle {
    // 层次越来越深, 目的却只是给文章加各种内容
}
// 装饰器模式做文章修饰功能

class BaseArt {
    protected $content;
    protected $art;

    public function __construct($content) {
        $this->content = $content;
    }

    public function decorator() {
        return $this->content;
    }
}

//编辑文章摘要

class BianArt extends BaseArt {
    public function __construct(BaseArt $art) {
        $this->art = $art;
    }
    public function decorator() {
        return $this->content = $this->art->decorator() . '小编摘要';
    }
}

// SEO
class SEOArt Extends BaseArt {
    public function __construct(BaseArt $art) {
        $this->art = $art;
    }
    public function decorator() {
        return $this->content = $this->art->decorator() . 'SEO关键词';
    }
}

$b = new SEOArt(new BianArt(new BaseArt('天天向上')));
echo $b->decorator();

$b = new BianArt(new SEOArt(new BaseArt('天天向上')));
echo $b->decorator();

// 装饰的先后顺序可以随便换, 新增多少个装饰者都很方便

// 基类负责创建对象
// 子类负责装饰
// 适配器模式

// 服务器端代码
class weather {
    public static function show() {
        $today = [
            'tep' => 28,
            'wind' => 7,
            'sun' => 'sunny',
        ];
        return serialize($today);
    }
}

// 客户端调用

$weather = unserialize(weather::show());
echo '温度: ',$weather['tep'],'<br>';
echo '风力: ',$weather['wind'],'<br>';
echo 'sun: ',$weather['sun'],'<br>';

// 来了一批手机上的java客户端 , 不认识PHP的串化行后的字符串, 怎么办?
// 把服务器端代码改了? 旧的客户端会受影响

// 旧的类和函数都不让修改, 如何来进行扩展

// 增加一个适配器
class AdapterWeather extends weather {
    public static function show() {
        //拿到旧数据
        $today = parent::show();
        // 然后开始转换
        $today = unserialize($today);
        $today = json_encode($today);
        return $today;
    }
}

// ==== java, python再来调用就不怕了, 通过适配器调用

$weather = AdapterWeather::show();
$w = json_decode($weather);
echo '温度: ',$w->tep,'<br>';
echo '风力: ',$w->wind,'<br>';
echo 'sun: ',$w->sun,'<br>';

// 没有修改旧的方法, 这样就没有影响旧的客户端
// 没有修改旧的类, 说明没有违反开闭原则
// 桥接模式 bridge 尝试

// 论坛给用户发信息, 可以是站内短信, email, 手机

// 本来只需要发站内短信, 后来又需要发email
// 一个类只做一件事, 我们把他做成一个接口或者抽象类, 具体的发送由具体的子类实现

interface msg {
    public function send($to, $content);
}

class zn implements msg {

    public function send($to, $content)
    {
        echo '站内信给', $to, ' 内容是: ', $content;
    }
}

// 有一天要改造, 要发email
class email implements msg {

    public function send($to, $content)
    {
        echo 'email给', $to, ' 内容是: ', $content;
    }
}

// 后来要发短信
class sms implements msg {

    public function send($to, $content)
    {
        echo '短信给', $to, ' 内容是: ', $content;
    }
}

// 后来内容也丰富了, 分普通, 加急, 特急
// 子类就爆炸了, 像数据的三范式一样, 如果完全遵循解耦是不行了
/*
class zncommon extends zn;
class znwarn extends zn;
class zndanger extends zn;

class emailcomm extends email;
class emailwarn extends email;
class emaildanger extends email;
....
*/

/**
 * 思考:
 * 信的发送方式是一个变化因素
 * 信的紧急程度是一个变化因素,
 * 为了不修改父类, 只好考虑2个因素的组织, 不停产生新类....
 */
abstract class info {
    protected $send = null;

    public function __construct($send) {
        $this->send = $send;
    }

    abstract public function msg($content);
    public function send($to, $content){
        $content = $this->msg($content);
        $this->send->send($to, $content);
    }
}

class zn {
    public function send($to, $content) {
        echo '站内给: ', $to, '内容是: ', $content;
    }
}

class email {
    public function send($to, $content) {
        echo 'email给: ', $to, '内容是: ', $content;
    }
}

class sms {
    public function send($to, $content) {
        echo 'sms给: ', $to, '内容是: ', $content;
    }
}

class commoninfo extends info{
    public function msg($content) {
        return '普通'. $content;
    }
}

class warninfo  extends info{
    public function msg($content) {
        return '紧急'.$content;
    }
}

class dangerinfo  extends info{
    public function msg($content) {
        return '紧急'.$content;
    }
}

// 用站内发普通信息
$commoninfo = new commoninfo(new zn());
$commoninfo->send('小明', '吃饭了');

//给小刚发紧急手机短信消息
$dangerinfo = new dangerinfo(new sms());
$dangerinfo->send('小刚', '失火了');

// 本来需要3x3个类
// 现在只需要3+3个类了
// 现在耦合了, view中不写逻辑, 数据库三范式, 老有相同的字段, 为了速度, 之前的设计模式都是在解耦, 现在反而增加了耦合, 是为了防止子类爆炸

以上就介绍了PHP常用设计模式单例, 工厂, 观察者, 责任链, 装饰, 策略,适配,桥接模式,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
使用数据库存储会话的优点是什么?使用数据库存储会话的优点是什么?Apr 24, 2025 am 12:16 AM

使用数据库存储会话的主要优势包括持久性、可扩展性和安全性。1.持久性:即使服务器重启,会话数据也能保持不变。2.可扩展性:适用于分布式系统,确保会话数据在多服务器间同步。3.安全性:数据库提供加密存储,保护敏感信息。

您如何在PHP中实现自定义会话处理?您如何在PHP中实现自定义会话处理?Apr 24, 2025 am 12:16 AM

在PHP中实现自定义会话处理可以通过实现SessionHandlerInterface接口来完成。具体步骤包括:1)创建实现SessionHandlerInterface的类,如CustomSessionHandler;2)重写接口中的方法(如open,close,read,write,destroy,gc)来定义会话数据的生命周期和存储方式;3)在PHP脚本中注册自定义会话处理器并启动会话。这样可以将数据存储在MySQL、Redis等介质中,提升性能、安全性和可扩展性。

什么是会话ID?什么是会话ID?Apr 24, 2025 am 12:13 AM

SessionID是网络应用程序中用来跟踪用户会话状态的机制。1.它是一个随机生成的字符串,用于在用户与服务器之间的多次交互中保持用户的身份信息。2.服务器生成并通过cookie或URL参数发送给客户端,帮助在用户的多次请求中识别和关联这些请求。3.生成通常使用随机算法保证唯一性和不可预测性。4.在实际开发中,可以使用内存数据库如Redis来存储session数据,提升性能和安全性。

您如何在无状态环境(例如API)中处理会议?您如何在无状态环境(例如API)中处理会议?Apr 24, 2025 am 12:12 AM

在无状态环境如API中管理会话可以通过使用JWT或cookies来实现。1.JWT适合无状态和可扩展性,但大数据时体积大。2.Cookies更传统且易实现,但需谨慎配置以确保安全性。

您如何防止与会议有关的跨站点脚本(XSS)攻击?您如何防止与会议有关的跨站点脚本(XSS)攻击?Apr 23, 2025 am 12:16 AM

要保护应用免受与会话相关的XSS攻击,需采取以下措施:1.设置HttpOnly和Secure标志保护会话cookie。2.对所有用户输入进行输出编码。3.实施内容安全策略(CSP)限制脚本来源。通过这些策略,可以有效防护会话相关的XSS攻击,确保用户数据安全。

您如何优化PHP会话性能?您如何优化PHP会话性能?Apr 23, 2025 am 12:13 AM

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显着提升应用在高并发环境下的效率。

什么是session.gc_maxlifetime配置设置?什么是session.gc_maxlifetime配置设置?Apr 23, 2025 am 12:10 AM

thesession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceIsiseededeedeedeedeedeedeedto to to avoidperformance andununununununexpectedLogOgouts.3)

您如何在PHP中配置会话名?您如何在PHP中配置会话名?Apr 23, 2025 am 12:08 AM

在PHP中,可以使用session_name()函数配置会话名称。具体步骤如下:1.使用session_name()函数设置会话名称,例如session_name("my_session")。2.在设置会话名称后,调用session_start()启动会话。配置会话名称可以避免多应用间的会话数据冲突,并增强安全性,但需注意会话名称的唯一性、安全性、长度和设置时机。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)