検索
ホームページphp教程php手册PHP 设计模式(二)
PHP 设计模式(二)Jun 06, 2016 pm 08:02 PM
phpコンセプトデザインパターン

设计模式 一书介绍了很多此类概念。当时,我还在学习面向对象 (OO),因此我发现那本书中有许多概念都很难领会。但是,随着越来越熟悉 OO 概念 —— 尤其是接口和继承的使用 —— 我开始看到设计模式中的实际价。作为一名应用程序开发人员,即使从不了解任何


设计模式 一书介绍了很多此类概念。当时,我还在学习面向对象 (OO),因此我发现那本书中有许多概念都很难领会。但是,随着越来越熟悉 OO 概念 —— 尤其是接口和继承的使用 —— 我开始看到设计模式中的实际价值。作为一名应用程序开发人员,即使从不了解任何模式或者如何及何时使用这些模式,对您的职业生涯也没有什么大的影响。但是,我发现了解这些模式以及 developerWorks 文章 “五种常见 PHP 设计模式” 中介绍的那些模式的优秀知识后(请参阅 参考资料),您可以完成两件事情:

启用高带宽会话
如果了解设计模式,您将能够更快地构建可靠的 OO 应用程序。但当整个开发团队知道各种模式时,您可以突然拥有非常高的带宽会话。您不再需要讨论将到处使用的所有类。相反,您可以与其他人谈论模式。“我要在这里引用一个单例(singleton),然后使用迭代器遍历对象集合,然后……” 比遍历构成这些模式的类、方法和接口快很多。单是通信效率一项就值得花时间以团队的形式通过会话来研究模式。
减少痛苦的教训
每个设计模式都描述了一种经过验证的解决常见问题的方法。因此,您无需担心设计是不是正确的,只要您已经选择了提供所需优点的模式。

缺陷

有句谚语说得好:“当您手中拿着一把锤子时,所有事物看上去都像钉子”。当您认为自己找到一个优秀模式时,您可能会尝试到处使用它,即使在不应当使用它的位置。记住您必须考虑正在学习的模式的使用目的,不要为了使用模式而把这些模式强行应用到应用程序的各个部分中。

本文将介绍可用于改进 PHP 代码的五个模式。每个模式都将介绍一个特定场景。可以在 下载 部分中获得这些模式的 PHP 代码。

要求

要发挥本文的最大功效并使用示例,需要在计算机中安装以下软件:

  • PHP V5 或更高版本(本文是使用 PHP V5.2.4 撰写的)
  • 压缩程序,例如 WinZIP(用于压缩可下载的代码归档)

注:虽然您也可以使用纯文本编辑器,但是我发现拥有语法高亮显示和语法纠错功能的编辑器真的很有帮助。本文中的示例是使用 Eclipse PHP Development Tools (PDT) 编写的。

适配器模式

在需要将一类对象转换成另一类对象时,请使用适配器模式。通常,开发人员通过一系列赋值代码来处理此过程,如清单 1 所示。适配器模式是整理此类代码并在其他位置重用所有赋值代码的优秀方法。此外,它还将隐藏赋值代码,如果同时还要设定格式,这样可以极大地简化工作。

清单 1. 使用代码在对象之间赋值

class AddressDisplay
{
    private $addressType;
    private $addressText;

    public function setAddressType($addressType)
    {
        $this->addressType = $addressType;
    }

    public function getAddressType()
    {
        return $this->addressType;
    }

    public function setAddressText($addressText)
    {
        $this->addressText = $addressText;
    }

    public function getAddressText()
    {
        return $this->addressText;
    }
}

class EmailAddress
{
    private $emailAddress;
    
    public function getEmailAddress()
    {
        return $this->emailAddress;
    }
    
    public function setEmailAddress($address)
    {
        $this->emailAddress = $address;
    }
}

$emailAddress = new EmailAddress();
/* Populate the EmailAddress object */
$address = new AddressDisplay();
<span>/* Here's the assignment code, where I'm assigning values 
  from one object to another... */</span>
$address->setAddressType("email");
$address->setAddressText($emailAddress->getEmailAddress());

此示例将使用 AddressDisplay 对象把地址显示给用户。AddressDisplay 对象有两部分:地址类型和一个格式化的地址字符串。

在实现模式(参见清单 2)后,PHP 脚本将不再需要担心如何把 EmailAddress 对象转换成 AddressDisplay 对象。那是件好事,尤其是在AddressDisplay 对象发生更改时或者控制如何把 EmailAddress 对象转换成 AddressDisplay 对象的规则发生更改时。记住,以模块化风格设计代码的主要优点之一就是,在业务领域发生一些更改时或者需要向软件中添加新功能时尽可能少的使用更改。即使在执行普通任务(例如把一个对象的属性值赋给另一个对象)时,也请考虑使用此模式。

清单 2. 使用适配器模式

class EmailAddressDisplayAdapter extends AddressDisplay
{
    public function __construct($emailAddr)
    {
        $this->setAddressType("email");
        $this->setAddressText($emailAddr->getEmailAddress());
    }
}	

$email = new EmailAddress();
$email->setEmailAddress("user@example.com");

$address = new EmailAddressDisplayAdapter($email);

echo($address->getAddressType() . "\n") ;
echo($address->getAddressText());

图 1 显示了适配器模式的类图。

图 1. 适配器模式的类图
PHP 设计模式(二)

替代方法

编写适配器的替代方法 —— 并且是推荐方法 —— 是实现一个接口来修改行为,而不是扩展对象。这是一种非常干净的、创建适配器的方法并且没有扩展对象的缺点。使用接口的缺点之一是需要把实现添加到适配器类中,如图 2 所示:

图 2. 适配器模式(使用接口)

回页首

迭代器模式

迭代器模式将提供一种通过对象集合或对象数组封装迭代的方法。如果需要遍历集合中不同类型的对象,则使用这种模式尤为便利。

查看上面清单 1 中的电子邮件和物理地址示例。在添加迭代器模式之前,如果要遍历个人地址,则可能要遍历物理地址并显示这些地址,然后遍历个人电子邮件地址并显示这些地址,然后遍历个人 IM 地址并显示这些地址。非常复杂的遍历!

相反,通过实现迭代器,您只需要调用 while($itr->hasNext()) 并处理下一个条目 $itr->next() 返回。清单 3 中显示了一个迭代器示例。迭代器功能强大,因为您可以添加要遍历的新类型条目,并且无需更改遍历条目的代码。例如,在 Person 示例中,可以添加 IM 地址数组;只需更新迭代器,无需更改遍历地址的任何代码。

清单 3. 使用迭代器模式遍历对象

class PersonAddressIterator implements AddressIterator
{
    private $emailAddresses;
    private $physicalAddresses;
    private $position;
    
    public function __construct($emailAddresses)
    {
        $this->emailAddresses = $emailAddresses;
        $this->position = 0;
    }
    
    public function hasNext()
    {
        if ($this->position >= count($this->emailAddresses) || 
            $this->emailAddresses[$this->position] == null) {
            return false;
        } else {
            return true;
        }
    }
    
    public function next()
    {
        $item = $this->emailAddresses[$this->position];
        $this->position = $this->position + 1;
        return $item;
    }
    
}

如果把 Person 对象修改为返回 AddressIterator 接口的实现,则在将实现扩展为遍历附加对象时无需修改使用迭代器的应用程序代码。您可以使用一个混合迭代器,它封装了遍历清单 3 中列出的每种地址的迭代器。本文提供了此类应用示例(请参阅 下载)。

图 3 显示了迭代器模式的类图。

图 3. 迭代器模式的类图
PHP 设计模式(二)

回页首

装饰器 (decorator) 模式

考虑清单 4 中的代码样例。这段代码的目的是要把许多功能添加到 Build Your Own Car 站点的汽车中。每个汽车模型都有更多功能及相关价格。如果只针对两个模型,使用 if then 语句添加这些功能十分平常。但是,如果出现了新模型,则必须返回查看代码并确保语句对新模型工作正常。

清单 4. 使用装饰器模式添加功能

require('classes.php');

$auto = new Automobile();

$model = new BaseAutomobileModel();

$model = new SportAutomobileModel($model);

$model = new TouringAutomobileModel($model);

$auto->setModel($model);

$auto->printDescription();

进入装饰器模式,该模式允许您通过一个优秀整洁的类将此功能添加到 AutomobileModel。每个类仅仅关注其价格、选项以及添加到基本模型的方式。

图 4 显示了装饰器模式的类图。

图 4. 装饰器模式的类图
PHP 设计模式(二)

装饰器模式的优点是可以轻松地同时跟踪库的多个装饰器。

如果您拥有流对象的使用经验,则一定使用过装饰器。大多数流结构(例如输出流)都是接受基本输入流的装饰器,然后通过添加附加功能来装饰它 —— 例如从文件输入流、从缓冲区输入流,等等。

回页首

委托模式

委托模式将提供一种基于各种条件委托行为的方法。考虑清单 5 中的代码。这段代码包含几个条件。根据条件,代码将选择相应类型的对象来处理请求。

清单 5. 使用条件语句来发送送货请求

pkg = new Package("Heavy Package");
$pkg->setWeight(100);

if ($pkg->getWeight() > 99)
{
	echo( "Shipping " . $pkg->getDescription() . " by rail.");
} else {
	echo("Shipping " . $pkg->getDescription() . " by truck");
}

使用委托模式,对象将内在化(internalize)此发送过程,方法为在调用如清单 6 中的 useRail() 之类的方法时设置对相应对象的内部引用。如果处理各个包的条件发生更改或者使用新的送货类型时,则使用此模式尤为便利。

清单 6. 使用委托模式来发送送货请求

require_once('classes.php');

$pkg = new Package("Heavy Package");
$pkg->setWeight(100);

$shipper = new ShippingDelegate();

if ($pkg->getWeight() > 99)
{
	$shipper->useRail();
}

$shipper->deliver($pkg);

委托将通过调用 useRail() 或 useTruck() 方法来切换处理工作的类,从而提供动态更改行为的优点。

图 5 显示了委托模式的类图。

图 5. 委托模式的类图
PHP 设计模式(二)

回页首

状态模式

状态模式类似于命令模式,但是意图截然不同。考虑下面的代码。

清单 7. 使用代码来构建机器人

class Robot 
{

	private $state;

	public function powerUp()
	{
		if (strcmp($state, "poweredUp") == 0)
		{
			echo("Already powered up...\n");
			/* Implementation... */
		} else if ( strcmp($state, "powereddown") == 0) {
			echo("Powering up now...\n");
			/* Implementation... */
		}
	}

	public function powerDown()
	{
		if (strcmp($state, "poweredUp") == 0)
		{
			echo("Powering down now...\n");
			/* Implementation... */
		} else if ( strcmp($state, "powereddown") == 0) {
			echo("Already powered down...\n");
			/* Implementation... */
		}
	}

	/* etc... */

}

在此清单中,PHP 代码表示变成一辆汽车的强大机器人的操作系统。机器人可以启动、关闭、由汽车变成机器人以及由机器人变成汽车。代码现已就绪,但是您会看到如果任何规则发生更改或者添加另一个状态则会变得十分复杂。

现在查看清单 8,其中提供了相同的逻辑处理机器人的状态,但是这一次把逻辑放入状态模式。清单 8 中的代码完成的工作与初始代码相同,但是用于处理状态的逻辑已经被放入每个状态的一个对象中。为了演示使用设计模式的优点,假定不久以后,这些机器人发现它们不应在处于机器人模式时关闭。实际上,如果它们关闭,它们必须先切换到汽车模式。如果它们已经处于汽车模式下,则机器人将关闭。使用状态模式,对代码的更改十分微小。

清单 8. 使用状态模式处理机器人的状态

$robot = new Robot();
echo("\n");
$robot->powerUp();
echo("\n");
$robot->turnIntoRobot();
echo("\n");
$robot->turnIntoRobot(); /* This one will just give me a message */
echo("\n");
$robot->turnIntoVehicle();
echo("\n");
清单 9. 对一个状态对象的微小更改

class NormalRobotState implements RobotState
{
    private $robot;

    public function __construct($robot)
    {
        $this->robot = $robot;
    }

    public function powerUp()
    {
        /* implementation... */
    }
    public function powerDown()  
    {
        <span>/* First, turn into a vehicle */
        $this->robot->setState(new VehicleRobotState($this->robot));
        $this->robot->powerDown();</span>
    }
    
    public function turnIntoVehicle()  
    {
        /* implementation... */
    }
    
    public function turnIntoRobot() 
    {
        /* implementation... */
    }
}

图 6 中一个不太明显的地方就是状态模式中的每个对象都有对上下文对象(机器人)的引用,因此每个对象都可以把状态提升到相应的状态。

图 6. 状态模式的类图
PHP 设计模式(二)

回页首

结束语

在 PHP 代码中使用设计模式可以使代码更容易阅读、更易维护。通过使用已经建立的模式,您将从通用的设计结构中获益,从而允许团队的其他开发人员了解代码的意图。它还使您可以从其他设计者完成的工作中获益,因此无需从失败的设计理念中吸取教训。


声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么设置implode没有分隔符php怎么设置implode没有分隔符Apr 18, 2022 pm 05:39 PM

在PHP中,可以利用implode()函数的第一个参数来设置没有分隔符,该函数的第一个参数用于规定数组元素之间放置的内容,默认是空字符串,也可将第一个参数设置为空,语法为“implode(数组)”或者“implode("",数组)”。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境