PHP设计模式

PHP设计模式

1. 学习目标

  • 掌握设计模式

2. 设计模式介绍

2.1 设计模式是什么?

  • 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,是一种在编程过程中发现的习惯性用法
  • 使用设计模式可以更快开发出健壮的程序。能够有效的提高代码的可重用性、让代码更容易被他人理解、保证代码的可靠性

3. 常用的设计模式

  • 单例模式
    • 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式
  • 工厂模式
    • 客户类和工厂类分开。客户需要某种产品,只需向工厂请求即可。客户无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改
  • 观察者模式
    • 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象在发生变化时,会通知所有观察者,并使他们自动更新
  • 命令链模式
    • 命令链中每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止
  • 策略模式
    • 针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换

3.1 单例模式

  • 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式
  • 单例模式的要点有三个:
    1. 某个类只能有一个实例/只生成一个对象;
    2. 它必须自行创建这个实例;
    3. 它必须自行向整个系统提供这个实例。
  • 根据上述要点,我们可以总结出一些特点:
    • 构造函数私有
    • 当构造函数为私有时,不能直接实例化类,只能在类内部实例化,这是要防止对象在类的外部被实例化
      • 那怎么在类的内部实例化对象——静态方法
<?php
header("Content-type:text/html;charset=utf-8");
class Db
{
    //私有是防止在外部调用
    //静态数据是为了让静态的单例入口存储唯一的全局实例/对象
    private static $_instance = null;
    private function __construct()
    {
        //这里可以做数据库连接操作
        var_dump('数据库连接');
        var_dump('被创建了');
    }
    //静态方法,单例统一入口
    public static function getInstance(){
        //自己实例化自己
        if(self::$_instance === null){
            //self 代表本类
            self::$_instance = new self;   
            //改变了$_instance,让他成为了对象,由于是静态属性,所以属于整个类本身,是所有对象所共有的数据
            var_dump('第一次被调用');
        }else{
            var_dump('已创建');
        }
        return self::$_instance;
        //返回本类对象
        // var_dump($instance);
    }
    //容易被遗漏,阻止克隆对象->阻止在外部克隆对象
    private function __clone(){}
    public function select()
    {
        var_dump('查询数据库');
    }
    public function insert()
    {
        var_dump('插入数据库');
    }
}
$db = Db::getInstance();
$db = Db::getInstance();
$db = Db::getInstance();
$db->select();
$db->insert();
?>
注:要加上 私有克隆函数 __clone,防止外部克隆对象,
因为克隆对象是值传递,那就变成2个对象了,和单例模式的概念不符。

3.2 工厂模式

  • 客户类和工厂类分开。客户需要某种产品,只需向工厂请求即可。客户无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改
  • 工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
  • 减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
  • 通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了,全部交由工厂类创建。
  • 假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(), new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
  • 工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来, 创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
<?php
header("Content-type:text/html;charset=utf-8");
//数据库的抽象类
abstract class Db
{
    abstract public function connect();
    abstract public function query();
}    
//工厂的生产流水线
class Mysql extends Db
{
    public function connect()
    {
        echo 'Mysql';
    }
    public function query()
    {
        echo '';
    }
}    
class Oracle extends Db
{
    public function connect()
    {
        echo 'Oracle';
    }
    public function query()
    {
        echo '';
    }
}  
class MsServer extends Db
{
    public function connect()
    {
        echo 'MsServer';
    }
    public function query()
    {
        echo '';
    }
} 

class MongoDB extends Db
{
    public function connect()
    {
        echo 'MongoDB';
    }
    public function query()
    {
        echo '';
    }
} 

/**
 * 工厂类,负责生成合适的数据库类 生产对象
 */ 
class DbFactory
{
    public static function createFactory($dbName){
        $db = null;
        switch ($dbName) {
            case 'mysql':
                $db = new Mysql;
                break;
            case 'oracle':
                $db = new Oracle;
                break;
            case 'oracle':
                $db = new Oracle;
                break;
            case 'msserver':
                $db = new MsServer;
                break;
            case 'mongodb':
                $db = new MongoDb;
                break;
        }
        //返回连接好数据库了的数据库对象
        $db->connect();
        return $db;
    }
}

$mysql = DbFactory::createFactory('mysql');
$oracle = DbFactory::createFactory('oracle');

3.3 观察者模式

  • 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象在发生变化时,会通知所有观察者,并使他们自动更新
  • 场景:一个事件发生后,要执行一连串更新操作。传统的编程方法,就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码。
  • 观察者模式实现了低耦合,非侵入式的通知与更新机制。
  • 例如:京东商城有个功能叫做降价通知,就是你登录后点击进入商品详情页,里面有个叫做降价通知,我们点击降价通知后,京东就会帮我们做一个登记,当商品真正降价的时候,调用短信接口,发短信通知我们。下面使用观察者模式模拟一下场景:
    • 本模式中有两类类,一种是被观察类,一种是观察类
    • 在被观察类中注册观察类,当被观察类变化时,通知所有已注册的观察类
    • 被观察类 商品
    • 观察类 要降价商品的人
/**
 * 观察者接口-相当于人,要观察商品有没有降价
 */ 
interface People
{
  //商品降价 发消息给用户
  //发送短信模板
  function sendMsg( $product );
}   
/**
 * 被观察者接口-相当于商品,当他降价的时候立刻就要发消息给用户,告诉用户商品降价了
 */
interface Product
{
  //添加要通知的人
  function addPeople( $people );
}   
/**
 * 用户列表(被观察的) //原本的程序
 */
class UserList implements Product
{
  private $_userList = array();    
  public function send( $name )
  {
    foreach( $this->_userList as $val ){
        $val->sendMsg( $name );//数组中每个元素都是一个对象     
        //给要降价的每一个人发短信
    }
  } 
  //添加要通知的人的是
  public function addPeople( $userame )
  {
    $this->_userList[] = $userame;//把对象写入到数组
  }
}   
/**
 * iphone类,要观察用户列表
 */
class gg implements People 
{
  public function sendMsg( $product )
  {
    echo '亲爱的gg,'.$product.'降价啦'.'<br />';
  }
}   
class zz implements People 
{
  public function sendMsg( $product )
  {
    echo '亲爱的zz,'.$product.'降价啦'.'<br />';
  }
}   
$ul = new UserList();
//点击降价提醒,把该用户加入要通知列表
$ul->addPeople(new gg());
$ul->addPeople(new zz());
//当商品降价时,调用以下方法,则通知通知列表里面的所有用户,商品降价了
$ul->send('iphone');   
//观察者模式更多体现了两个独立的类利用接口完成一件本应该很复杂的事情。不利用观察者接口的话,我们还需要不断循环创建实例,执行操作。
//而现在只需要创建实例【把要通知的人加入通知列表】就好,执行操作,只需要调用一次通知的方法就好了。

3.4 命令链模式

  • 命令链中每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。
  • 注册多个下属,当老板发生变化时,所有的下属就像接收到命令那样执行
  • 命令链模式跟观察者模式,虽然都是设计模式。但是两者很不一样。命令链模式是一系列像链式一样的操作。而观察者模式,是事件监听一样,在某个操作前,操作中,或操作后,触发一个其他的动作,来处理相应的逻辑。
  • 例如:淘宝APP有个功能叫做88会员,就是你登录后点击进入个人中心,里面有个88会员,我们点击88会员后,就可以看到88会员带来的各种权限,以及使用这些权限。下面使用命令链模式模拟一下场景,我们下单买一件商品88会员3500,普通用户4000,那么当我们下单的时候,就会检测是否88会员,是则3500可以购买,不是那只能4000购买了;
/*
*命令接口类
*所有命令都得实现它
 */
interface MyCommand{

    public function onCommand($name);
}

/*
 *注册命令类
 *用于注册和运行命令
 */
class Register{

    private $_commands = array();//存储命令
    public function addCommand($cmd){
        $this->_commands[] = $cmd;
    }

    public function runCommand($name){
        foreach($this->_commands as $key => $val){
            //只要有一个命令类能够执行本次请求命令则执行运行并退出
            //就像上述说的,都知道你是88会员了,那么使用3500购买即可,
            //不要再找普通会员可以多少钱购买
            if($val->onCommand($name)){
                return;
            }
        }
    }
}

class CommonCommand implements MyCommand{
    public function onCommand($name){
        if($name != '普通会员'){
            return false;
        }
        echo '我是普通会员,买这件商品需要4000<br />';
        return true;
    }
}

class Vip88Command implements MyCommand{
    public function onCommand($name){
        if($name != '88会员'){
            return false;
        }
        echo '我是88会员,买这件商品需要3500<br />';
        return true;
    }
}

//实例化注册类
$shell = new Register();
//注册普通会员和88会员命令
$shell->addCommand(new CommonCommand);
$shell->addCommand(new Vip88Command);
//假设我是普通会员,执行下列操作
$shell->runCommand('普通会员');
//假设我是88会员,执行下列操作
$shell->runCommand('88会员');

3.5 策略模式

  • 它体现面向对象重要思想,封装变化点!通过传入不同策略类,分离并封装变化点(这里的变化点就是――――算法)
  • 针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
  • 下面模拟一个超市打折场景,vip用户打8折,vip10周年纪念卡折上5折,超市清仓装修大甩卖全场5折;
//客户接口 
interface Customer
{
    function Count($total);//收钱
}    

//普通客户打折策略
class Common implements Customer
{
    public function Count($total)
    {
        return $total;//不打折
    }
}

//vip会员打折策略
class Vip implements Customer
{
    public function Count($total)
    {
        return 0.8 * $total;//打八折
    }
}

//十周年时候的vip打折策略
class Vip10 implements Customer
{
    public function Count($total)
    {
        $vip = new Vip;
        $v = $vip->Count($total);
        return 0.5 * $v; //原来vip打折基础上再打5折
    }
}

//超市清仓装修大甩卖全场5折
class Clear implements Customer
{
    public function Count($total)
    {
        return 0.5 * $total;
    }
}

//收银机
class Calculator
{
    public $total = 0;
    public function getTotal($customer)
    {
        return $customer->Count($this->total);
    }
}

//收银机
$cal = new Calculator;

//原始总价钱
$cal->total = 100;

//分别计算出不同客户应付
//echo $cal->getTotal(new Common);
echo $cal->getTotal(new Vip);
echo $cal->getTotal(new Vip10);
echo $cal->getTotal(new Clear);

4、课堂总结

  • 掌握设计模式

5、课后练习

  • 掌握设计模式
  • 对课堂知识做好属于自己的笔记
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值