PHP设计模式实例学习笔记

 更新时间:2016年11月25日 14:59  点击:1387
本文章要讲到的PHP设计模式中包括有值对象模式、策略模式、观察者模式、命令模式四种模式,下面我一一给各位同学详细介绍希望文章对大家会带来帮助。

一、值对象模式

再说设计模式-值对象模式之前,你要了解值传递和引用传递:

PHP设计模式实例学习笔记
1.值对象模式概念:
如果你把同一个对象资源赋值给两个不同的变量,然后改变其中的一个变量,另一个变量仍然不受影响。这就是使用值对象模式的目的。
看下面例子:

 代码如下 复制代码
<?php
 
class BadDollar {
  protected $amount;
 
  public function __construct($amount=0) {
    $this->amount = (float)$amount;
  }
 
  public function getAmount() {
    return $this->amount;
  }
 
  public function add($dollar) {
    $this->amount += $dollar->getAmount();
  }
}
 
class Work {
  protected $salary;
 
  public function __construct() {
    $this->salary = new BadDollar(200);
  }
 
  public function payDay() {
    return $this->salary;
  }
 
}
 
class Person {
  public $wallet;
}
 
 
$job = new Work;
$p1 = new Person;
$p2 = new Person;
$p1->wallet = $job->payDay();
print_r($p1->wallet->getAmount()); //200
 
$p2->wallet = $job->payDay();
print_r($p2->wallet->getAmount()); //200
 
$p1->wallet->add($job->payDay());
print_r($p1->wallet->getAmount()); //400
 
//this is bad — actually 400
print_r($p2->wallet->getAmount()); //400
 
//this is really bad — actually 400
print_r($job->payDay()->getAmount()); //400


看上面例子,可以知道明显的错误是$p1和$p2使用的是同一个BadDollar对象,首先,类Work和类Person的实例已经创建。那么,假设每一个雇员最初有一个空的电子钱包,雇员的电子钱包Person:wallet是通过Work::payDay()函数返回的对象资源变量赋值的,所以被设定为一个BadDollar类的对象实例。实际上,$job::salary,、$p1::wallet和$p2::wallet指向的是同一个对象的实例。
使用值对象模式,重新设计Dollar对象如下

 代码如下 复制代码

class Dollar {
  protected $amount;
 
  public function __construct($amount=0) {
    $this->amount = (float)$amount;
  }
 
  public function getAmount() {
    return $this->amount;
  }
 
  public function add($dollar) {
    return new Dollar($this->amount + $dollar->getAmount());
  }
}

可以看到,主要的变化在于Dollar:add()函数中,它是创建并返回一个新的Dollar实例,所以,尽管你指定当前对象给多个变量,但是每一个变量的变化都不会影响其它的变量实例。
值对象模式设计注意:

1.保护值对象的属性,禁止被直接访问。

2.在构造函数中就对属性进行赋值。

3.去掉任何一个会改变属性值的方式函数(setter),否则属性值很容易被改变

二、策略模式

1.策略模式概念
策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,此模式让算法的变化独立于使用算法的客户。从而让程序结构更灵活,具有更好的扩展性和维护性
2.策略模式结构图

PHP设计模式实例学习笔记

3.策略模式角色说明
    抽象策略(Strategy)角色:定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现。Context使用这个接口来调用其ConcreteStrategy定义的算法。
    具体策略(ConcreteStrategy)角色:以Strategy接口实现某具体算法
    环境(Context)角色:持有一个Strategy类的引用,用一个ConcreteStrategy对象来配置
4.策略模式实例 
比如说购物车系统,在给商品计算总价的时候,普通会员肯定是商品单价乘以数量,但是对中级会员提供8者折扣,对高级会员提供7折折扣,这种场景就可以使用策略模式实现:

 代码如下 复制代码

<?php

//抽象策略角色《为接口或者抽象类,给具体策略类继承》
interface Strategy
{
    public function computePrice($price);
}
 
//具体策略角色-普通会员策略类
class GenernalMember implements Strategy
{
    public function computePrice($price)
    {
        return $price;
    }
}
 
//具体策略角色-中级会员策略类
class MiddleMember implements Strategy
{
    public function computePrice($price)
    {
        return $price * 0.8;
    }
}
 
//具体策略角色-高级会员策略类
class HignMember implements Strategy
{
    public function computePrice($price)
    {
        return $price * 0.7;
    }
}
 
//环境角色实现类
class Price
{
    //具体策略对象
    private $strategyInstance;
   
    //构造函数
    public function __construct($instance)
    {
        $this->strategyInstance = $instance;
    }
   
    public function compute($price)
    {
        return $this->strategyInstance->computePrice($price);
    }
}
 
//客户端使用
$p = new Price(new HignMember());
 
$totalPrice = $p->compute(100);
 
echo $totalPrice; //70


三、命令模式

1.命令模式概念:
将来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化。用于“行为请求者”与“行为实现者”解耦,可实现二者之间的松耦合,以便适应变化。 
2.参与者:

  • Command(命令):在一个方法调用之上定义一个抽象;
  • ConcreteCommand(具体的命令):一个操作的实现;
  • Invoker(调用者):引用Command实例作为它可用的操作。

 

 代码如下 复制代码
<?php
//命令
interface Validator 

    /** 
     * The method could have any parameters. 
     * @param mixed 
     * @return boolean 
     */
    public function isValid($value); 

 
//具体命令
class MoreThanZeroValidator implements Validator 

    public function isValid($value) 
    { 
        return $value > 0; 
    } 

 
//具体命令
class EvenValidator implements Validator 

    public function isValid($value) 
    { 
        return $value % 2 == 0; 
    } 

 
//调用者
class ArrayProcessor 

    protected $_rule; 
 
    public function __construct (Validator $rule) 
    { 
        $this->_rule = $rule; 
    } 
 
    public function process(array $numbers) 
    { 
        foreach ($numbers as $n) { 
            if ($this->_rule->IsValid($n)) { 
                echo $n, " "; 
            } 
        } 
    } 

 
//客户端
$processor = new ArrayProcessor(new EvenValidator()); 
$processor->process(array(1, 20, 18, 5, 0, 31, 42));

在这个模式中,Invoker(调用者)知道传递给它的Command,无需依赖于真实的ConcreteCommand(具体的命令)实现,解决了通过配置进行方法调用相关的问题,如UI控件按钮和菜单等引用一个Command,它们的行为是通过通用的ConcreteCommand实例呈现的。

四、观察者模式

首先了解观察者模式的概念:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦。看下面例子你就明白了!
1.使用观察者模式实现消息推送

 代码如下 复制代码

<?php

//观察者
interface IObserver
{
 public function notify();
}
 
//定义可以被观察的对象接口
interface IObservable
{
 public function addObserver($observer);
}
 
//实现IObservable接口
class MessageSystem Implements IObservable
{
 private $_observers = array();
 
 public function addObserver($observer)
 {
  $this->_observers = $observer;
 }
 
 public function doNotify()
 {
  foreach($this->_observers as $o)
  {
   $o->notify();
  }
 }
}
 
//实现IObserver接口
class User Implements IObserver
{
 public function __construct($username)
 {
  echo "我是新用户{$username}<br/>";
 }
 //通知观察者方法
 public function notify()
 {
  echo '欢迎新用户';
 }
}
 
//使用
$u = new MessageSystem();
 
$u->addObserver(new User('小明'));
//$u->addObserver(new User('小红'));
//$u->addObserver(new User('小黑'));
 
$u->doNotify();


2.摘自PHPCHINA的一个不错例子:


/**
* 定义观察接口
*/
interface Subject
{
    public function Attach($Observer); //添加观察者
    public function Detach($Observer); //踢出观察者
    public function Notify(); //满足条件时通知观察者
    public function SubjectState($Subject); //观察条件
}
 
/**
* 观察类的具体实现
*/
class Boss Implements Subject
{
    public $_action;
   
    private $_Observer;
   
    public function Attach($Observer)
    {
        $this->_Observer[] = $Observer;
    }
   
    public function Detach($Observer)
    {
        $ObserverKey = array_search($Observer, $this->_Observer);
       
        if($ObserverKey !== false)
        {
            unset($this->_Observer[$ObserverKey]);
        }
    }
   
    public function Notify()
    {
        foreach($this->_Observer as $value )
        {
            $value->Update();
        }
    }
   
    public function SubjectState($Subject)
    {
        $this->_action = $Subject;
    }
}
 
/**
* 抽象观察者
*
*/
abstract class Observer
{
    protected $_UserName;
   
    protected $_Sub;
   
    public function __construct($Name,$Sub)
    {
        $this->_UserName = $Name;
        $this->_Sub = $Sub;
    }
   
    public abstract function Update(); //接收通过方法
}
 
/**
* 观察者
*/
class StockObserver extends Observer
{
    public function __construct($name,$sub)
    {
        parent::__construct($name,$sub);
    }
   
    public function Update()
    {
        echo $this->_Sub->_action.$this->_UserName." 你赶快跑...";
    }
}
 
$huhansan = new Boss(); //被观察者
 
$gongshil = new StockObserver("三毛",$huhansan); //初始化观察者
 
$huhansan->Attach($gongshil); //添加一个观察者
$huhansan->Attach($gongshil); //添加一个相同的观察者
$huhansan->Detach($gongshil); //踢出基中一个观察者
 
$huhansan->SubjectState("警察来了"); //达到满足的条件
 
$huhansan->Notify(); //通过所有有效的观察者

 

如果你使用的确php代码我们可以使用$_SERVER[HTTP_X_REWRITE_URL]与ORIG_PATH_INFO来判断你所使用的环境是iis还是apache了

if(isset($_SERVER['HTTP_X_REWRITE_URL'])) // IIS

else if(isset($_SERVER['ORIG_PATH_INFO']))  // IIS 5.0 CGI

//以上两行代码,在yii里的,原样复过来的。

//只作参考

 

在php中条件语句主要用到if else与if elseif及swicth case之类的语句了,这两语句是用到最多的,下面我来给大家普通一下。

一、if…else语句

if...else 语句

在条件成立时执行一块代码,条件不成立时执行另一块代码

语法:

1、简单条件

if(条件){
        ……
    }
    else{
        ……
    }  

 代码如下 复制代码

<?php
if (date("D") == "Sat") echo " http://www.111cn.net提醒你周末了,狂欢去";
?>

范例:本例的执行部分有三行,不可省略大括号。

 代码如下 复制代码

<?php
if (file_exists("/usr/local/lib/php3.ini")) {
  echo "以下是 PHP3 的配置文件<p><pre>n";
  readfile("/usr/local/lib/php3.ini");
  echo "</pre>n";
}
?>


2、复杂条件

elseif 语句

与 if...else 配合使用,在若干条件之一成立时执行一个代码块

if(条件){
        ……
    }
    elseif(条件){
        ……
    }
    else{
        ……
}

范例:上面的例子来修改成更完整的处理。其中的 else 由于只有一行执行的指令,因此不用加上大括号。

 代码如下 复制代码
<?php
$f="/usr/local/lib/php3.ini";
if (file_exists($f)) {
  echo "以下是 PHP3 的配置文件<p><pre>n";
  readfile($f);
  echo "</pre>n";
} else echo "很抱歉,找不到 $f";
?>

--------------------------------------------------------------------------------

第三种就是递归的 if..else 循环,通常用在多种决策判断时。它将数个 if..else 拿来合并运用处理。

直接看下面的例子

 代码如下 复制代码

<?php
if ($a > $b) {
  echo "a 比 b 大";
} elseif ($a == $b) {
  echo  "a 等于 b";
} else {
  echo "a 比 b 小";
}
?>

上例只用二层的 if..else 循环,用来比较 a 和 b 两个变量。实际要使用这种递归 if..else 循环时,请小心使用,因为太多层的循环容易使设计的逻辑出问题,或者少打了大括号等,都会造成程序出现莫名其妙的问题

二、Switch语句

1 语法:

switch(表达式){
    case 值1:
                    语句
                    break;
    case 值2:
                    语句
                    break;
    default:
                没有匹配的值时执行的语句
}


工作原理:

1.对表达式(通常是变量)进行一次计算
2.把表达式的值与结构中 case 的值进行比较
3.如果存在匹配,则执行与 case 关联的代码
4.代码执行后,break 语句阻止代码跳入下一个 case 中继续执行
5.如果没有 case 为真,则使用 default 语句

 代码如下 复制代码

<?php
switch ($d=date("D"))
{
case "Mon";
  echo "周一";
  break;
case "Tue";
  echo "周二";
  break;
case "Wed";
  echo "周三";
  break;
case "Thu";
  echo "周四";
  break;
case "Fir";
  echo "周五";
  break;
case "Sat";
  echo "周六";
  break;
case "Sun";
  echo "周日";
  break;
}
?>

另外一个实例,利用switch实现一个页面多用途,首先建立test.php页面:

 代码如下 复制代码

<?php
echo "<a href='solution.php?action=add'>增加www.111cn.net</a><br><br>";
echo "<a href='solution.php?action=del'>删除</a><br><br>";
echo "<a href='solution.php?action=search'>查找</a><br><br>";
echo "<a href='solution.php?action=update'>更新</a>";
?>

  接着,让我们看看solution.php是如何处理这四种操作的。

 代码如下 复制代码

<?php
$action=$_GET["action"];

switch ($action)
{
 case "add":
 echo "现在可以实现增加功能!";
 break;
 case "del":
 echo "现在可以实现删除功能!";
 break;
 case "search":
 echo "现在可以实现查询功能!";
 break;
 case "update":
 echo "现在可以实现更新功能!";
 break;
}
?>

很简单,我们首先接收到action的值,运用switch语句根据action值的不同分别赋予它相应的操作。怎么样,是不是比大家想象中要简单很多?

大括号是php中一个非常常用到的符号了,他不是变量也不是常量那它到底是什么呢,下面我一起来看看大括号在php中使用实例吧。


刚用到一个由字符串来设定对像属性名的功能。发现大括号的作用真强….


1. 动态设置对象的属性名的使用:
写法一(不能正确设置):

$obj->$string[$key]; //这里只能使用$string的0位置的字符作为属性名值,如:$string = 'string'; $obj->$string[$key] 等同于$obj->s[$key];

写法二(可以正确设置):

    $obj->{$string}[$key]; //这里的大括号有点类似于运算中的小括号的作用,即先算大括号里面的内容。但是请注意:

写法三(错误的写法):

    $obj->($string)[$key]; // 直接报错!

2. 检测字符串的长度中使用:

    $string = 'string';
    if(isset($string{8})) {
    echo 'Is set!';
    } else {
    echo 'Not set!';
    }

3. 像字符串中使用变量的时候也要用到,如:

    echo "{$string}, {$obj->string}";

 

在php中empty(), is_null(), isset()三个函数都可以用来判断变量是否存在的,那么empty(), is_null(), isset()之间的区别在哪里呢,如果你不明白我一起来看看下文。

empty(), is_null(), isset()真值表(区别)

我们先来看看这3个函数的功能描述 www.111cn.net

isset 判断变量是否已存在,如果变量存在则返回 TRUE,否则返回 FALSE。

empty 判断变量是否为空,如果变量是非空 或非零 的值,则 empty() 返回 FALSE。换句话说,"" 、0 、"0" 、NULL 、FALSE 、array() 、var $var; 以及没有任何属性的对象 都将被认为是空的,如果变量为空,则返回 TRUE。

is_null 判断变量是否为NULL

怎么样,一般的都是这种解释,但这种解释已经很让人迷糊了,下面结合具体的例子来分析吧!


从中我们可以发现只要变量是""或者0,或者是false和null,只要是这些值empty都会返回true。

isset只是判断变量是否存在,只要你这个变量不是null或未赋值,返回结果都是true。若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。同时要注意的是一个 NULL 字节("")并不等同于 PHP 的 NULL 常数。

而is_null正好是isset的反结果,我们可以把它看成是!isset,是isset的一个逆操作。

从以上的例子中,我们也可以得出下面这几个结论(以后编程中会经常用到的哦):

假设 $var 是任何type

当 empty($var) 为 true 时,(bool)($var) 为 false 。反之亦然。

当 is_null($var) 为 true 时,isset($var) 为 false 。反之亦然。

例如:

$i=$j+1;

这里的is_null($j)为true(可以理解为因为 isset($j)为false,因为没有事先声明 $j 这个变量)

另外需要注意的两点是:

(1)empty() 只检测变量,检测任何非变量的东西都将导致解析错误 。换句话说,后边的语句将不会起作用: empty(addslashes($name))。

(2)isset() 只能用于变量,因为传递任何其它参数都将造成解析错误。若想检测常量是否已设置,可使用 defined() 函数。


概括总结isset,empty,is_null区别

刚才介绍的:检查变量,以及参数类型,这个是这3个函数不同之处的基础,也是最容易被忽视的。看到网上有很多对这个3个函数进行比较文章。很少涉及这些。下面我要说的,是在都检查已存在变量情况下,不同之处。

 代码如下 复制代码

<?php
$a=100;
$b="";
$c=null;
//isset检查
echo "isset","$a=$a",isset($a)?"define":"www.111cN.net undefine","rn";
echo "isset","$b=$b",isset($b)?"define":"undefine","rn";
echo "isset","$c=$c",isset($c)?"define":"undefine","rn";
unset($b);
echo "isset","$b",isset($b)?"define":"undefine","rn";
$b=0;
echo "rnrn";
 
//empty检查
echo "empty","$a=$a",!empty($a)?"no empty":"empty","rn";
echo "empty","$b=$b",!empty($b)?"no empty":"empty","rn";
echo "empty","$c=$c",!empty($c)?"no empty":"empty","rn";
unset($b);
echo "empty","$b",!empty($b)?"no empty":"empty","rn";
$b=0;
echo "rnrn";
 
//is_null检查
echo "is_null","$a=$a",!is_null($a)?"no null":"null","rn";
echo "is_null","$b=$b",!is_null($b)?"no null":"null","rn";
echo "is_null","$c=$c",!is_null($c)?"no null":"null","rn";
unset($b);
echo "is_null","$b",is_null($b)?"no null":"null","rn";


 

通过上面这个简单测试,我们可以大体知道,当一个变量存在情况下:isset,empty,is_null检测,得到值情况了。上面没有举例更多变量。其实测试发现:

empty

如果 变量 是非空或非零的值,则 empty() 返回 FALSE。换句话说,""、0、"0"、NULL、FALSE、array()、var $var、未定义; 以及没有任何属性的对象都将被认为是空的,如果 var 为空,则返回 TRUE。

isset

如果 变量 存在(非NULL)则返回 TRUE,否则返回 FALSE(包括未定义)。变量值设置为:null,返回也是false;unset一个变量后,变量被取消了。注意,isset对于NULL值变量,特殊处理。

is_null

检测传入值【值,变量,表达式】是否是null,只有一个变量定义了,且它的值是null,它才返回TRUE . 其它都返回 FALSE 【未定义变量传入后会出错!】.

[!--infotagslink--]

相关文章

  • javascript设计模式之解释器模式详解

    神马是“解释器模式”?先翻开《GOF》看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如...2014-06-07
  • 学习JavaScript设计模式之装饰者模式

    这篇文章主要为大家介绍了JavaScript设计模式中的装饰者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
  • php上传图片学习笔记与心得

    我们在php中上传文件就必须使用#_FILE变量了,这个自动全局变量 $_FILES 从 PHP 4.1.0 版本开始被支持。在这之前,从 4.0.0 版本开始,PHP 支持 $HTTP_POST_FILES 数组。这...2016-11-25
  • Smarty模板学习笔记之Smarty简介

    1、简介Smarty是一个使用PHP写出来的模板PHP模板引擎,是目前业界最著名的PHP模板引擎之一。它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与HTML代码混杂在一起PHP代码逻辑分离。简单的讲,目...2014-05-31
  • JavaScript设计模式之职责链模式

    这篇文章主要介绍了JavaScript设计模式之职责链模式,对设计模式感兴趣的同学,可以参考下...2021-04-25
  • 学习JavaScript设计模式之状态模式

    这篇文章主要为大家介绍了JavaScript设计模式中的状态模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-12
  • php Observer观察者模式之学习笔记

    当我们在星际中开地图和几家电脑作战的时候,电脑的几个玩家相当于结盟,一旦我们出兵进攻某一家电脑,其余的电脑会出兵救援。 那么如何让各家电脑知道自己的盟友被攻击了...2016-11-25
  • 学习JavaScript设计模式之单例模式

    这篇文章主要为大家介绍了JavaScript设计模式中的单例模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
  • JavaScript设计模式之命令模式

    这篇文章主要介绍了JavaScript设计模式之命令模式,对设计模式感兴趣的同学,可以参考下...2021-04-25
  • PHP 日期函数 学习笔记介绍

    举一个简单的date例子 我将使用echo命令把内容输出到我们的客户端(浏览器)。我将使用下面的代码做为基础代码。 代码如下 复制代码 <!DOCTY...2016-11-25
  • 学习JavaScript设计模式之责任链模式

    这篇文章主要为大家介绍了JavaScript设计模式中的责任链模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
  • 学习JavaScript设计模式之代理模式

    这篇文章主要为大家介绍了JavaScript设计模式中的状态模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-14
  • 学习JavaScript设计模式之观察者模式

    这篇文章主要为大家介绍了JavaScript设计模式中的观察者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
  • Android学习笔记之多界面切换实例

    一篇Android学习笔记之多界面切换实例,希望对各位朋友有所帮助。 用过VB 、 VC#的朋友都知道,在VB或VC#里要进行窗口(界面)切换很容易 例如在VB、C#里: 有 Fom1、...2016-09-20
  • php中的变量引用传值学习笔记

    引用:意思是将原始对象在内存中的地址传递给目标对象,就相当于原始对象和目标对象指向的是同一个内存地址。此时,如果对目标对象或者原始对象进行修改,内存中的数据也会改...2016-11-25
  • JavaScript中的设计模式 单例模式

    这篇文章主要给大家介绍的是JavaScript中的单例模式,设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,需要的朋友可以参考一下...2021-09-25
  • C# 设计模式系列教程-组合模式

    组合模式可以使客户端调用简单,它可以一致使用组合结构或是其中单个对象,简化了客户端代码。...2020-06-25
  • PHP中的cURL请求及示例学习笔记

    cURL是php中一个很强大的功能,可以模仿各种用户请求,如模仿用户登录,发送php cookie等等操作,下面我来整理一些相关的方法与各位同学看看 备注:使用curl_init函数,必须...2016-11-25
  • 那些年,我还在学习C# 学习笔记

    那些年学了ASP.NET后,才开始学习C#,说来也怪,怎么学了ASP.NET才来学习C#,其实没有什么的...2020-06-25
  • YII2 WIDGET的学习笔记

    CWidget是所有Widget的基类。CWidget是自包含组件,可以看出是MVC的简略版,CWidget相比Controller,既没有actions,也没有filters widget,英文意思为小工具,小挂件,在程序...2016-11-25