设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,即是特定环境下同类问题的一种解决方案。使用设计模式是为了可重用代码、让代码灵活易维护、扩展方便,降低耦合,保证代码可靠性。
1、单例模式
单例模式顾名思义,就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式的要点有三个:
- 一是某个类只能有一个实例;
- 二是它必须自行创建这个实例;
- 三是它必须自行向整个系统提供这个实例。
- php的应用主要在于数据库应用, 一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时, 如果使用单例模式, 则可以避免大量的new创建对象操作消耗的资源,还可以减少数据库连接这样就不容易出现 too many connections情况。
- 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend Framework的FrontController和CI的CI_controller。
- 在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。
例子:
/**
* 设计模式之单例模式
* $_instance必须声明为静态的私有变量
* 构造函数必须声明为私有,防止外部程序new类从而失去单例模式的意义
* getInstance()方法必须设置为公有的,必须调用此方法以返回实例的一个引用
* ::操作符只能访问静态属性和静态函数
* new对象都会消耗内存
* 使用场景:最常用的地方是数据库连接。
* 使用单例模式生成一个对象后,该对象可以被其它众多对象所使用。
*/
class man
{
//保存例实例在此属性中
private static $_instance;
//构造函数声明为private,防止直接创建对象
private function __construct()
{
echo '我被实例化了!';
}
//单例方法
public static function get_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->test();
// 复制对象将导致一个E_USER_ERROR.
//$test_clone = clone $test;
2、工厂模式
工厂模式 是一种类,它具有为您创建对象的某些方法。您可以使用工厂类创建对象,而不直接使用 new
。这样,如果您想要更改所创建的对象类型,只需更改该工厂即可。使用该工厂的所有代码会自动更改。
/**
*
* 定义个抽象的类,让子类去继承实现它
*
*/
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、观察者模式
观察者模式属于行为模式,观察者模式定义了对象间的一种一对多的依赖关系,当被观察的对象发生改变时,所有依赖于它的对象都会得到通知并被自动更新,而且被观察的对象和观察者之间是松耦合的。在该模式中,有目标(Subject)和观察者(Observer)两种角色。目标角色是被观察的对象,持有并控制着某种状态,可以被任意多个观察者作为观察的目标,SPL(Standard PHP Library即标准 PHP 库,是 PHP 5 在面向对象上能力提升的真实写照,它由一系列内置的类、接口和函数构成。) 中使用 SplSubject
接口规范了该角色的行为。
一个对象通过添加一个方法(该方法允许另一个对象,即观察者注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。
- <?php
- interface IObserver //定义个一个接口
- {
- function onChanged( $sender, $args );
- }
- interface IObservable //定义个二个接口
- {
- function addObserver( $observer );
- }
- class UserList implements IObservable //继承第二个接口
- {
- private $_observers = array();
- public function addCustomer( $name )
- {
- foreach( $this->_observers as $obs )
- $obs->onChanged( $this, $name );//调用观察者接口来使用多个接口
- }
- public function addObserver( $observer ) //添加观察者模式接口
- {
- $this->_observers []= $observer;
- }
- }
- class UserListLogger implements IObserver //继承第一个接口
- {
- public function onChanged( $sender, $args )
- {
- echo( "'$args' added to user list/n" );
- }
- }
- class UserListLogger2 implements IObserver //继承第一个接口2
- {
- public function onChanged( $sender, $args )
- {
- echo( "'$args' 2222222222222222 list/n" );
- }
- }
- $ul = new UserList();
- $ul->addObserver( new UserListLogger() );
- $ul->addObserver( new UserListLogger2() );
- $ul->addCustomer( "Jack" );
- ?>
4、策略模式
策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。
至少在在以下两种情况下,大家可以考虑使用策略模式,
- 几个类的主要逻辑相同,只在部分逻辑的算法和行为上稍有区别的情况。
- 有几种相似的行为,或者说算法,客户端需要动态地决定使用哪一种,那么可以使用策略模式,将这些算法封装起来供客户端调用。
策略模式的主要优点有:
- 策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换。
- 易于扩展,增加一个新的策略对策略模式来说非常容易,基本上可以在不改变原有代码的基础上进行扩展。
- 避免使用多重条件,如果不使用策略模式,对于所有的算法,必须使用条件语句进行连接,通过条件判断来决定使用哪一种算法,在上一篇文章中我们已经提到,使用多重条件判断是非常不容易维护的。
策略模式的缺点主要有两个:
- 维护各个策略类会给开发带来额外开销,可能大家在这方面都有经验:一般来说,策略类的数量超过5个,就比较令人头疼了。
- 必须对客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么策略,并且了解各种策略之间的区别,否则,后果很严重。例如,有一个排序算法的策略模式,提供了快速排序、冒泡排序、选择排序这三种算法,客户端在使用这些算法之前,是不是先要明白这三种算法的适用情况?再比如,客户端要使用一个容器,有链表实现的,也有数组实现的,客户端是不是也要明白链表和数组有什么区别?就这一点来说是有悖于迪米特法则的。
5、命令链模式
命令链模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序,而不影响其他处理程序。
总体来说命令链模式就是让系统定义多个执行接口,而通过一个命令链接口来执行!
- <?php
- interface ICommand //定义一个接口
- {
- function onCommand( $name, $args );
- }
- class CommandChain //一个命令链接口类
- {
- private $_commands = array();
- public function addCommand( $cmd )//增加命令链接口
- {
- $this->_commands []= $cmd;
- }
- public function runCommand( $name, $args )//执行命令链函数接口
- {
- foreach( $this->_commands as $cmd )
- {
- if ( $cmd->onCommand( $name, $args ) )
- return;
- }
- }
- }
- class UserCommand implements ICommand //定义一个接口类
- {
- public function onCommand( $name, $args )
- {
- if ( $name != 'addUser' ) return false;
- echo( "UserCommand handling 'addUser'/n" );
- return true;
- }
- }
- class MailCommand implements ICommand //定义一个接口类
- {
- public function onCommand( $name, $args )
- {
- if ( $name != 'mail' ) return false;
- echo( "MailCommand handling 'mail'/n" );
- return true;
- }
- }
- $cc = new CommandChain();
- $cc->addCommand( new UserCommand() );
- $cc->addCommand( new MailCommand() );
- $cc->runCommand( 'addUser', null );
- $cc->runCommand( 'mail', null );
- ?>
工厂模式与策略模式的主要差异:
1、作用
工厂模式主要的用途是创建对象,根据在客户端传入一个数据,在工厂类中创建相应的对象,然后返回给客户端,它使对象的创建独立于使用对象的客户,它属于创建型模型;
策略模式主要用于选择策略方法,在客户端中根据你所选择的行为或者策略创建一个对象。策略模式就是为了解决策略的切换和扩展,它定义了策略族,并将它们封装起来,策略模式将策略的变换独立于使用策略的客户,它属于行为型模型;
2、关注点
工厂模式关注的是对象的创建
策略模式关注的是行为的封装
另外在这里引用两个我在CSDN的帖子里面看到认为比较能让我找到感觉的例子,在此非常感觉写出这个例子的大神。
一、
女人有很多种,基本都会生孩子。(不要在意例子本身不雅,注意体会其含义)
1、找个会生孩子的身材好的女人。(如何找的逻辑就交给女人工厂了,客户端只要得到女人就可以了)。
2、找个身材好的女人代孕生个孩子,当然到最后只要孩子不要女人。(如何在多种女人中找个身材好的女人代孕生孩子是策略类的职责,最后策略类只要把孩子交给客户端即可)。
二、
工厂模式:根据你给出的目的来生产不同用途的斧子,例如要砍人,那么工厂生产砍人斧子,要伐木就生产伐木斧子。即根据你给出一些属性来生产不同行为的一类对象返回给你。关注对象创建
策略模式:用工厂生产的斧子来做对应的事情,例如用砍人的斧子来砍人,用伐木的斧子来伐木。即根据你给出对应的对象来执行对应的方法。关注行为的选择