PHP常用的设计模式

1.单例模式


含义:

单例模式顾名思义,就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。


要点:

1. 一是某个类只能有一个实例;

2. 二是它必须自行创建这个实例;

3. 三是它必须自行向整个系统提供这个实例。


场景:

1. php的应用主要在于数据库应用, 一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时, 如果使用单例模式, 则可以避免大量的new 操作消耗的资源,还可以减少数据库连接这样就不容易出现 too many connections情况。

2. 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend Framework的FrontController部分。

3. 在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。

示例:

class man
{
    //保存例实例在此属性中
    private static $_instance;

    //构造函数声明为private,防止直接创建对象
    private function __construct()
    {
        echo '我被实例化了!';
    }

    //单例方法
    public static function get_instance()
    {
        var_dump(isset(self::$_instance));
        
        if(!isset(self::$_instance))
        {
            self::$_instance=new self();
        }
        return self::$_instance;
    }

    //阻止用户复制对象实例
    private function __clone()
    {
        trigger_error('Clone is not allow' ,E_USER_ERROR);
    }

    function test()
    {
        echo("test");

    }
}

// 这个写法会出错,因为构造方法被声明为private
//$test = new man;

// 下面将得到Example类的单例对象
$test = man::get_instance();
$test = man::get_instance();
$test->test();

// 复制对象将导致一个E_USER_ERROR.
//$test_clone = clone $test;



2.简单工厂模式


含义:

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。


要点:

1. 抽象基类:类中定义抽象一些方法,用以在子类中实现

2. 继承自抽象基类的子类:实现基类中的抽象方法

3. 工厂类:用以实例化所有相对应的子类


场景:



示例:

/**
 * 定义个抽象的类,让子类去继承实现它
 */
abstract class Operation {
    // 抽象方法不能包含函数体
    abstract public function getValue($num1, $num2); // 强烈要求子类必须实现该功能函数
}

/**
 * 加法类
 */
class OperationAdd extends Operation {
  public function getValue($num1, $num2) {
	return $num1 + $num2;
    }
}
/**
 * 减法类
 */
class OperationSub extends Operation {
    public function getValue($num1, $num2) {
	return $num1 - $num2;
  }
}
/**
 * 乘法类
 */
class OperationMul extends Operation {
  public function getValue($num1, $num2) {
	return $num1 * $num2;
  }
}
/**
 * 除法类
 */
class OperationDiv extends Operation {
    public function getValue($num1, $num2) {
	    try {
		if ($num2 == 0) {
		    throw new Exception ( "除数不能为0" );
		} else {
	            return $num1 / $num2;
	    	}
	    } catch ( Exception $e ) {
	    echo "错误信息:" . $e->getMessage ();
	}
  }
}

// 通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等,以还可以避免加载没有必要的代码。

// 如果我们现在需要增加一个求余的类,会非常的简单
// 我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展
/**
 * 求余类(remainder)
 */
class OperationRem extends Operation {
    public function getValue($num1, $num2) {
	    return $num1 % $num12;
    }
}
// 现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
// 解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂

/**
 * 工程类,主要用来创建对象
 * 功能:根据输入的运算符号,工厂就能实例化出合适的对象
 */
class Factory {
    public static function createObj($operate) {
	    switch ($operate) {
		case '+' :
		    return new OperationAdd ();
		    break;
		case '-' :
		    return new OperationSub ();
		    break;
		case '*' :
		    return new OperationSub ();
		    break;
		case '/' :
		    return new OperationDiv ();
		    break;
	    }
    }

}
$test = Factory::createObj ( '/' );
$result = $test->getValue ( 23, 0 );
echo $result;


3.观察者模式


含义:

观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。


要点:

1. 定义2个接口:观察者(通知)接口、被观察者(主题)接口

2. 定义2个类,观察者对象实现观察者接口、主题类实现被观者接口

3. 主题类注册自己需要通知的观察者

4. 主题类某个业务逻辑发生时通知观察者对象,每个观察者执行自己的业务逻辑。


场景:

哈票以购票为核心业务(此模式不限于该业务),但围绕购票会产生不同的其他逻辑,如:

 1、购票后记录文本日志

 2、购票后记录数据库日志

 3、购票后发送短信

 4、购票送抵扣卷、兑换卷、积分

 5、其他各类活动等

 

 传统解决方案:

 在购票逻辑等类内部增加相关代码,完成各种逻辑。

 

 存在问题:

 1、一旦某个业务逻辑发生改变,如购票业务中增加其他业务逻辑,需要修改购票核心文件、甚至购票流程。

 2、日积月累后,文件冗长,导致后续维护困难。

 存在问题原因主要是程序的"紧密耦合",使用观察模式将目前的业务逻辑优化成"松耦合",达到易维护、易修改的目的,


示例:

/**
 * 观察者接口(通知接口)
 */
interface ITicketObserver // 观察者接口
{
  function onBuyTicketOver($sender, $args); // 得到通知后调用的方法
}
/**
 * 主题接口
 */
interface ITicketObservable // 被观察对象接口
{
  function addObserver($observer); // 提供注册观察者方法
}
//主题类实现
/**
 * 主题类(购票)
 * 实现主题接口(被观察者)
 */
class HipiaoBuy implements ITicketObservable { 
  // 通知数组(观察者)
  private $_observers = array (); 
  /**
   * 购票核心类,处理购票流程
   */
  public function buyTicket($ticket) {
    // TODO 购票逻辑
	
    // 循环通知,调用其onBuyTicketOver实现不同业务逻辑
    foreach ( $this->_observers as $obs ){
         $obs->onBuyTicketOver ( $this, $ticket ); // $this 可用来获取主题类句柄,在通知中使用
     }
   }
	
    // 添加通知
  public function addObserver($observer){	
	$this->_observers [] = $observer;
  }
}

// 定义多个通知

// 短信日志通知
class HipiaoMSM implements ITicketObserver {
  public function onBuyTicketOver($sender, $ticket) {
            echo (date ( 'Y-m-d H:i:s' ) . " 短信日志记录:购票成功:$ticket<br>");
  }
}
// 文本日志通知
class HipiaoTxt implements ITicketObserver {
  public function onBuyTicketOver($sender, $ticket) {
            echo (date ( 'Y-m-d H:i:s' ) . " 文本日志记录:购票成功:$ticket<br>");
  }
}
// 抵扣卷赠送通知
class HipiaoDiKou implements ITicketObserver {
  public function onBuyTicketOver($sender, $ticket) {
            echo (date ( 'Y-m-d H:i:s' ) . " 赠送抵扣卷:购票成功:$ticket 赠送10元抵扣卷1张。<br>");
  }
}
//用户购票
$buy = new HipiaoBuy ();
//根据不同业务逻辑加入各种通知
$buy->addObserver ( new HipiaoMSM () );
$buy->addObserver ( new HipiaoTxt () );
$buy->addObserver ( new HipiaoDiKou () );
// 执行购票
$buy->buyTicket ( "一排一号" );


4.策略模式


含义:

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。


要点:

1. 一个接口,用于规范一系列策略,暴露统一的接口。

2. 抽象类,可有可无,主要用于提供辅助类。

3. 一系列策略类。


场景:

刘备要到江东娶老婆了,走之前诸葛亮给赵云三个锦囊妙计,说是按天机拆开能解决棘手问题。场景中出现三个要素:三个妙计(三种策略,具体策略类)、一个锦囊(环境类)、赵云(调用者)。


示例:

//一个接口,用于规范一系列策略,暴露统一的接口。
interface Strategy {  
     public function operate();  
}
 
//策略一: 初到吴国
class BackDoor implements Strategy{
     
    public function operate() {
        echo '找乔国老帮忙,让吴国太给孙权施加压力,使孙权不能杀刘备<br/>';
    }
}
 
//策略二: 求吴国太开绿灯放行
class GivenGreenLight implements Strategy{
    public function operate() {
        echo '求吴国太开个绿灯,放行<br/>';
    }
}
 
//策略三: 孙夫人断后,挡住追兵
class BlackEnemy  implements Strategy{
    public function operate() {
        echo '孙夫人断后,挡住追兵<br/>';
    }
}
 
class Context {
    /**
     * 策略对象
     * @var Strategy
     */
    private $strategy;
     
    function __construct(Strategy $_strategy){
        $this->strategy = $_strategy;
    }
     
    public function operate() {
        $this->strategy->operate();
    }
}

//调用者,这里就不封装成类了,直接使用
$context = null;
//使用策略一
$context = new Context( new BackDoor() );
$context->operate();
//使用策略二
$context = new Context( new GivenGreenLight() );
$context->operate();
//使用策略三
$context = new Context( new BlackEnemy() );
$context->operate();

/**
依次输出:
找乔国老帮忙,让吴国太给孙权施加压力,使孙权不能杀刘备
求吴国太开个绿灯,放行
孙夫人断后,挡住追兵
**/


本文链接:http://www.likelys.com/article/10559 posted @ 2017-02-08 09:22:59
top