最火下载站首页
手机版
最火下载站
关注公众号
最火下载站

当前位置:首页 > 网络知识 > 网络编程 > PHP教程> php教程:《PHP设计模式介绍》 导言

php教程:《PHP设计模式介绍》 导言

文章作者:网友投稿 发布时间:2008-08-15 来源:网络

《PHP设计模式介绍》 导言

当你在不断的试图从你的应用程序中发现新的特征时,你是否发现你提出的解决方法和一些以前你已经实现的东西是如此的类似呢?如果你是一个程序员(即使你才 开始很短的时间),你都可能回答“是”。这看起来就是你使用了一些以前的代码来解决软件开发过程中新发现的问题了。你可能已经认识到:你的解决方法是基本 原理,是一个不仅仅是你而且是所有专业开发者都能广泛重复应用的方法。

事实上,许多程序问题都不断的反复的遇到,而且许多用来解决这些问题的基本方法(或者说是设计模式)都已经浮现出来。设计模式就是一个教你如何利用真实可靠的设计来组织你的代码的模板。

设计模式历史

“设计模式”这个术语最初被设计用于建筑学领域。Christopher Alexander 在他1977的著作“A Pattern Language :Towns/Building/Construction”里面描述了一些常见的建筑学设计问题,并解释了如何用这些已有的,著名的模式集合来开始全新 的有效的设计。Alexander的观点被很好的转化到软件开发上来,并且长期的合意的用原有的组件来构造新的解决方案。 网页教学网

所有的设计模式都有一些常用的特性:一个标识(a name),一个问题陈述(a problem statement)和一个解决方案(a solution)。 Webjx.Com

一个设计模式的标识是重要的,因为它会让其他的程序员不用进行太深入的学习就能立刻理解你的代码的目的(至少通过这个标识程序员会很熟悉这个模式)。
问题描述是用来说明这个模式的应用的领域。?
解决方案描述了这个模型的执行。一个好的设计模式的论述应该覆盖使用这个模型的优点和缺点。
一个模式是解决特定问题的有效方法。一个设计模式不是一个库(能在你的项目中直接包含和使用的代码库)而是一个用来组织你的代码的模板。事实上,一个代码库和一个设计模式在应用上是有很多不同的。

比如,你从店铺里面买的一件衬衫是一个代码库,它的颜色,样式和大小都由设计师和厂商决定,但它满足了你的需求。

然而,如果店里面没有什么衣服适合你,那你就能自己创建自己的衬衫(设计它的形状,选择布料,然后裁缝在一起)。但是如果你不是一个裁缝,你可能会发现自 己很容易的去找一个合适的模式然后按着这个模式去设计自己的衬衫。使用一个模型,你可以在更少的时间内得到一个熟练设计的衬衫。

回到讨论软件上来,一个数据提取层或者一个CMS(content management system)就是一个库——它是先前设计好而且已经编码好了的,如果它能准确的满足你的需要那它就是一个好的选择。但如果你正在读这本书,可能你会发现 库存的(原有的)解决方案并不是总是对你有效。至今你知道什么是你所要的,而且你能够实现它,你仅仅需要一个模型来引导你。

最后一个想法:就象一个裁缝模型,一个设计本身而言是没有什么用处的。毕竟,你不可能穿一个服装模型——它仅仅是由很薄的纸拼凑起来的。类似的,一个软件设计模型也仅仅只是一个引导。它必须根据程序设计语言和你的应用程序的特点和要求而特别的设计。

本书的目标


本书的目的不是为了全面的介绍各种类别的软件设计模式,也不是为了发展一个新的设计模式或者术语,而是为了突出介绍一些已有的著名的设计模式。这本书的 独特之处在于介绍了一些我认为对开发动态WEB应用程序很有帮助的设计模式,而且用PHP语言显示了如何实现这些设计模式。


面对对象编程OOP

这本书一个优点就是所有的观点都是基于OOP这种很自然的设计模式,并且用OOP来实现。

如果你不熟悉OOP,有很多相关的资源(书籍,网站,杂志,上课等等)来帮助你更好的了解它。大多数OOP资料都赞美它的好处——代码重用,代码健壮,代 码封装,多态和可扩展性,当然所有的这些也是非常重要和有用的。然而,我认为OOP最主要的优点是它如何激励你亲手将问题分解成易处理的模块。清晰的设计 和实现,分解成小的模块,这样你的代码能够得到更加彻底的测试而且也更加容易理解和维护。

读者技能要求

这本书假定你已经能够流畅的使用PHP了。特别地,这本书假设你已经了解了PHP和PHP语法的运作知识而且懂得用OOP执行PHP代码的基本原理。这本书不是一本PHP编程的介绍书,也不是为了介绍PHP的OOP编程的。

由于不是所有的OOP开发者都使用相同的术语,当有新的术语被引入时,我都将在正文中或者工具条中定义。#p#副标题#e#
PHP4和PHP5

当我写这本书的时候,PHP5已经发行有一段时间了,但是还没有被公众团体广泛的采用。在我自己的工作上,我已经开始把新的应用程序开发工作迁移到 PHP5.0.3的环境中,而且到目前为止我很高兴的发现PHP5对PHP4的代码具有很好的向后兼容性,同时它的面对对象模型是PHP5最有意义的新特 征之一。


有很多很好的文章和指导手册来处理不同PHP版本之间的对象模型之间的细微差别。但简单来说,PHP5提供了:


对象柄(将在下面解释,更详细的请见第二章:对象模型的价值)?
更好的构造器(统一名字,不允许改变)?
析构器?
可见度(方法和属性的public公开,protected受保护,private私有)?
异常处理(可以选择新的语法try{}catch{}来触发错误)?
静态类?
映像(动态检查类,方法,属性)?
类型隐藏?
 PHP5也提供了一些更加模糊的特征:

 新的魔术方法?

__get()和__set()允许你来控制变量存取
__call()让你能动态的截取被call对象的所有属性。
__sleep()和__wakeup()能让你重载序列化的方法
__toString()让你能控制如何用一个字符串来描述这个对象自身。
自动加载Autoloading(当类第一次被对象化时候,允许用户自动加载该类)?
Final(不允许该方法或者一个类被其子类重载)?
对象柄 网页教学网

 PHP5最好的特性就是采用句柄来定义类,类似于一个文件或者数据库句柄。在PHP函数中使用对象不再是含蓄的复制这个对象,而是提供一个操作句柄。

为了更加清楚的看出其间的不同,我们考虑下面两个例子:

// PHP4 class
class ExampleP1 {
var $foo;
function setFoo($foo) {
$this->foo = $foo`;
}
function getFoo() {
return $this->foo;
}
}
function changeExample($param) {
$param->setFoo(‘blah’);
return $param->getFoo();
}
$obj = new ExampleP1;
$obj->setFoo(‘bar’);
echo $obj->getFoo(); // bar
echo ChangeExample($obj); //blah
echo $obj->getFoo(); // bar
 

在PHP4中,函数changeExample()中的变量$param是$obj的一个复制,所以,这个函数没有改变原来对象中的$foo的值,这样$obj->getFoo()最终输出的是’bar’。


在PHP5中,由于$obj在函数中只是一个对象操作柄,同样的changeExample()函数确确实实的影响了原来的对象。换句话说,使用操作柄,就不再需要去复制了而$param就是$obj实例。


// PHP5 class
class ExampleP2 {
protected $foo;
function setFoo($foo) {
$this->foo = $foo;
}
function getFoo() {
return $this->foo;
}
}
$obj = new ExampleP2;
$obj->setFoo(‘bar’);
echo $obj->getFoo(); // bar
echo ChangeExample($obj); //blah
echo $obj->getFoo(); // IMPORTANT, produces blah
 

当你在其他的对象或者这个对象内置的构造函数(__construct)里面使用$this这个变量时,这个问题会变的更加的复杂。 网页教学网

结果是在PHP4中,你几乎总需要:

创建一个引用对象,就象这样$obj=?& new class;
在函数中调用引用对象,比如function func(?&$obj_param){}
通过引用函数来捕捉对象,比如function? &some_func(){}  $return_obj=&some_funct()
事实上,现在有些情况下你也需要复制原始对象。在我的PHP4代码里面,我总是把任何无引用的对象任务比如为一个明显的对象复制注释的很清楚。从长 期来说,这样的一个简短的注释能够极大的减轻任何一个维护你代码的人的头痛(译者注:这样的代码可维护性很强)。引用传值,对象柄和对象复制的相关知识将 在第二章“对象模式的价值”里面详细讲解。 

尽管我个人很偏向于使用PHP5开发,但我感觉到PHP4将继续被我们使用相当长的一段时间,而且现有的公开发布的项目也将继续支持PHP4。所以,本 书对两种版本的PHP提供相同的支持。尽可能的提供了PHP4,PHP5两个版本的实例代码。在每一章里面,每一个代码块(在不同的PHP版本中有所改动 的)中都提供有相应的注释来暗示其改变,比如//php4和//php5。

参考书目和其他资源

这里有很多相关的参考书来帮助你更好的学习设计模式。设计模式的“圣经”(译者译:最好的书)是Erich Gamma, Richard Helm, Ralph Johnson和John Vlissides(他的开创性作品经常被提及为“Gang of Four”简写为”GOF”,就是代表他们四个人)撰写的《设计模式:可重用的面对对象软件开发原理》。

 关于“设计模式”,下一个最有用的关于PHP WEB应用程序设计模式方面的书就是Martin Fowler出版的Patterns of Enterprise Application Architecture (译者译:企业应用程序体系结构模型)。与GOF的书包含了所有一般的设计模式相比,Fowler的这本书详细的介绍了许多专门用于开发web应用程序的 设计模式。


另外在很多网站上也提供了有关设计模式方面的丰富资源,一个特别典型的网站就是波特兰模型库(http://cz.com/por/)。


另外一个关于PHP设计模式的站点是phpPatterns,在线地址是http://www.phppatterns.com。 

感谢


我很感谢我得老板,在他那里,我的任务和责任允许我在这个我感兴趣的领域花费一部分时间,也感谢他给我提供知识和经验让我有信心写完这本书。

我的灵感,想法和经验的另外一个来源是SitePoint这个站点(http://www.sitepoint.com)的论坛。特别值得一提的,那些经 常上”Advanced PHP Forum”(译者译:PHP高级论坛)的投稿人都有着丰富的知识与经验,他们是我在网上发现的最慷慨大方的共享自己想法的团体。我也正是通过这些资源 (译者注:SitePoint站点)登陆了SimpleTest(http://simpletest.sf.net),WACT(http: //wact.sf.net)和其他许多我认为无价的PHP工程项目。在接下来的岁月里,我希望SitePoint能够继续成为PHP开发者的丰富资源 点。


没有PHP开发团队的贡献和重要的努力,这本书显然是不可能存在的。因为正是他们开发出如此好用、易学、通用而且非常适合开发WEB应用程序的语言。 

最后,我还要感谢Marco Tabini 和php|architect的全体成员。这本杂志(译者注:php|architect)已经成为许多不同PHP主题的来源,而且这些主题都是专业的开发人员发布的。由Macro和公司主办的会议也是相当好的。 
#p#副标题#e#
《PHP设计模式介绍》第一章 编程惯用法

学习一门新的语言意味着要采用新的惯用法。这章将介绍或者可能重新强调一些惯用法。你会发现这些惯用法在你要在代码中实现设计模式时候是非常有用的。

在这里总结的许多编程惯用法都是很值得做为单独一个章节的,甚至一本书的。你应该把这章做为PHP模式设计使用惯用法的相关介绍,而且查看一些列出的参考书来进行更深入的学习。

测试你的代码

可能没有什么代码惯用法比测试代码更加重要了。好的测试可以提高开发速度。


可能一开始,这句格言会和你的直觉相矛盾。你可能会断言,测试是自由的障碍物。事实上恰恰相反,如果你十分完整的运行那些测试来检查你的软件的公共接口,你就可能在不改变(或者更加糟糕,破坏)原来的应用软件的前提下改变自己系统内在的执行。测试并检验你的公共接口的精确性和正确性,并且让自己随意改变一些代码的内在工作来确保你的软件是正确而且没有bug(错误)。

在讨论更多关于测试的好处之前,先让我们看一个示例。这本书里面所有的测试实例都使用了PHP测试框架——SimpleTest 。这个测试框架可以在 http://simpletest.org 获取到。

考虑下面的代码

上面的代码首先定义了一个常量——TAX_RATE,和一个计算销售税的函数。接着,代码包含了使用SimpleTest框架的必备组件:单体测试本身和一个用来显示测试结果的“reporter”模块。

类TestingTestCase继承于SimpleTest框架的UnitTestCase类。通过扩展UnitTestCase,类TestingTestCase里面所有使用Test开头的方法都将被认为是测试实例——创造条件来调试你的代码并断言结果。

TestingTestCase定义了一个测试,TestSalesTax(),它包含了一个断言函数AssertEqual()。如果它的前两个输入参数是相等的,它将返回true,否则返回false。(如果你想显示assertEqual()失败的信息,你可以传入三个参数就像这样$this->assertEqual(7,calculate_sales_tax(100), “The sales tax calculation failed”))。

代码的最后两行创建了这个测试实例的实体并且使用一个HtmlReporter运行了它。你可以访问这个web页面来运行这个简单的测试。

运行这个测试将显示测试名称,失败断言的详细情况和一个总结条。(绿色的意味着成功(所有的断言都通过了),而红色的暗示着失败(至少有一个断言没有通过))

(assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。)

注:(assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。)

上面的代码有一个(有意的)错误,所以运行是不能通过了,显示结果如下:

Calculate_sales_tax()这么一个简单的才一行的函数哪里出错了呢?你可能已经注意到这个函数没有返回结果。下面是正确的函数:

function calculate_sales_tax($amount) {
return round($amount * TAX_RATE,2);
}


修改后运行,测试通过。

但是一个简单的测试并不能保证代码是稳定的。比如,你把calculate_sales_tax()改成 function calculate_sales_tax($amount) { return 7; },代码也会通过测试,但只有当1美元等价于100的时候才是正确的。你可以自己增加一些额外的测试方法来测试其他的静态值。

function TestSomeMoreSalesTax() {
$this->assertEqual(3.5, calculate_sales_tax(50));
}

或者改变函数TestSalesTax()来验证第二个(和第三个,等等)值,如下所示

function TestSalesTax() {
$this->assertEqual(7, calculate_sales_tax(100));
$this->assertEqual(3.5, calculate_sales_tax(50));
}

到目前为止还有一种更好的方法,就是新增加一个测试:选择随即值来测试你的代码。具体如下:

function TestRandomValuesSalesTax() {
$amount = rand(500,1000);
$this->assertTrue(defined(‘TAX_RATE’));
$tax = round($amount*TAX_RATE*100)/100;
$this->assertEqual($tax, calculate_sales_tax($amount));
}

TestRandomValuesSalesTax()引入了方法assertTrue(),如果传入的第一个变量等于于布尔真则assertTrue()通过。(和方法assertEqual()一样,方法assertTrue()在接受一个可选择性的、额外的后将返回一个失败的信息)。所以TestRandomValuesSalesTax()首先认为常量TAX_RATE已经定义了,然后使用这个常量来计算随机选择的的数量的税收。

但是TestRandomValuesSalesTax()也存在一个问题:它很大程度的依赖于方法calculate_sales_tax()。测试是应该和特殊的实现细节无关的。一个更好的测试应该只建立一个合理的分界线。接下来的这个测试假定销售税永远不会超过20%。

function TestRandomValuesSalesTax() {
$amount = rand(500,1000);
$this->assertTrue(calculate_sales_tax($amount)<$amount*0.20);
}

确保你的代码正常工作是测试的首要的目的,但是在测试你的代码时候,你应该认识到除此之外还有一些额外的,相对次要的目的:

测试让你书写容易测试的代码。这使得代码松散耦合,复杂设计,而且具有很好的模块性。
测试能让你清晰的了解运行代码的期望结果,让你从一开始就注重于模块的设计和分析。通过测试,也会让你考虑所有可能的输入和相应的输出结果。
测试能很快速的了解编码的目的。换句话说,测试事例扮演着“实例”和“文档”的功能,准确的展示着如何构建一个类,方法等。在这本书中,我有时候通过一个测试事例来演示代码的期望功能。通过读取一个测试方法的声明,你可以清楚的了解代码是如何运行的。一个测试实例定义在代码在明确惯用法下的运行情况。
最后,如果你的测试集——测试实例的集合——是非常彻底的,而且当所有的测试都通过的时候,你可以说你的代码是完备的。有趣的是,这个观点也恰好是Test Driven Development(测试驱动开发)的特征之一。

Test Driven Development(TDD)也被认为是Test First Coding(编码前测试)。Test First Coding是一种把测试更提前一步的方法:在你写任何代码之前先写好测试。你可以从http://xprogramming.com/xpmag/testFirstGuidelines.htm下载到一份很好的,简洁的关于TDD的摘要文章,同时下载到一本很好的关于策略的入门书——Kent Beck著作的《Test Driven Development:By Example》(这本书的例子都是用JAVA开发的,但其中代码的可读性是很好的,而且对主题的介绍和说明都做的很好的)。

注:敏捷开发(Agile Development)
最近,单体测试——特别是测绘驱动开发——已经和敏捷开发方法学紧密的联系起来了,比如说极限编程(XP)。极限编程的焦点关注于快速的反复的发步功能性的代码给客户,并把变化的客户需求做为开发过程中的必备部分。下面是一些关于学习敏捷编程的在线资源:
函数性测试
这本书里面的大部分测试例子都是用来测试面对对象的代码,但是所有形式的编程都可以从中得到收获的。单体测试框架,比如说PHPUnits和SimpleTest,也都能很容易的用来测试功能函数的。例如上面的SimpleTest例子,它就是用来测试calculate_sales_tax()函数的。世界各地的程序员们:把单体测试用例放到你的函数库里面吧!

我希望经过上面的讨论后,你也会被带动起来——“测试引导”(Test Infected)!(这个术语,原创于Erich Gamma,详细情况请见文章http://junit.sourceforge.net/doc/testinfected/testing.htm),就象Gamma所写的那样,刚开始你可能会感到测试是很繁琐的,但是当你为你的程序搭建好一个广阔的测试集后,你将你的代码更加自信!

共有0条评论网友评论

当前没有评论!

加载更多

游戏排行榜