您的位置:首页 > 编程语言 > PHP开发

五种常见的 PHP 设计模式

2017-05-26 11:10 429 查看
一、首先来看,单例模式

所谓单例模式,即在应用程序中只会有这个类的一个实例存在。

通常单例模式用在仅允许数据库访问对象的实例中,从而防止打开多个数据库连接。

一个单例类应包括以下几点:

和普通类不同,单例类不能被直接实例化,只能是由自身实例化。因此,要获得这样的限制效果,构造函数必须标记为private。

要让单例类不被直接实例化而能起到作用,就必须为其提供这样的一个实例。因此,就必须要让单例类拥有一个能保存类的实例的私有静态成员变量和对应的一个能访问到实例的公共静态方法。

在PHP中,为防止对单例类对象的克隆来打破单例类的上述实现形式,通常还为基提供一个空的私有__clone()方法。

单例模式的例子:

 

复制代码 代码如下:

<?php

class SingetonBasic {

private static $instance;

// other vars..

private function __construct() {

// do construct..

}

private function __clone() {}

public static function getInstance() {

if (!(self::$instance instanceof self)) {

self::$instance = new self();

}

return self::$instance;

}

// other functions..

}

$a = SingetonBasic::getInstance();

$b = SingetonBasic::getInstance();

var_dump($a === $b);

============================================================

其实单例模式,说白了就是说一个类只能实例化一次。但是我们如何在这个实例化一次上面做文章呢。其实有个突破口就是__construct()这个魔术方法。这个方法就代表着如果类实例化的时候,就会自动执行这个方法。然后如果我把这个方法变成保护或者私有的,会是什么效果呢。

<?phpclass test{protected function __construct(){}}$test = new test();?>

然后执行以下,就会出现这个情况。

这样的话,就不能实例化了。这样就保证不能随便让人给实例化了。

但是既然这样的话,我们应该怎么实现实例化呢。就是这样:

<?phpclass test{protected function __construct(){}public static function getInstance(){$_test = new test();return $_test;}}$test = test::getInstance();var_dump($test);?>

这样的话。实例的话,就能出现了。我们看一下:

但是说了这么多,我还是没说到重点。下面重点来了,只要我们再使用一个关键字(static)就好了。铛铛铛铛:

<?phpclass test{protected function __construct(){}public static function getInstance(){static $_test;if (empty($_test)) {$_test = new test();}return $_test;}}$test1 = test::getInstance();$test2 = test::getInstance();$test3
= test::getInstance();var_dump($test1,$test2,$test3);echo $test1 == $test2 ? 'true' : 'false';echo "<br>";echo $test2 == $test3 ? 'true' : 'false';echo "<br>";echo $test1 == $test3 ? 'true' : 'false';?>

看一下结果:

这样的话就能实现php单例的效果了。

单例的话,最长用在需要只使用这一个类,而不是会有多个类。

打个比方。比如现在有个config类,这个类主要是存储这个项目的配置信息。如果说这个类能实例化多次的话,那么如果在代码运行中对配置进行了修改,那么你怎么知道是在哪个配置类中进行了修改了呢。这个时候的话使用单例模式,就避免了情况的发生,所有对于配置文件的改变都是基于这个类的实例进行修改的。而不会出现因为多个类的实例化,操作对于操作的改变没有进行实时的更新。而且,实例多个类库,占用内存也会非常的厉害,这样只实例化一次。是不是好处多多呢。

二、工厂模式PHP工厂模式分为:简单工厂模式、工厂方法模式和抽象工厂模式

案例:https://www.zhihu.com/question/20367734

三者的区别:

简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。

工厂方法模式相当于在简单工厂模式的基础上增加一个抽象工厂,在简单工厂模式下如果增加一个产品,要修改工厂类,不符合开闭原则。在工厂方法下,只需要增加具体工厂和具体产品即可。

抽象工厂,类似于一个集团旗下生产的各种产品的工厂,这些产品是一个产品族。是在工厂方法下的扩展。比如一个产品的界面,可以通过直接改变具体工厂的实例来改变产品的界面风格

工厂模式在于可以根据输入参数或者应用程序配置的不同来创建一种专门用来实现化并返回其它类的实例的类。

工厂模式的例子:

 

复制代码 代码如下:

<?php

class FactoryBasic {

public static function create($config) {

}

}

比如这里是一个描述形状对象的工厂,它希望根据传入的参数个数不同来创建不同的形状。

 

复制代码 代码如下:

<?php

// 定义形状的公共功能:获取周长和面积。

interface IShape {

function getCircum();

function getArea();

}

// 定义矩形类

class Rectangle implements IShape {

private $width, $height;

public function __construct($width, $height) {

$this->width = $width;

$this->height = $height;

}

public function getCircum() {

return 2 * ($this->width + $this->height);

}

public function getArea() {

return $this->width * $this->height;

}

}

// 定义圆类

class Circle implements IShape {

private $radii;

public function __construct($radii) {

$this->radii = $radii;

}

public function getCircum() {

return 2 * M_PI * $this->radii;

}

public function getArea() {

return M_PI * pow($this->radii, 2);

}

}

// 根据传入的参数个数不同来创建不同的形状。

class FactoryShape {

public static function create() {

switch (func_num_args()) {

case 1:

return new Circle(func_get_arg(0));

break;

case 2:

return new Rectangle(func_get_arg(0), func_get_arg(1));

break;

}

}

}

// 矩形对象

$c = FactoryShape::create(4, 2);

var_dump($c->getArea());

// 圆对象

$o = FactoryShape::create(2);

var_dump($o->getArea());

使用工厂模式使得在调用方法时变得更容易,因为它只有一个类和一个方法,若没有使用工厂模式,则要在调用时决定应该调用哪个类和哪个方法;使用工厂模式还使得未来对应用程序做改变时更加容易,比如要增加一种形状的支持,只需要修改工厂类中的create()一个方法,而没有使用工厂模式,则要修改调用形状的代码块。

三、观察者模式

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

一个简单的示例:当听众在收听电台时(即电台加入一个新听众),它将发送出一条提示消息,通过发送消息的日志观察者可以观察这些消息。

 

概念:其实观察者模式这是一种较为容易去理解的一种模式吧,它是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦合的另一种方法。

命令链模式

命令链 模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序,而不影响其他处理程序。清单 5 显示了此模式的一个示例。

清单 5. Chain.php

<?phpinterface 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 );?>

此代码定义维护 ICommand 对象列表的
CommandChain 类。两个类都可以实现 ICommand 接口 —— 一个对邮件的请求作出响应,另一个对添加用户作出响应。 图 5 给出了 UML。

图 5. 命令链及其相关命令

如果您运行包含某些测试代码的脚本,则会得到以下输出:

% php chain.php UserCommand handling 'addUser'MailCommand handling 'mail'%

代码首先创建 CommandChain 对象,并为它添加两个命令对象的实例。然后运行两个命令以查看谁对这些命令作出了响应。如果命令的名称匹配
UserCommand 或
MailCommand,则代码失败,不发生任何操作。

为处理请求而创建可扩展的架构时,命令链模式很有价值,使用它可以解决许多问题。

策略模式

我们讲述的最后一个设计模式是策略 模式。在此模式中,算法是从复杂类提取的,因而可以方便地替换。例如,如果要更改搜索引擎中排列页的方法,则策略模式是一个不错的选择。思考一下搜索引擎的几个部分 —— 一部分遍历页面,一部分对每页排列,另一部分基于排列的结果排序。在复杂的示例中,这些部分都在同一个类中。通过使用策略模式,您可将排列部分放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。

作为一个较简单的示例,清单 6 显示了一个用户列表类,它提供了一个根据一组即插即用的策略查找一组用户的方法。

清单 6. Strategy.php

<?phpinterface IStrategy{ function filter( $record );}class FindAfterStrategy implements IStrategy{ private $_name; public function __construct( $name ) { $this->_name = $name; } public function filter( $record ) { return strcmp( $this->_name, $record ) <=
0; }}class RandomStrategy implements IStrate
bfde
gy{ public function filter( $record ) { return rand( 0, 1 ) >= 0.5; }}class UserList{ private $_list = array(); public function __construct( $names ) { if ( $names != null ) { foreach( $names as $name ) { $this->_list
[]= $name; } } } public function add( $name ) { $this->_list []= $name; } public function find( $filter ) { $recs = array(); foreach( $this->_list as $user ) { if ( $filter->filter( $user ) ) $recs []= $user; } return $recs; }}$ul = new UserList( array( "Andy",
"Jack", "Lori", "Megan" ) );$f1 = $ul->find( new FindAfterStrategy( "J" ) );print_r( $f1 );$f2 = $ul->find( new RandomStrategy() );print_r( $f2 );?>

此代码的 UML 如图 6 所示。

图 6. 用户列表和用于选择用户的策略

UserList 类是打包名称数组的一个包装器。它实现
find 方法,该方法利用几个策略之一来选择这些名称的子集。这些策略由 IStrategy 接口定义,该接口有两个实现:一个随机选择用户,另一个根据指定名称选择其后的所有名称。运行测试代码时,将得到以下输出:

% php strategy.php Array( [0] => Jack [1] => Lori [2] => Megan)Array( [0] => Andy [1] => Megan)%

测试代码为两个策略运行同一用户列表,并显示结果。在第一种情况中,策略查找排列在 J 后的任何名称,所以您将得到 Jack、Lori 和 Megan。第二个策略随机选取名称,每次会产生不同的结果。在这种情况下,结果为 Andy 和 Megan。

策略模式非常适合复杂数据管理系统或数据处理系统,二者在数据筛选、搜索或处理的方式方面需要较高的灵活性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式