2010/07 6
隐藏实现

代理模式(Proxy)和状态模式(State)都提供一个代理类,用来隐藏真正的实现过程。在调用代理类时,他简单的将请求转发给实际类去处理,并返回其结果。这两种设计模式比较相似,代理模式可以看做是一种特殊的状态模式,他只有一个状态。一个观点是将这两者合并,称之为代理模式(Surrogate)。但Proxy这个词汇使用的太久了,概念被固化了。因此,这两个模式并没有被合并。

最开始的想法很简单,通过代理类,以实际类做支持,来完成实际的实现功能。



代理对象被建立后,他可以提供一个应用接口,将所有的调用转发给实际类。

在结构上,代理模式和状态模式很容易区分:代理模式只有一个应用,状态模式则有多个。《设计模式》一书的看法不同:代理模式用来控制对类的访问权限,状态模式这用来在运行过程中动态的改变类。

代理

我们来为上图的代理模式写些代码:

 
#: c04:ProxyDemo.py 
# Simple demonstration of the Proxy pattern. 
 
class Implementation: 
  def f(self):  
    print "Implementation.f()" 
  def g(self):  
    print "Implementation.g()"  
  def h(self):  
    print "Implementation.h()"  
 
class Proxy: 
  def __init__(self):  
    self.__implementation = Implementation()  
  # Pass method calls to the implementation:
  def f(self): self.__implementation.f()  
  def g(self): self.__implementation.g()  
  def h(self): self.__implementation.h()  
 
p = Proxy() 
p.f(); p.g(); p.h() 
#:~ 

Proxy类的接口并不需要和Implementation类的接口保持完全一致,代理可以以任何方式来“代言”实际类。一般来讲,将代理类和实际类保持一致的接口会更为通用和方便,也能够保证实际类完全支持代理类的所有调用。

当然,在python中,我们可以使用内置的代理机制。简化后的代码像这样:

 
#: c04:ProxyDemo2.py 
# Simple demonstration of the Proxy pattern. 
 
class Implementation2: 
  def f(self):  
    print "Implementation.f()" 
  def g(self):  
    print "Implementation.g()"  
  def h(self):  
    print "Implementation.h()"  
 
class Proxy2: 
  def __init__(self):  
    self.__implementation = Implementation2()  
  def __getattr__(self, name): 
    return getattr(self.__implementation, name) 
 
p = Proxy2() 
p.f(); p.g(); p.h(); 
#:~ 

Proxy2使用__getattr__( )方法,这看起来更为优雅,他不依赖于任何的实施细节。


状态模式

和代理模式相比,状态模式中存在多个实现类,并提供了在实现类中动态切换的接口。

 
#: c04:StateDemo.py 
# Simple demonstration of the State pattern. 
 
class State_d: 
  def __init__(self, imp):  
    self.__implementation = imp  
  def changeImp(self, newImp): 
    self.__implementation = newImp 
  # Delegate calls to the implementation: 
  def __getattr__(self, name): 
    return getattr(self.__implementation, name) 
 
class Implementation1: 
  def f(self):  
    print "Fiddle de dum, Fiddle de dee,"  
  def g(self):  
    print "Eric the half a bee."  
  def h(self):  
    print "Ho ho ho, tee hee hee,"  
 
class Implementation2: 
  def f(self):  
    print "We're Knights of the Round Table."  
  def g(self):  
    print "We dance whene'er we're able."  
  def h(self):  
    print "We do routines and chorus scenes"  
 
def run(b): 
  b.f() 
  b.g() 
  b.h() 
  b.g() 
 
b = State_d(Implementation1()) 
run(b) 
b.changeImp(Implementation2()) 
run(b) 
#:~


在程序中,我们先构造了一个实现类,并将其封装到State_d。运行一段时间后,程序又被切换给另一个实现类。


代理模式和状态模式有不同的使用场景。《设计模式》描述了代理模式的主要场景:

1. 远程代理
2. 虚拟代理,提供“惰性类”
3. 保护代理 用于限制客户端对被代理对象的完全访问。
4. 转发 建立代理时,增加一些额外的操作,用来对调用进行跟踪。

在pyhton引用中,使用了不少的保护代理,用来阻止对真实对象的访问。比如,你无法建立使用一个对NULL的引用。


状态机模式

状态模式允许客户端程序在运行过程中修改其实现过程,我们可以在实现过程中,将一个实施自动的转变为另一个实施,这样的实现,我们称之为状态机模式。当前的运行状态代表系统当前的状态,而状态可以由系统自动的切换。最基本的状态机模式是对象状态。

切换系统状态一般可以使用上述模板方法中所介绍的代码。

每一个状态都使用run()接口实现其具体行为,或者你也可以传入一个input参数,以便告诉系统,你应该切换的下一个状态是什么。这两种设计的主要不同点是由谁来决定下一个状态是什么。我们可以传入一个input参数,标示下一个状态,或者,在状态类中定义一个表(或者定一个全局表),定义当前状态的下一个状态是什么。
 
#: c04:statemachine:State.py
# A State has an operation, and can be moved
# into the next State given an Input:
class State:
	def run(self):
		assert 1, "run not implemented"
	def next(self, input):
		assert 1, "next not implemented"
#:~

这个类并不是很完整,但他足以说明状态类的代码结构,如果其子类没有重构其所有方法时,他也可以给出一个简短的错误提示。更简单,我们可以直接定义为:
 
class State: pass

因为如果run或next没有被实现,系统同样也会抛出异常。


状态机保存了状态的轨迹,这些轨迹一般定义在状态机的构造函数中。runAll方法将对所有的input对象实施状态操作及变更。run方法则只执行其特定的对象的状态操作及变更。
 
#: c04:statemachine:StateMachine.py
# Takes a list of Inputs to move from State to
# State using a template method.
class StateMachine:
	def __init__(self, initialState):
		self.currentState = initialState
		self.currentState.run()
	# Template method:
	def runAll(self, inputs):
		for i in inputs:
			print i
			self.currentState = self.currentState.next(i)
			self.currentState.run()
#:~

我将runAll方法也写作成了模板方法同样的形式,尽管这并不是必须的。

目前,状态机模式的基本框架已经完成了。下面我们将使用捕鼠器做为示例来惊醒一个演示(放心,没有哪只老鼠会收到伤害)。我们可以操作捕鼠器的各个状态,老鼠类及其星系将放到mouse这个包中,主要包括老鼠的移动行为,他将作为状态机的输入参数。
 
#: c04:mouse:MouseAction.py
class MouseAction:
	def __init__(self, action):
		self.action = action
	def __str__(self): return self.action

	def __cmp__(self, other):
		return cmp(self.action, other.action)
	# Necessary when __cmp__ or __eq__ is defined
	# in order to make this class usable as a
	# dictionary key:
	def __hash__(self):
		return hash(self.action)

# Static fields; an enumeration of instances:
MouseAction.appears = MouseAction("mouse appears")
MouseAction.runsAway = MouseAction("mouse runs away")
MouseAction.enters = MouseAction("mouse enters trap")
MouseAction.escapes = MouseAction("mouse escapes")
MouseAction.trapped = MouseAction("mouse trapped")
MouseAction.removed = MouseAction("mouse removed")
#:~

我们注意到__cmp__( )被重构了,用来比较两个action对象的值。每一种移动行为都被定义在MouseAction对象中,成为该对象的一个属性。

为此,我们再建立一个测试用例,创建一个老鼠的行为队列:
 
#:! c04:mouse:MouseMoves.txt
mouse appears
mouse runs away
mouse appears
mouse enters trap
mouse escapes
mouse appears
mouse enters trap
mouse trapped
mouse removed
mouse appears
mouse runs away
mouse appears
mouse enters trap
mouse trapped
mouse removed
#:~


有了以上的这些基础,我们现在可以开始编写老鼠夹的代码了。每一个状态子类都定义了run的方法。也有if语句用来选择下一个状态是什么。

 
#: c04:mousetrap1:MouseTrapTest.py 
# State Machine pattern using 'if' statements 
# to determine the next state. 
import string, sys 
sys.path += ['../statemachine', '../mouse'] 
from State import State 
from StateMachine import StateMachine 
from MouseAction import MouseAction 
# A different subclass for each state: 
 
class Waiting(State): 
  def run(self):  
    print "Waiting: Broadcasting cheese smell" 
 
  def next(self, input): 
    if input == MouseAction.appears: 
      return MouseTrap.luring 
    return MouseTrap.waiting 
 
class Luring(State): 
  def run(self): 
    print "Luring: Presenting Cheese, door open" 
 
  def next(self, input): 
    if input == MouseAction.runsAway: 
      return MouseTrap.waiting 
    if input == MouseAction.enters: 
      return MouseTrap.trapping 
    return MouseTrap.luring 
 
class Trapping(State): 
  def run(self): 
    print "Trapping: Closing door" 
 
  def next(self, input): 
    if input == MouseAction.escapes: 
      return MouseTrap.waiting 
    if input == MouseAction.trapped: 
      return MouseTrap.holding 
    return MouseTrap.trapping 
 
class Holding(State): 
  def run(self): 
    print "Holding: Mouse caught"  
  def next(self, input): 
    if input == MouseAction.removed: 
      return MouseTrap.waiting 
    return MouseTrap.holding 
 
class MouseTrap(StateMachine): 
  def __init__(self):  
    # Initial state 
    StateMachine.__init__(self, MouseTrap.waiting) 
 
# Static variable initialization: 
MouseTrap.waiting = Waiting() 
MouseTrap.luring = Luring() 
MouseTrap.trapping = Trapping() 
MouseTrap.holding = Holding() 
 
moves = map(string.strip,  
  open("../mouse/MouseMoves.txt").readlines()) 
MouseTrap().runAll(map(MouseAction, moves)) 
#:~


状态机类的定义了所有可能的状态类型,并建立了初始状态。单元测试程序建立了一个老鼠夹,并运行了我们刚刚建立的老鼠行为队列。

在next()方法中使用if方法是可行的,但是在状态比较多的时候,使用if则会变得很难维护。一个更好的办法是在每个状态内部建立一个表,并基于传入参数进行自动判断。

这乍看起来很容易,我们可以在每一个子状态中建立一个静态表,定义其下一个状态元素。但是,这将造成各个状态类之间的循环依赖关系。为了解决这个问题,我们只能在调用next方法时,才惰性得再加载这个状态表。因为这个原因,next的写法看起来会有些怪。

我们重新定义了上述的State类,并将其命名围StateT,他增加了一个Map并将其初始化为一个二位数组。next方法定义了一个基本实现逻辑,StateT子类的next也必须调用该方法。
 
#: c04:mousetrap2:MouseTrap2Test.py 
# A better mousetrap using tables 
import string, sys 
sys.path += ['../statemachine', '../mouse'] 
from State import State 
from StateMachine import StateMachine 
from MouseAction import MouseAction 
 
class StateT(State): 
  def __init__(self): 
    self.transitions = None 
  def next(self, input): 
    if self.transitions.has_key(input): 
      return self.transitions[input] 
    else: 
      raise "Input not supported for current state" 
 
class Waiting(StateT): 
  def run(self):  
    print "Waiting: Broadcasting cheese smell" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = {  
        MouseAction.appears : MouseTrap.luring  
      } 
    return StateT.next(self, input) 
 
class Luring(StateT): 
  def run(self): 
    print "Luring: Presenting Cheese, door open" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.enters : MouseTrap.trapping, 
        MouseAction.runsAway : MouseTrap.waiting 
      } 
    return StateT.next(self, input) 
 
class Trapping(StateT): 
  def run(self):     print "Trapping: Closing door" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.escapes : MouseTrap.waiting, 
        MouseAction.trapped : MouseTrap.holding 
      } 
    return StateT.next(self, input) 
 
class Holding(StateT): 
  def run(self): 
    print "Holding: Mouse caught" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.removed : MouseTrap.waiting 
      } 
    return StateT.next(self, input) 
 
class MouseTrap(StateMachine): 
  def __init__(self):  
    # Initial state 
    StateMachine.__init__(self, MouseTrap.waiting) 
 
# Static variable initialization: 
MouseTrap.waiting = Waiting() 
MouseTrap.luring = Luring() 
MouseTrap.trapping = Trapping() 
MouseTrap.holding = Holding() 
 
moves = map(string.strip,  
  open("../mouse/MouseMoves.txt").readlines()) 
mouseMoves = map(MouseAction, moves) 
MouseTrap().runAll(mouseMoves) 
#:~


其他的代码也是一样的,不同的只在于StateT的类名和next方法的具体实现上。

当你不得不建立或维护大量的状态时,使用这样的方式无疑是必要的。通过查看状态表,将很容易阅读并理解这些状态。

Defined tags for this entry: , , ,

Posted by rollenc

Last modified on 2010-07-21 11:00
2010/07 3
设计模式能帮助你获得别人的成功经验,减少自己在摸索过程中的错误。 --Mark Johnson.

可以讲,面向对象设计的发展史,就是设计模式的发展史。《设计模式》一书是设计模式发展中的里程碑著作。它描述了23中不同的面向对象解决方案。在这本书中,主要的设计模式都附有示例。Gamma四人组的这本书绝对值得你去阅读。它是面向对象开发人员所必须的了解的。

接下来本书将通过一些实例来介绍设计思想的分析过程,从开始状态,到理性思考,再到分析方法的改进,最终确定一种更合适的设计。这个流程经过了很长时间的提炼,你也可以将他作为一个参考原型,用来分析特殊问题和改进。


什么是设计模式

最开始,你可以将设计模式理解为一种用来组织类结构的聪明的有效的方法,它是许多人的的经验总结和归纳。一般他们都具有很好的的通用性和可扩展性。某一个设计模式可能能够解决你所遇到的问题,但并非和你的逻辑完全一致。

虽然我们称它为“设计模式”,但他和设计并没有太多关系。一个设计模式和传统意义中的分析,设计,实施是不同的。设计模式体现的是一种思维模式,他时常出现在分析阶段或更高级别的设计阶段。但讽刺的是,设计模式通常描诉一些具体实现代码,它和底层设计或实施密切相关(而经常的,你只有在到达这一阶段才会意识到,你需要一个设计模式来帮助你解决问题)

设计模式基本概念和程序设计的基本概念相当,只是在其基础之上,增加了一个抽象层概念而已。 任何的抽象都是建立在具体细节之上的,抽象的动力在于如何封装变化。或者说,当需要修改一处代码时,你不会影响到其他的功能。使用设计模式可以花很少的成本,来可以降低维护成本,也能提升代码的可理解性。

通常,程序设计中最难的问题就是如何编写优雅而易于维护的代码。这样的代码成本我们称它为“线性变化”(这里的线性,是指一种增长曲线,而不是线性队列这一容器类型)。这意味着,找到未来最重要的变化,或者说找到最大的变化成本显得格外重要。一旦你找到了他们,你就需要考虑,如何设计你的代码结构,来减少这些变化风险。

设计模式的目的是在代码中关联变化。通过这一个理解,上面的代码中已经体现了一些设计模式的思想。类继承可以看成是一种设计模式,他允许你在各个子类中编写独立的和父类不同的行为。组合也可以看成是一种设计模式,他允许你动态或静态的改变类中的组合对象行为,就像改变类本身行为一样。

另一种设计模式是迭代器。在Python之初,你就可以隐晦的使用到它,在Python2.2,它作为一种重要的特性被提炼了出来。迭代器隐藏了容器内部的元素,通过迭代来逐个访问容器中的各个元素。同时,你可以写一些通用的代码来实现顺序访问,而不用关心该容器是队列的类型。你的通用方法能够被任何一个提供迭代器的队列调用。


模式分类

当前,大量的设计模式被提了出来,开发人员认为设计模式的就是“好”的,但另一部分人认为这是词汇爆炸,或者说是词汇污染。在反复思考后,我用几个阶段来定义模式:

1. 惯用法: 在不同的语言中,我们通常有一些写代码的惯用习惯,比如在C中对数组的for循环遍历(特别是防止越界的方法)

2. 特殊的设计: 为了解决一些特殊的问题,我们说采用的特殊的处理方式。它可能设计的很巧妙,但它并不通用。

3. 标准设计: 它可以解决某一种问题。这种设计比较通用,而且考虑了重用。

4. 设计模式: 它可以解决某一类相似的问题。在使用标准设计一段时间之后,它才又可能被抽象出来,用来解决这一类的相似问题。

我发现这种透视图的方法很管用,他展示了哪一等级是最合适的。这并不说明,哪一种优于另一种。将所有问题都应用于设计模式是没有意义的,它只会浪费你的时间。设计模式不是被意外发现或研究出来的,而应该建立在对一些巧妙方法的使用经验和总结之上。

还有一些设计模式分类方法,如设计模式分析或设计模式架构进行分类。


设计结构

我在设计模式分类上纠结了很久。我发现,GoF四人组提出的分类很晦涩,而且不是每次都管用。很明显的,构造模式这一分类可以告诉你如何建立一个对象。但它还不够明确,他还是一个设计模式组。它无法清晰的说明:“嗨,毫无疑问,你这里可以使用结构模式”。这样的分类方法,很难让我找到一种解决方案(我承认,我也许错过了某一些细节)。

在这一问题上,一开始我显的十分吃力。首先我发现到GoF设计模式中有一些基础设计模式很相似,而且它们所针对的,也是一些相似的问题。经验告诉我,这些设计模式并没有产生多大的作用。一个有用的方法是分析问题,并找到问题和可能出现问题之间的联系。

后来,我开始收集一些基本的设计结构,并试图找到这些设计结构和设计模式之间的联系,特别是在一些公认的优秀的系统中,他们是怎么设计的。目前我已经总结了一个列表(我以后可能会使用一些其他的方法,目前还并不成熟)

以下是一个草案,它们并不会全部出现在最终终稿中。如果你有任何的建议,或者能够找到他们和设计模式之间的关系,请不吝指教。

  • 封装: 自我控制的一种模型,包含数据和行为
  • 聚合
  • 本地化
  • 分离
  • 隐藏
  • 保护
  • 连接
  • 隔离
  • 行为变化
  • 通知
  • 转化
  • 镜像: “并行的实现方式”
  • 映射 “跟踪变化,但有些许变化” (也可以理解为“代理”的一个变种).


设计准则

当我把我的想法发布到我的新闻列表中时,我收到了不少的有用的反馈,但很多都超出了设计结构所讨论的范围。这让我觉得,一个设计准则列表也是相当有用的。但作用不同,在思考设计时,这些准则用来提出一些疑问,或者说,当作一个核查表。

  • 保持平常,不要让人感到惊讶
  • 能够容易的应用于常用的场景,同时有可能应用于不常见的场景。
  • 一致性 我非常肯定的需要做到这一点,特别是在Python中。随意的规则将打乱程序中的正常秩序,而且会使将死开发效率。效率的降低会随大量规则的引入和成指数级增长。
  • 得墨忒耳定律: "不好和陌生人说话",一个对象只应该访问自己,自己的属性和自己的方法。
  • 减法原则: 一个好的设计完成之后,你应该不能够再从他内部移走什么。
  • 在通用设计之前保持简单: (简单的就是最好的)。一个常见的问题是,一个框架常被设计的很通用,并不针对某一个特定的系统。它增加了大量的不常用,滥用甚至是不会用到的属性,这往往使人感到头晕。大部分开发都是针对特定的系统的,通用性对他们往往没有用。最好的通用型是建立在明确的需求文档之上的。这个规则和“封装并适应变化”又一定冲突。当然,有一条是可以实现的:越简单的解决方法也会越通用。
  • 自反性: 一个抽象一个类,一个类一个抽象。这也被成为同质化
  • 独立,或正交性: 他和隔离,封装和变化相关,也是低耦合高内聚的体现
  • 有且仅有一次:避免复制逻辑代码或结果。
  • 这些想法还在进行头脑风暴中,我希望你在你的分析过程中,能够提出一些有用的想法。其他的想法也在分析过程中作为核查表来使用。



单例

可能单例是最简单的设计模式了。他提供一种方式,用来构建并只构建一个实例。为了实现这一个目的,你需要对对象的构造函数加以调整。一个便捷的方式是使用通过代表的形式,使用递归的方式建立子类:
#: c01:SingletonPattern.py
class OnlyOne:
	class __OnlyOne:
		def __init__(self, arg):
			self.val = arg
		def __str__(self):
			return `self` + self.val
	instance = None
	def __init__(self, arg):
		if not OnlyOne.instance:
			OnlyOne.instance = OnlyOne.__OnlyOne(arg)
		else:
			OnlyOne.instance.val = arg
	def __getattr__(self, name):
		return getattr(self.instance, name)

x = OnlyOne('sausage')
print x
y = OnlyOne('eggs')
print y
z = OnlyOne('spam')
print z
print x
print y
print `x`
print `y`
print `z`
output = '''
<__main__.__OnlyOne instance at 0076B7AC>sausage
<__main__.__OnlyOne instance at 0076B7AC>eggs
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.__OnlyOne instance at 0076B7AC>spam
<__main__.OnlyOne instance at 0076C54C>
<__main__.OnlyOne instance at 0076DAAC>
<__main__.OnlyOne instance at 0076AA3C>
'''
#:~

内联类以双下划线命名,代表私有类,这样用户无法直接访问到他。内联类应该实现所有你需要的功能,就像他不是一个内联类一样。外部封装类负责内联类的构建,第一次调用将建立一个OnlyOne类,它将初始化一个内联类的实例,再次调用他将忽略实例化操作。

访问将通过委托的方式,使用__getattr__()的方法,来将调用委托给单例。在输出中你可以看到多个OnlyOne对象被建立了,但__OnleOne实例只有一个。虽然各个OnlyOne实例不同,但他们都委托给同一个__OnlyOne实例。

上面的方法并没有限制你只能建立一个对象,这也是一种建立共享对象池的技术。然而共享对象池有时候会带来一些问题。你可以使用另一种当时来解决:

注意这里的实现变化,类使用了__new__方法,该方法与Pyhton2.2被引入。

#: c01:NewSingleton.py
class OnlyOne(object):
	class __OnlyOne:
		def __init__(self):
			self.val = None
		def __str__(self):
			return `self` + self.val

	instance = None
	def __new__(cls): # __new__ always a classmethod
		if not OnlyOne.instance:
			OnlyOne.instance = OnlyOne.__OnlyOne()
		return OnlyOne.instance

	def __getattr__(self, name):
		return getattr(self.instance, name)
	def __setattr__(self, name):
		return setattr(self.instance, name)

x = OnlyOne()
x.val = 'sausage'
print x
y = OnlyOne()
y.val = 'eggs'
print y
z = OnlyOne()
z.val = 'spam'
print z
print x
print y
#
output = ''' <__main__.__OnlyOne instance at 0x00798900>sausage <__main__.__OnlyOne instance at 0x00798900>eggs <__main__.__OnlyOne instance at 0x00798900>spam <__main__.__OnlyOne instance at 0x00798900>spam <__main__.__OnlyOne instance at 0x00798900>spam ''' #:~


Alex Martelli发现,有时候我们确实需要建立一个单例,来存储一组状态信息。也就是说你可以建立根据需求,建立任意多个对象,但他们的状态信息都是一样的。他的实现代码如下,使用了__dict__技术来存储一个静态的共享存储区域。
#: c01:BorgSingleton.py
# Alex Martelli's 'Borg'
class Borg:
	_shared_state = {}
	def __init__(self):
		self.__dict__ = self._shared_state

class Singleton(Borg):
	def __init__(self, arg):
		Borg.__init__(self)
		self.val = arg
	def __str__(self): return self.val

x = Singleton('sausage')
print x
y = Singleton('eggs')
print y
z = Singleton('spam')
print z
print x
print y
print `x`
print `y`
print `z`
output = '''
sausage
eggs
spam
spam
spam
<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>
'''
#:~


这和SingletonPattern.py文件的作用是一样的,但显得更加优雅。之前的例子中我们需要些一些代码将单例的行为和外部类关联起来,但这里Borg的代码使用了继承,更方便进行重用。

通过包装或定义元类的方式也可以实现单例。首先我们来看看第一个方式,主类被另一个类所包装,他比较像类装饰模型(装饰将在本书后面继续讨论):
#: c01:SingletonDecorator.py
class SingletonDecorator:
	def __init__(self,klass):
		self.klass = klass
		self.instance = None
	def __call__(self,*args,**kwds):
		if self.instance == None:
			self.instance = self.klass(*args,**kwds)
		return self.instance

class foo: pass

foo = SingletonDecorator(foo)
x=foo()
y=foo()
z=foo()
x.val = 'sausage'
y.val = 'eggs'
z.val = 'spam'
print x.val
print y.val
print z.val
print x is y is z
#:~


第二种方法是使用元类。这一章我还没有完全明白,但他看起来很有趣很强大。(注意,Python2.2改进和简化了元类的语法,因此,以下的列子可能需要改写了。)

#: c01:SingletonMetaClass.py
class SingletonMetaClass(type):
	def __init__(cls,name,bases,dict):
		super(SingletonMetaClass,cls)\
			.__init__(name,bases,dict)
		original_new = cls.__new__

		def my_new(cls,*args,**kwds):
			if cls.instance == None:
				cls.instance = \
					original_new(cls,*args,**kwds)
				return cls.instance
			cls.instance = None
			cls.__new__ = staticmethod(my_new)

class bar(object):
	__metaclass__ = SingletonMetaClass
	def __init__(self,val):
		self.val = val
	def __str__(self):
		return `self` + self.val

x=bar('sausage')
y=bar('eggs')
z=bar('spam')
print x
print y
print z
print x is y is z
#:~


练习:
修改BorgSingleton.py,使其使用__new__方法

Defined tags for this entry: ,

Posted by rollenc

Last modified on 2010-07-07 18:14
2008/12 20
40条PHP代码的优化技巧
40 Tips for optimizing your php Code
原文地址:http://reinholdweber.com/?p=3

这篇文章发布于去年, 不过还有很大的参考意义。 同样也备注一些东西。

1. If a method can be static, declare it static. Speed improvement is by a factor of 4.
如果一个方法可静态化,就对它做静态声明。速率可提升至4倍。

2. echo is faster than print.
echo 比 print 快。

3. Use echo’s multiple parameters instead of string concatenation.
使用echo的多重参数代替字符串连接。
实例:( echo $a1, $a2; )

4. Set the maxvalue for your for-loops before and not in the loop.
在执行for循环之前确定最大循环数,不要每循环一次都计算最大值。
实例: for($i=0,$i
5. Unset your variables to free memory, especially large arrays.
注销那些不用的变量尤其是大数组,以便释放内存。

6. Avoid magic like __get, __set, __autoload
尽量避免使用__get,__set,__autoload。
注:这一个问题在php5.2版本中得到了比较好的优化。

7. require_once() is expensive
require_once()代价昂贵。

8. Use full paths in includes and requires, less time spent on resolving the OS paths.
在包含文件时使用完整路径,解析操作系统路径所需的时间会更少。
实例: require dirname(__FILE__) . "/../config.php";

9. If you need to find out the time when the script started executing, $_SERVER[’REQUEST_TIME’] is preferred to time()
如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。

10. See if you can use strncasecmp, strpbrk and stripos instead of regex.
检查是否能用strncasecmp,strpbrk,stripos函数代替正则表达式完成相同功能。

11. str_replace is faster than preg_replace, but strtr is faster than str_replace by a factor of 4.
str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。

12. If the function, such as string replacement function, accepts both arrays and single characters as arguments, and if your argument list is not too long, consider writing a few redundant replacement statements, passing one character at a time, instead of one line of code that accepts arrays as search and replace arguments.
如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行 代码接受数组作为查询和替换的参数。

13. It’s better to use select statements than multi if, else if, statements.
使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。

14. Error suppression with @ is very slow.
用@屏蔽错误消息的做法非常低效。

15. Turn on apache’s mod_deflate
打开apache的mod_deflate模块。

16. Close your database connections when you’re done with them.
数据库连接当使用完毕时应关掉。

17. $row[’id’] is 7 times faster than $row[id].
$row[‘id’]的效率是$row[id]的7倍。

18. Error messages are expensive.
错误消息代价昂贵。

19. Do not use functions inside of for loop, such as for ($x=0; $x < count($array); $x) The count() function gets called each time.
尽量不要在for循环中使用函数,比如for ($x=0; $x < count($array); $x)每循环一次都会调用count()函数。

20. Incrementing a local variable in a method is the fastest. Nearly the same as calling a local variable in a function.
在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。

21. Incrementing a global variable is 2 times slow than a local var.
递增一个全局变量要比递增一个局部变量慢2倍。

22. Incrementing an object property (eg. $this->prop++) is 3 times slower than a local variable.
递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。

23. Incrementing an undefined local variable is 9-10 times slower than a pre-initialized one.
递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。

24. Just declaring a global variable without using it in a function also slows things down (by about the same amount as incrementing a local var). PHP probably does a check to see if the global exists.
仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP大概会检查看是否存在全局变量。

25. Method invocation appears to be independent of the number of methods defined in the class because I added 10 more methods to the test class (before and after the test method) with no change in performance.
方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了10个方法,但性能上没有变化。

26. Methods in derived classes run faster than ones defined in the base class.
派生类中的方法运行起来要快于在基类中定义的同样的方法。

27. A function call with one parameter and an empty function body takes about the same time as doing 7-8 $localvar++ operations. A similar method call is of course about 15 $localvar++ operations.
调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作。类似的方法调用所花费的时间接近于15次的局部变量递增操作。

28. Surrounding your string by ‘ instead of " will make things interpret a little faster since php looks for variables inside "…" but not inside ‘…’. Of course you can only do this when you don’t need to have variables in the string.
用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会。当然,只有当你不需要在字符串中包含变 量时才可以这么做。
注: 实际上,不要注意这么多。 这两者并没有明显差别。0.1%不到。

29. When echoing strings it’s faster to separate them by comma instead of dot. Note: This only works with echo, which is a function that can take several strings as arguments.
输出多个字符串时,用逗号代替句点来分隔字符串,速度更快。注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”
注:重复强调第3条?

30. A PHP script will be served at least 2-10 times slower than a static HTML page by Apache. Try to use more static HTML pages and fewer scripts. Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。

31. Your PHP scripts are recompiled every time unless the scripts are cached. Install a PHP caching product to typically increase performance by 25-100% by removing compile times.
除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。

32. Cache as much as possible. Use memcached - memcached is a high-performance memory object caching system intended to speed up dynamic web applications by alleviating database load. OP code caches are useful so that your script does not have to be compiled on every request.
尽量做缓存,可使用memcached。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。运算码 (OP code)缓存也很有用,使得脚本不必为每个请求做重新编译。

33. When working with strings and you need to check that the string is either of a certain length you’d understandably would want to use the strlen() function. This function is pretty quick since it’s operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using an isset() trick.
当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数。此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步 骤,如字母小写化(译注:指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。

Ex.(举例如下)
if (strlen($foo) < 5) { echo "Foo is too short"; }
vs.(与下面的技巧做比较)
if (!isset($foo{5})) { echo "Foo is too short"; }

Calling isset() happens to be faster then strlen() because unlike strlen(), isset() is a language construct and not a function meaning that it’s execution does not require function lookups and lowercase. This means you have virtually no overhead on top of the actual code that determines the string’s length.
调用isset()恰巧比strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是 说,实际上在检验字符串长度的顶层代码中你没有花太多开销。

34. When incrementing or decrementing the value of the variable $i++ happens to be a tad slower then ++$i. This is something PHP specific and does not apply to other languages, so don’t go modifying your C or Java code thinking it’ll suddenly become faster, it won’t. ++$i happens to be faster in PHP because instead of 4 opcodes used for $i++ you only need 3. Post incrementation actually causes in the creation of a temporary var that is then incremented. While pre-incrementation increases the original value directly. This is one of the optimization that opcode optimized like Zend’s PHP optimizer. It is still a good idea to keep in mind since not all opcode optimizers perform this optimization and there are plenty of ISPs and servers running without an opcode optimizer.
当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言,所以请不要修改你的C或Java代码并指望它 们能立即变快,没用的。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个临时变量,这个临时变 量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。牢记这个优化处理不失为一个好主意,因为并不 是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提供商(ISPs)和服务器。

35. Not everything has to be OOP, often it is too much overhead, each method and object call consumes a lot of memory.
并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。

36. Do not implement every data structure as a class, arrays are useful, too.
并非要用类实现所有的数据结构,数组也很有用。

37. Don’t split methods too much, think, which code you will really re-use.
不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?

38. You can always split the code of a method later, when needed.
当你需要时,你总能把代码分解成方法。

39. Make use of the countless predefined functions.
尽量采用大量的PHP内置函数。

40. If you have very time consuming functions in your code, consider writing them as C extensions.
如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。

41. Profile your code. A profiler shows you, which parts of your code consumes how many time. The Xdebug debugger already contains a profiler. Profiling shows you the bottlenecks in overview.
评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码 的瓶颈。

42. mod_gzip which is available as an Apache module compresses your data on the fly and can reduce the data to transfer up to 80%.
mod_gzip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。、
注: 重复第15条, mod_deflate和mod_zip是同样的东西

43. Excellent Article (http://phplens.com/lens/php-book/optimizing-debugging-php.php)about optimizing php by John Lim
另一篇优化PHP的精彩文章,由John Lim撰写。

在本blog中, 也有其他的一些优化建议。不免有重复的地方。
这也是优化所需要的。 代码的优化必须时刻提醒,时刻牢记。
强化为一种习惯,固有的思维,在日常的开发中定型下来。
PHP优化技巧 - PHP Optimization Tricks
优化php脚本 翻译

Defined tags for this entry: ,

Posted by rollenc

Last modified on 2008-12-20 23:15
2006/10 29
Test First Guidelines
Sean Shubin
01/25/2002
Sean Shubin has begun using Test-First development. He wanted to share these ideas and guidelines with us.

Contents:

This document is my attempt to filter out guidelines about Test First Design from the XP series of books. Thanks to Denise Phillips and Ron Jeffries for their input. It is not my intent to justify Test First Design, I just want summarize what martin Fowler's Refactoring and the XP texts have to say on the subject. If you see anything that you think contradicts these sources, please e-mail me with the book and page number. I am still working on samples for each of these guidelines. If you find any of them completely confusing, send me an e-mail to let me know which ones I need to work on. Send e-mail to Sean Shubin, (remove the "nospam" portion of the e-mail address).

The tests should drive you to write the code, the reason you write code is to get a test to succeed, and you should only write the minimal code to do so. Note that test-first-design is more than just unit testing. Unit testing by itself does not change the design of the code. In addition to documenting how code should be used, test-first-design helps you keep the design simple right from the start, and keeps the design easy to change.

As project complexity grows, you may notice that writing automated tests gets harder to do. This is your early warning system of overcomplicated design. Simplify the design until tests become easy to write again, and maintain this simplicity over the course of the project.
Guidelines for test first design:

* The name of the test should describe the requirement of the code
* There should be at least one test for each requirement of the code. Each possible path through of the code is a different requirement
* Only write the simplest possible code to get the test to pass, if you know this code to be incomplete, write another test that demonstrates what else the code needs to do
* A test should be similar to sample code, in that it should be clear to someone unfamiliar with the code as to how the code is intended to be used
* If a test seems too large, see if you can break it down into smaller tests
* If you seem to be writing a lot of code for one little test, see if there are other related tests you could write first, that would not require as much code
* Test the goal of the code, not the implementation
* One test/code/simplify cycle at a time. Do not write a bunch of tests, and try to get them working all at once
* Keep writing tests that could show if your code is broken, until you run out of things that could possibly break
* When choosing an implementation, be sure to choose the simplest implementation that could possibly work
* If you are unsure about a piece of code, add a test you think might break it
* A test is one specific case, for which there is a known answer
* If all of the tests succeed, but the program doesn't work, add a test
* Tests should be as small as possible, before testing a requirement that depends on multiple things working, write a test for each thing it depends
* Tests should not take longer than a day to get working, typical test/code/simplify cycles take around 10 minutes
* Don't test every single combination of inputs. Do test enough combinations of inputs to give you confidence that the any code that passes the test suite will work with every single combination of inputs
* Do not write a single line of code that doesn't help a failing test succeed. (Clarification for GUI's, some aspects of GUI's are impossible to test automatically, so it will have to be an acceptance test that drives you two write some GUI code. Use automated testing whenever possible)
* Do not fix a bug until you have written a test that demonstrates the bug

What is the simplest code? (Paraphrased from the "Extreme Programming" series of books)

* All of the tests run
* There is no duplicate code (any given code segment or structural pattern should appear "once and only once")
* Clarity. The code and tests communicate the intent as clearly as possible
* The code is minimal (no classes or methods unnecessary to get the tests to pass)

The Test-Code-Simplify cycle (Quoted verbatim from "Extreme Programming Applied", p159)

* Write a single test
* Compile it. It shouldn't compile, because you haven't written the implementation code it calls
* Implement just enough code to get the test to compile
* Run the test and see it fail
* Implement just enough code to get the test to pass
* Run the test and see it pass
* Refactor for clarity and "once and only once"
* Repeat

Online Examples of Test First Design:

Roman numeral converter example

http://www.differentpla.net/~roger/devel/xp/test_first/

Sample application that calculates bowling score

http://www.objectmentor.com/publications/xpepisode.htm

Books:

"Refactoring" and "Design Patterns" tell you how to maintain a simple design.

The "Extreme Programming" series explains test-first design in detail, and was the primary resource I used to put together this set of guidelines.

来源:http://xprogramming.com/xpmag/testFirstGuidelines.htm
最近感觉想翻译的东西太多了,有一些就纯粹转载了。有时间再翻译。
Defined tags for this entry: , , ,

Posted by rollenc

Not modified
2006/10 27
From: http://www.devshed.com/c/a/PHP/Unit-Testing/
Unit Testing
单元测试

Having a formalized unit testing infrastructure for your projects will save you time in the long run, especially when looking for bugs in code. This article introduces you to setting up a unit testing framework. It is excerpted from chapter 6 of the book Advanced PHP Programming, written by George Schlossnagle (Sams; ISBN: 0672325616).
在正式的单元测试编程的基础上,你可以给你的程序开发节省下不少时间,特别是查找和修复BUG时的时间。本文介绍了如何建立一个测试框架。本文引于《告急PHP编程》第六章,作者:George Schlossnagle (Sams; ISBN: 0672325616).

Testing and engineering are inextricably tied forever.
测试和开发紧密联系

All code is tested at some point—perhaps during its implementation, during a dedicated testing phase, or when it goes live. Any developer who has launched broken code live knows that it is easier to test and debug code during development than after it goes into production.
所有的代码都在同一时候被测试,很可能是在程序执行的时候,或者是在专门的测试阶段,或者在程序发布后。程序员们都知道,在开发阶段中,在开发阶段修复程序BUG比发布后再修复要容易。

Developers give many excuses for not testing code until it is too late. These are some of the popular ones:
程序员给没有经过测试的代码找了很多的理由,常见的有:
* The project is too rushed. 程序开发过于匆忙。
* My code always works the first time. 程序在开始是可以正常运行的。
* The code works on my machine. 这些代码在我的机器上是可以正常运行的。

Let's explore these excuses. First, projects are rushed because productivity lags. Productivity is directly proportional to the amount of debugging required to make code stable and working. Unfortunately, testing early and testing late are not equal cost operations. The problem is two-fold:
我们来研究这几个理由。首先,工程匆忙是因为效率不佳,对代码的稳定性和正确性的调试直接影响到开发效率。不幸的是。在不同时候对代码进行调试所需要的花费是不一样的。从下面几点来说明:

* In a large code base that does not have a formalized testing infrastructure, it is hard to find the root cause of a bug. It's a needle-in-a-haystack problem. Finding a bug in a 10-line program is easy. Finding a bug in 10,000 lines of included code is a tremendous effort.
* 项目中没有基于一个正式的测试基础,这导致很难查找到BUG的来源。在10行代码中找出错误是简单的,但是在10,000行代码中,而且包含很多文件包含关系的代码中找出一个错误则是一场噩梦。

* As the code base grows, so do the number of dependencies between components. Seemingly innocuous changes to a "core" library—whether adding additional features or simply fixing a bug—may unintentionally break other portions of the application. This is known as refactoring. As the size and complexity of software grow, it becomes increasingly difficult to make these sorts of changes without incurring time costs and introducing new bugs.
*
* All software has bugs. Any developer who claims that his or her software is always bug-free is living in a fantasy world.
* System setups are all slightly different, often in ways that are hard to anticipate. Differing versions of PHP, differing versions of libraries, and different file system layouts are just a few of the factors that can cause code that runs perfectly on one machine to inexplicably fail on another.
Defined tags for this entry: , ,

Posted by rollenc

Not modified
2006/10 6
原地址:http://www.phpbuilder.com/columns/kassemi20050613.php3?aid=923


XMLHttpRequest and AJAX for PHP programmers, Part 2

PHP程序员需要了解的XMLHttpRequest和AJAX 第二部分

James Kassemi

翻译:rollenc


Let's Jump Right In!

我们继续!


This week's article takes off where last week's tutorial left off--we're about to jump into the creation of the PHP backend! If you haven't read the previous article, we recommend that you begin there before moving forward so you don't miss anything.

通过上周的学习,本周文章开始正式开始了。我们准备开始编辑PHP后台!如果你还没有阅读上一篇文章,我们建议你先阅读它,不要拉下必要的知识。


Calling the Functions

调用函数


Now, how is the products.html page supposed to call these functions? We want to be able to let the user select an item, and then load the values based on that selection. Using a "Search" or "Go" button is a little archaic, so let's use the onChange method, which we can use to call javascript functions whenever the user changes the selected item. Change the <select> tag in products.html to look like this:

那现在,products.html如何来调用这些函数呢 ?我们需要在用户选择条目后,加载与这个条目相关的信息。使用“搜索”或“go”显得有点老套,所以我们使用了onChange方法(事件),当用户改变选择的条目时,我们通过JAVASCRIPT来调用。将products.html文件中的<select>标签修改成这样:




                    
                    

                    
                
            
        
^ Please select a category from above.


Source for Included File: internal_request.js
products.html的源代码:
/* The following function creates an XMLHttpRequest object... */
function createRequestObject(){
    var request_o; //declare the variable to hold the object.
    var browser = navigator.appName; //find the browser name
    if(browser == "Microsoft Internet Explorer"){
        /* Create the object using MSIE's method */
        request_o = new ActiveXObject("Microsoft.XMLHTTP");
    }else{
        /* Create the object using other browser's method */
        request_o = new XMLHttpRequest();
    }
    return request_o; //return the object
}

/* You can get more specific with version information by using
    parseInt(navigator.appVersion)
    Which will extract an integer value containing the version
    of the browser being used.
*/
/* The variable http will hold our new XMLHttpRequest object. */
var http = createRequestObject();

/* Function called to get the product categories list */
function getProducts(){
    /* Create the request. The first argument to the open function is the method (POST/GET),
        and the second argument is the url...
        document contains references to all items on the page
        We can reference document.form_category_select.select_category_select and we will         
        be referencing the dropdown list. The selectedIndex property will give us the
        index of the selected item.
    */
    http.open('get', 'internal_request.php?action=get_products&id='
            + document.form_category_select.select_category_select.selectedIndex);
    /* Define a function to call once a response has been received. This will be our
        handleProductCategories function that we define below. */
    http.onreadystatechange = handleProducts;
    /* Send the data. We use something other than null when we are sending using the POST
        method. */
    http.send(null);
}

/* Function called to handle the list that was returned from the internal_request.php file.. */
function handleProducts(){
    /* Make sure that the transaction has finished. The XMLHttpRequest object
        has a property called readyState with several states:
        0: Uninitialized
        1: Loading
        2: Loaded
        3: Interactive
        4: Finished */
    if(http.readyState == 4){ //Finished loading the response
        /* We have got the response from the server-side script,
            let's see just what it was. using the responseText property of
            the XMLHttpRequest object. */
        var response = http.responseText;
        /* And now we want to change the product_categories 
content. we do this using an ability to get/change the content of a page element that we can find: innerHTML. */ document.getElementById('product_cage').innerHTML = response; } }


Source for Included File: internal_request.php
internal_request.php的源代码:

                    
  • CDex
  • CoolEdit
  • Winamp
  • XMMS
  • '; break; case 1: //Games echo '
    • Blackjack
    • Calculatron
    • Hold\'em
    • Minesweeper
    • Tetris
    '; break; case 2: //Internet echo '
    • Epiphany
    • Internet Explorer
    • Mozilla
    • Netscape
    • Opera
    • Safari
    '; break; default: echo 'You didn\'t select an item from above!'; break; } } ?>
    Defined tags for this entry: , , , , , ,

    Posted by rollenc

    Last modified on 2007-01-12 19:06
    2006/10 5
    Link:http://www.phpbuilder.com/columns/kassemi20050606.php3?aid=917

    XMLHttpRequest and AJAX for PHP programmers
    php程序中的XMLHttpRequest 和 AJAX

    Introduction:
    前言:

    Although the concept isn't entirely new, XMLHttpRequest technology is implemented on more sites now than ever. Compatibility is no longer an issue (IE, Mozilla and Opera all support it), and the benefits to using it are amazing. There are too many PHP programmers avoiding any work with javascript beyond simple form validation, and for good reason. It's difficult to keep several languages proficiently under your belt. But using the XMLHttpRequest object is not as hard as everybody thinks, and you don't need to buy and memorize another reference manual.
    虽然 XMLHttpRequest概念并不是新的,但最近在网站上的应用远多于以前。兼容性也不存在问题了(IE, Mozilla and Opera都支持),其应用有时也是令人惊奇的。很多PHPer通过这一方式来避免使用javascript来验证简单的表单输入。同时使用几种语言是很困难的。而使用XMLHttpRequest并不象人们想象的那么难,你也不需要去为此买一本参看书或者记忆一本参考手册。

    Let's Get To It!
    开始!

    Asynchronous JavaScript and XML, or AJAX is a method of sending and receiving data (usually XML) from a server-side application through javascript. Since javascript offers the ability to change the contents of a web page on-the-fly, this technique allows web programmers to venture closer to programming truly interactive web applications similar to those built with Java and ActiveX.
    Ajax(Asynchronous JavaScript and XML)是通过javascript去请求和获取一个服务器端操作。由于这种功能,javascript可以改变网页中的内容。这种技术使得web程序员可以像Java和ActiveX程序一样进行交互设计。

    As PHP developers, it might seem tempting to avoid the use of Javascript and leave it to the designer. After all, we aren't usually programming the UI, but the processing components required by the UI. The distinction between the two is disappearing. Here's a simple diagram that demonstrates just how AJAX works:
    PHP开发人员更倾向于不使用javascript,而把他交给前台设计人员来处理。毕竟,我们不常对UI进行编码。但AJAX过程中的一些部分是在UI中的。这两者没有什么区别。这里是演示AJAX如何运行的一个简单图表。

    点击查看原始尺寸

    If you're working for a small or medium sized company interested in implementing AJAX solutions, you might end up responsible for figuring out how.
    如果你任职于一家中小型公司,仅仅对AJAX的解决方案感兴趣的话,你可能仅仅能够理解到AJAX是如何工作的而已。

    XMLHttpRequest objects can be a simple way of getting data to and from a PHP application while keeping your client right at home on the same page. Our example today will allow a user to select a specific piece of software that your company makes. We will show a selection box with several categories. When a user selects a category, a request is sent to a PHP application which returns a list of applicable software. The information is used to generate a list of the results underneath the selection box. Since the information is not loaded with the initial page, your company saves bandwidth, and because the user doesn't have to bounce from page to page for results, he will find your company's page more inviting and faster to load.
    XMLHttpRequest对象在客户端的同一页面可以简单的从获得数据后从PHP程序结果。这里的例子允许用户选择贵公司的程序模块。我们通过一个选择框来实现。当用户选择一个种类时,就会发送一个请求到PHP程序,以得到应用程序的列表。这些生成的列表信息属于用户所选择类别之下。由于信息不需要在首次全部载入,这为贵公司省下了流量。对用户来讲,他们不需要在页面间跳来跳去,他们对觉得贵网站很有吸引力,加载也很快。

    Javascript for PHP Programmers
    PHPer需要的Javascript

    Since we'll be working with Javascript, it's good to get a basic tutorial given a background in PHP.
    因为我们需要编写Javascript,先通过PHP背景来了解一下Javascript语言的基础。

    Variables:
    变量:

    Variables in javascript are declared in much the same was as in PHP. To declare a variable in javascript, use the following:
    Javascript中的变量声明与PHP中的很相似。在Javascript中声明一个变量如下:

    var varname = varvalue; //Declaring your variable is not mandatory, but good practice.
    var varname = varvalue; //变量声明不是必须的,但这是一个好的习惯
    


    Control Structures:
    控制结构:
    PHP and javascript have a very similar way of using these as well... You can use if/else statements, switch statements, for and while loops and nested loops all with PHP syntax. If/elseif/else statements are a little different, but not much:
    PHP和Javascript具有非常相似的语法结构,你可以使用PHP语法中的if/else句型,swicth句型,for和while循环和嵌套循环。if/elseif/else句型有一点点区别。

    var variable1 = 1; //Declare a variable.
    if(variable1 == 1){
        // Increment variable value by 1
        variable1++;
        /* The following brings up a message box, a handy way of checking variable values as you
                        go. Be careful not to use these in loops that are too long. */
        alert(variable1);
    }else if(variable1 == 2){ //Elseif's in javascript require a space between the else and if.javascript中Elseif一定需要一个空格
        for(i=0;i < 20;i++){ //For loop.
            variable1++;
        }
            switch(variable1){ //Switch conditional
                   case 22:
                       alert("variable 1 has value of 22!");
                       break;
                   default:
                       alert("didn't have value of 22!");
                break;
        }
    }else{
        alert('The final else statement');
    }
    


    Functions
    函数
    To declare a function in javascript, use the following:
    在javascript中声明一个函数的方式如下:

    function functioname(argument1, argument2, argument3){
        /* Arguments not provided will be null */
        if(argument3 == null){
            return argument1 + argument2
        }else{
            return argument1 + argument2 + argument3
        }
    }
    
    /* And call it with */
    alert(functionname(1, 2)); //returns 3
    /* or */
    alert(functionname(1, 2, 3)); //returns 6
    


    Other than the difference with default values for optional arguments, you should assign a default when a passed argument has a null value.
    和PHP不同的是可选参数,你需要比较它是否是空的,再赋给它一个默认值。

    Using Javascript in a Document:
    在文档中使用Javascript

    Javascript can be included in your document in two ways, similar to CSS inclusion:
    Javascript可以通过两种方式在文档中使用,与CSS很像:

    
    
    
    
    


    The first way is to link to the file directly, and the second is to include it on the page itself. If your code is getting long and is used on multiple pages, it is preferable to use the first method, as the browser will cache the file for future use. And that's all you need to know about Javascript to continue.
    第一种方式是从文件中引用,第二种是在文档自身调用。如果你的代码需要很长后者需要在多个页面中使用的话,最好使用地一种方法,浏览器会缓存这些文件以便将来之用。现在你需要的是继续看下文。

    Developing the Initial Page
    第一个页面
    Below we're going to be creating the page the user loads to view your companies products.
    以下我们准备建立一个用户调用和查看公司产品的页面。

    
        
            CompanyXYZ Software
            
        
        
            
    ^ Please select a category from above.


    The above HTML first links to a javascript file, internal_request.js, and displays a page with two <div> tags, the second of which is where we'll be displaying our data. Go ahead and copy the above code into a file called products.html.
    以上的HTML文件第一行指向了一个Javascript文件,internal_request.js,页面通过两个DIV标签来显示,我们会在第二个标签里显示我们的内容。复制以上代码到一个文件中,保存为products.html

    Creating the XMLHttpRequest Object
    建立XMLHttpRequest对象

    The XMLHttpRequest object works differently in Internet Explorer and Mozilla-like browsers. To create an XMLHttpRequest object in IE, the following can be used:
    XMLHttpRequest对象调用方式在Internet Explorer和Mozilla的浏览器不同,在IE中可以像如下方式使用:
    var request_o = new ActiveXObject("Microsoft.XMLHTTP");
    


    And the following works for supporting browsers other than IE:
    这种方式对其他IE外的浏览器使用:
    var request_o = new XMLHttpRequest();
    


    Determining what browser you are working with and creating the appropriate object is simple:
    兼容代码如下:
    /* The following function creates an XMLHttpRequest object... */
    
    function createRequestObject(){
        var request_o; //declare the variable to hold the object.
        var browser = navigator.appName; //find the browser name
        if(browser == "Microsoft Internet Explorer"){
            /* Create the object using MSIE's method */
            request_o = new ActiveXObject("Microsoft.XMLHTTP");
        }else{
            /* Create the object using other browser's method */
            request_o = new XMLHttpRequest();
        }
        return request_o; //return the object
    }
    
    /* You can get more specific with version information by using
        parseInt(navigator.appVersion)
        Which will extract an integer value containing the version
        of the browser being used.
    */
    


    Copy the above code into a file called internal_request.js, located in the same directory as the products.html file.
    复制以上代码,保存到文件internal_request.js,与products.html放在同意目录。

    We now have a function that will create an XMLHttpRequest object in internal_request.js, and we have an HTML file that calls upon the code in internal_request.js. Remember how we left the product selection <div> in products.html blank? Let's write the code that utilizes our createRequestObject function to get the list of products.
    我们现在已经有了一个可以创建XMLHttpRequest对象的internal_request.js和调用这个JS的HTML文件,注意我们把DIV中的列表留空了。我们来写一段代码利用createRequestObject函数来的得到公司产品列表。

    /* The variable http will hold our new XMLHttpRequest object. */
    var http = createRequestObject();
    
    /* Function called to get the product categories list */
    function getProducts(){
        /* Create the request. The first argument to the open function is the method (POST/GET),
            and the second argument is the url...
            document contains references to all items on the page
            We can reference document.form_category_select.select_category_select and we will
            be referencing the dropdown list. The selectedIndex property will give us the
            index of the selected item.
        */
        http.open('get', 'internal_request.php?action=get_products&id='
                + document.form_category_select.select_category_select.selectedIndex);
        /* Define a function to call once a response has been received. This will be our
            handleProductCategories function that we define below. */
        http.onreadystatechange = handleProducts;
        /* Send the data. We use something other than null when we are sending using the POST
            method. */
        http.send(null);
    }
    
    /* Function called to handle the list that was returned from the internal_request.php file.. */
    function handleProducts(){
        /* Make sure that the transaction has finished. The XMLHttpRequest object
            has a property called readyState with several states:
            0: Uninitialized
            1: Loading
            2: Loaded
            3: Interactive
            4: Finished */
        if(http.readyState == 4){ //Finished loading the response
            /* We have got the response from the server-side script,
                let's see just what it was. using the responseText property of
                the XMLHttpRequest object. */
            var response = http.responseText;
            /* And now we want to change the product_categories 
    content. we do this using an ability to get/change the content of a page element that we can find: innerHTML. */ document.getElementById('product_cage').innerHTML = response; } }


    The above code should be appended to what you already have in the internal_request.js file.
    以上的代码依赖于internal_request.js文件。

    Conclusion
    结论

    We'll followup with the rest of this informative article next week, so be sure to visit us again for the conclusion! btw, don't miss the important Quick Tips listed below!
    剩下的内容我们将在下周进行介绍,记得对本站的继续关注!顺便提一下,不要忘记以下的快速提示。

    Quick Tip 1: (Using the POST method instead of GET):
    The following will send the request to the PHP file using the POST method:
    快速提示1:使用Post方法替代GET
    下面是使用POST方法发送请求的例子:
        http.abort;
        http.open('post',  'back_end.php');
        http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        http.send('arg1=val1&arg2=val2&arg3=val3');
    


    Quick Tip 2: (Parsing XML results):
    快速提示2:(解析XML结果):

    If you're receiving XML instead plain-text results from your processing PHP script, you can use the javascript DOM to parse them. Suppose you receive the following:
    如果你从PHP程序返回XML结果而非纯文本结果,你可以使用Javascript DOM来解析他们。假设你得到了以下结果:
        
            
                EeasySMS
                2.2
            
            
                BabyMon
                1.2
            
        
    


    To receive a DOM compatible response from our XMLHttpRequest object, instead of using the responseText property, substitute the responseXML property. In this case we'll refer to it by assigning it the variable XMLResponse.
    为了使DOM模型可用,需要使用responseXML来替代responseText属性。这是,我们把结果赋给变量XMLResponse。

    The product_list element contains two elements of interest: product elements. In order to get to these, we can use the following:
    product_list元素包含有两个product元素,为了获得他们,可以使用以下代码:
    /* Reference the product_list element to the variable product_list */
    product_list = XMLReponse.getElementByTagName('product_id');
    
    /* By substituting product_list for XMLResponse, we will be searching
        only the product_list element, not the entire response
        We also use getElementsByTagName, not getElementByTagName,
        since we are interested in all of the results, not just one. */
    
    product_id = XMLResponse.getElementsByTagName('product');
    
    /* getElementsByTagName produces an array, which we can access  like this:
        product_id[n], the same way we access an array item in PHP.
        Let's get the id attribute from the product elements like this: */
    
    for(i=0; i < product_id.length; i++){ //length is the same as count($array)
        id = product_id[i].getAttribute('id') //Grabs the id attribute.
        /* To get the text from within a text node, we use firstChild.data
            for the corresponding element. */
        name = product_id[i].getElementByTagName('name').firstChild.data;
        version = product_id[i].getElementByTagName('version').firstChild.data;
    }
    


    This may seem like a bit to work with at first, but with a little work, you can get what you want to work. If you want to avoid this, you could use the responseText method, parsing the XML in PHP before sending it to the XMLHttpRequest object.
    你可以少量的工作来使的这段代码来实现你自己的功能。如果你不想使用他,你可以在返回XMLHttpRequest对象前使用PHP来解析XML文档而使用responseText属性。

    Quick Tip 3: (Relevant Links):
    快速参考3:(相关参考)
    * Microsoft XMLHttpRequest Documentation
    * XMLHttpRequest @ Apple Developer Connection
    * The DOM Model in Mozilla

    About the Author:
    关于作者:
    James Kassemi lives in Albuquerque, New Mexico. He works mainly with PHP, programming for a variety of clients in the southwest.

    翻译:rollenc
    Defined tags for this entry: , , , , , ,

    Posted by rollenc

    Last modified on 2007-01-12 19:09
    2006/09 9



    英文中文




    Optimizing PHP Scripts
    By Leon Atkinson

    June 20, 2001

    One reason I like PHP is that it allows the freedom to quickly create Web applications without worrying about following all the rules of proper design. When it comes to rapid prototyping, PHP shines. With this power comes the responsibility to write clean code when it's time to write longer-lasting code. Sticking to a style guide helps you write understandable programs, but eventually you will write code that doesn't execute fast enough.

    Optimization is the process of fine-tuning a program to increase speed or reduce memory usage. Memory usage is not as important as it once was, because memory is relatively inexpensive. However, shorter execution times are always desirable.

    There are many tips for writing efficient programs, and I can hardly discuss them all here. And anyway, that would be like giving you a fish instead of teaching you to catch your own. This month, I will review the techniques I use for speeding up my PHP scripts.

    When to optimize

    Before you write a program, commit yourself to writing clearly at the expense of performance. Follow coding conventions, such as using mysql_fetch_row instead of mysql_result. But keep in mind that programming time is expensive, especially when programmers must struggle to understand code. The simplest solution is usually best.

    When you finish a program, consider whether its performance is adequate. If your project benefits from a formal requirements specification, refer to any performance constraints. It's not unusual to include maximum page load times for Web applications. Many factors affect the time between clicking a link and viewing a complete page. Be sure to eliminate factors you cannot control, such as the speed of the network.

    If you determine that your program needs optimization, consider upgrading the hardware first. This may be the least expensive alternative. In 1965, Gordon Moore observed that computing power doubled every 18 months. It's called Moore's Law. Despite the steep increase in power, the cost of computing power drops with time. For example, despite CPU clock speeds doubling, their cost remains relatively stable. Upgrading your server is likely less expensive than hiring programmers to optimize the code.

    After upgrading hardware, consider upgrading the software supporting your program. Start with the operating system. Linux and BSD Unix have the reputation of squeezing more performance out of older hardware, and they may outperform commercial operating systems, especially if you factor in server crashes.

    If your program uses a database, consider the differences between relational databases. If you can do without stored procedures and sub-queries, MySQL may offer a significant performance enhancement over other database servers. Check out the benchmarks provided on their Web site. Also, consider giving your database server more memory.

    Two Zend products can help speed execution times of PHP programs. The first is the Zend Optimizer. This optimizes PHP code as it passes through the Zend Engine. It can run PHP programs 40% to 100% faster than without it. Like PHP, the Zend Optimizer is free. The next product to consider is the Zend Cache. It provides even more performance over the optimizer by keeping compiled code in memory. Some users have experienced 300% improvements. Contact Zend to purchase the Zend Cache.
    Measuring performance

    Before you can begin optimizing, you must be able to measure performance. The two tools I'll discuss are inserting HTML comments and using Apache's ApacheBench utility. PHP applications run on a Web server, but the overhead added by serving HTML documents over a network should be factored out of your measurements.

    You need to isolate the server from other activity, perhaps by barring other users or even disconnecting it from the network. Running tests on a server that's providing a public site may give varying results, as traffic changes during the day. Run your tests on a dedicated server even if the hardware doesn't match the production server. Optimizations made on slower hardware should translate into relative gains when put into production.

    The easiest method you can use is insertion of HTML comments into your script's output. This method adds to the overall weight of the page, but it doesn't disturb the display. I usually print the output of the microtime function. I insert a line like:

    print("\n");

    I place these calls to microtime at the beginning, end and at key points inside my script. To measure performance, I request the page in a Web browser and view the source. This produces lines like this:







    The microtime function returns the number of seconds on the clock. The first figure is a fraction of seconds, and the other is the number of seconds since January 1, 1970. You can add the two numbers and put them in an array, but I prefer to minimize the affect on performance by doing the calculation outside of the script. In the example above, the first part of the script takes approximately 0.005 seconds, and the second part takes 0.03.

    If you decide to calculate time differences, consider the method used in the example below. Entries to the clock array contain a one-word description followed by the output of microtime. The explode function breaks up the three values so the script can display a table of timing values. The first column of the table holds the number of seconds elapsed since the last entry.
    //start clock
    $clock[] = 'Start ' . microtime();

    //fake some long process
    sleep(2);
    $clock[] = 'Connected ' . microtime();


    //end clock
    $clock[] = 'End ' . microtime();

    //print clock
    $entry = explode(' ', $clock[0]);
    $lastVal = $entry[1] + $entry[2];
    print('');
    foreach($clock as $c)
    {
    $entry = explode(' ', $c);

    print('');

    print('');
    print('');
    print('');

    print('');

    $lastVal = $entry[1] + $entry[2];
    }
    print('
    ' . ($entry[1] + $entry[2] - $lastVal) .
    '
    ' . $entry[0] . '' . ($entry[1] + $entry[2]) . '
    ');
    ?>

    Inserting HTML comments is my favorite method, because it takes no preparation. But its big weakness is a small sample size. I always try three or four page loads to eliminate any variances due to caching or periodic server tasks.

    The Apache Web server includes a program that addresses this problem by measuring the number of requests your server can handle. It's called ApacheBench, but the executable is "ab". ApacheBench makes a number of requests to a given URL and reports on how long it took. Here's an example of running 1000 requests for a plain HTML document:

    ~> /usr/local/apache/bin/ab -n 1000 http://localhost/test.html

    This is ApacheBench, Version 1.3c <$Revision: 1.1.2.6 $> apache-1.3

    Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

    Copyright (c) 1998-2000 The Apache Group, http://www.apache.org/

    Server Software: Apache/1.3.19

    Server Hostname: localhost

    Server Port: 80

    Document Path: /test.html

    Document Length: 6 bytes

    Concurrency Level: 1

    Time taken for tests: 5.817 seconds

    Complete requests: 1000

    Failed requests: 0

    Total transferred: 262000 bytes

    HTML transferred: 6000 bytes

    Requests per second: 171.91

    Transfer rate: 45.04 kb/s received

    Connnection Times (ms)

    min avg max

    Connect: 1 1 11

    Processing: 3 3 16

    Total: 4 4 27

    I requested an HTML document to get an idea of the baseline performance of my server. Any PHP script ought to be slower than an HTML document. Comparing the figures gives me an idea of the room for improvement. If I found my server could serve a PHP script at 10 requests per second, I'd have a lot of room for improvement.

    Keep in mind that I'm running ApacheBench on the server. This eliminates the effects of moving data over the network, but ApacheBench uses some CPU time. I could test from another machine to let the Web server use all the system resources.

    By default, ApacheBench makes one connection at a time. If you use 100 for the -n option, it connects to the server one hundred times sequentially. In reality, Web servers handle many requests at once. Use the -c option to set the concurrency level. For example, -n 1000 -c 10 makes one thousand connections with 10 requests active at all times. This usually reduces the number of requests the server can handle, but at low levels the server is waiting for hardware, such as the hard disk.

    The ApacheBench program is a good way to measure overall change without inconsistencies, but it can't tell you which parts of a script are slower than others. It also includes the overhead involved with connecting to the server and negotiating for the document using HTTP. You can get around this limitation by altering your script. If you comment out parts and compare performance, you can gain an understanding of which parts are slowest. Alternatively, you may use ApacheBench together with microtime comments.

    Whichever method you use, be sure to test with a range of values. If your program uses input from the user, try both the easy cases and the difficult ones, but concentrate on the common cases. For example, when testing a program that analyzes text from a textarea tag, don't limit yourself to typing a few words into the form. Enter realistic data, including large values, but don't bother with values so large they fall out of normal usage. People rarely type a megabyte of text into a textarea, so if performance drops off sharply, it's probably not worth worrying about.

    Remember to measure again after each change to your program, and stop when you achieve your goal. If a change reduces performance, return to an earlier version. Let your measurements justify your changes.
    Attacking the slowest parts

    Although there are other motivations, such as personal satisfaction, most people optimize a program to save money. Don't lose sight of this as you spend time increasing the performance of your programs. There's no sense in spending more time optimizing than the optimization itself saves. Optimizing an application used by many people is usually worth the time, especially if you benefit from licensing fees. It's hard to judge the value of an open-source application you optimize, but I find work on open-source projects satisfying as recreation.

    To make the most of your time, try to optimize the slowest parts of your program where you stand to gain the most. Generally, you should try to improve algorithms by finding faster alternatives. Computer scientists use a special notation to describe the relative efficiency of an algorithm called big-O notation. An algorithm that must examine each input datum once is O(n). An algorithm that must examine each element twice is still called O(n) as linear factors are not interesting. A really slow algorithm might be O(n^2), or O of n-squared. A really fast algorithm might be O(n log n), or n times the logarithm of n. This subject is far too complex to cover here -- you will find lots of information on the Internet and in university courses. Understanding it may help you choose faster algorithms.

    Permanent storage, such as a hard disk, is much slower to use than volatile storage, such as RAM. Operating systems compensate somewhat by caching disk blocks to system memory, but you can't keep your entire system in RAM. Parts of your program that use permanent storage are good candidates for optimization.

    If you are using data stored in files, consider using a relational database instead. Database servers can do a better job of caching data than the operating system because they view the data with a finer granularity. Database servers may also cache open files, saving you the overhead in opening and closing files.

    Alternatively, you can try caching data within your own program, but consider the lifecycle of a PHP script. At the end of the request, PHP frees all memory. If during your program you need to refer to the same file many times, you may increase performance by reading the file into a variable.

    Consider optimizing your database queries, too. MySQL includes the EXPLAIN statement, which returns information about how the join engine uses indexes. MySQL's online manual includes information about the process of optimizing queries.

    Here are two tips for loops. If the number of iterations in a loop is low, you might get some performance gain from replacing the loop with a number of statements. For example, consider a for loop that sets 10 values in an array. You can replace the loop with 10 statements, which is a duplication of code, but may execute slightly faster.

    Also, don't recompute values inside a loop. Before the foreach statement appeared in PHP, I often wrote for($i=0; $i

    Function calls carry a high overhead. You can get a bump in performance if you eliminate a function. Compiled languages, such as C and Java, have the luxury of replacing function calls with inline code. You should avoid functions that you only call once. One technique for readable code is to use functions to hide details. This technique is expensive in PHP.

    If all else fails, you have the option of moving part of your code into C, wrapping it in a PHP function. This technique is not for the novice, but many of PHP's functions began as optimizations. Consider the in_array function. You can test for the presence of the value in an array by looping through it, but the function written in C is much faster.


    优化PHP脚本
    作者:Leon Atkinson

    2001年6月20日

    我喜欢PHP的一个原因是我可以在很少的语言规则约束下进行快速的网络应用程序开发。当谈及快速原型时,PHP具有足够的优势。为了让代码具有长久有效性(译者注:是不是指可重用性),我们需要使代码简洁。遵循编码规范可以是你的代码易读,但并不能使你的代码效率提高。



    优化是为了增加速度或减少内存使用的微调过程,内存使用量并不想以前那样重要了,应为现在内存很便宜。而更好的执行效率更让人关注。



    这里有一些的编写高效率代码的提示,我很难在这里一条条列出。授人以鱼不如授人以渔,这个月我将回顾一下我的一直用来加快我的PHP脚本的技术。



    何时优化

    在写代码前,就要告诫自己要编写该效率程序,遵循代码编写惯例,如使用mysql_fetch_row替代mysql_result。紧记程序执行时间是很重要的,另外不要使你的代码很难读懂。越简单越好。




    程序编写完毕时,充分考虑程序的性能. 如果你需要正式确定项目受益,参考一些性能规范。通常不要包含大量的文件,这将花费一些加载时间。排除诸如网速等补课控制的因素外,还有狠斗因素影响了用户点击链接和页面显示的这一过程时间。




    如果你决定去优化你的程序,首先去优化。这是最廉价的方法。在1965年,Gordon Moore提出计算机运行速度每18个月就可以增加一倍。这被称为Moore规律。尽管计算机速度增快,但他的价格也在随时间降低。比如,CPU的主频不断在提升,二价格确一致保持在一定水平。升级服务器比雇佣一个程序员来优化代码要便宜得多。



    升级硬件后,再考虑升级支持你运行的软件。从操作系统升级开始。Linux和BSD Unix在老式硬件的一行效率上比商业操作系统享有很高的声誉。




    如果你的程序使用了数据库,考虑关系数据库将的差别。如果你不打算使用存储过程,MySQL是一个执行效率较高的数据库解决方案。参考一下其他的网站。当然,也要考虑给数据库更大的内存。



    两个Zend产品可以帮助你提高程序的执行效率。一是Zend优化器,优化的代码和Zend引擎优化的结果相同,可以提高PHP40%~100%的执行效率。Zned优化器也是免费的。第二是Zend Cache(直译为高速缓冲存储器,对这个产品不知道是什么)。它将编译后的代码存储在内存中,以提高执行效率。一些有经验的程序员可以使效率提高300%。Zend Cache是Zend公司的商业商品。

    在开始优化前,你需要前分析一下服务器性能。我想讨论的来年改革工具是插入HTML注释和使用Apache的ApacheBench效用。PHP应用程序是在WEB服务器上运行的,而在服务器头部输出传送的HTML文档将超出了的衡量因素。


    你需要从其他应用中隔离你的服务器,可能包括其他用户或断开网络中。
    翻译了这么长了,还没到PHP的优化
    Defined tags for this entry: , , ,

    Posted by rollenc

    Last modified on 2008-12-20 23:07
    2006/09 8
    PHP Optimization Tricks

    PHP优化技巧

    原文地址:http://ilia.ws/archives/12-PHP-Optimization-Tricks.html







    英文 中文
    There are a number of tricks that you can use to squeeze the last bit
    of performance from your scripts. These tricks won't make your
    applications much faster, but can give you that little edge in
    performance you may be looking for. More importantly it may give you
    insight into how PHP internals works allowing you to write code that
    can be executed in more optimal fashion by the Zend Engine. Please keep
    in mind that these are not the 1st optimization you should perform.
    There are some far easier and more performance advantageous tricks,
    however once those are exhausted and you don't feel like turning to C,
    these maybe tricks you would want to consider. So, without further
    ado...



    1)
    When working with strings and you need to check that the string is
    either of a certain length you'd understandably would want to use the
    strlen() function. This function is pretty quick since it's operation
    does not perform any calculation but merely return the already known
    length of a string available in the zval structure (internal C struct
    used to store variables in PHP). However because strlen() is a function
    it is still somewhat slow because the function call requires several
    operations such as lowercase & hashtable lookup followed by the
    execution of said function. In some instance you can improve the speed
    of your code by using a isset() trick.



    Ex.

    if (strlen($foo) < 5) { echo "Foo is too short"; }

    vs.

    if (!isset($foo{5})) { echo "Foo is too short"; }



    Calling isset() happens to be faster then strlen() because unlike
    strlen(), isset() is a language construct and not a function meaning
    that it's execution does not require function lookups and lowercase.
    This means you have virtually no overhead on top of the actual code
    that determines the string's length.



    2) When incrementing or decrementing the value of the variable $i++
    happens to be a tad slower than ++$i. This is something PHP specific
    and does not apply to other languages, so don't go modifying your C or
    Java code thinking it'll suddenly become faster, it won't. ++$i happens
    to be faster in PHP because instead of 4 opcodes used for $i++ you only
    need 3. Post incrementation actually causes in the creation of a
    temporary var that is then incremented. While pre-incrementation
    increases the original value directly. This is one of the optimization
    that opcode optimized like Zend's PHP optimizer. It is a still a good
    idea to keep in mind since not all opcode optimizers perform this
    optimization and there are plenty of ISPs and servers running without
    an opcode optimizer.



    3) When it comes to printing text to screen PHP has so many
    methodologies to do it, not many users even know all of them. This
    tends to result in people using output methods they are already
    familiar from other languages. While this is certainly an
    understandable approach it is often not best one as far as performance
    in concerned.



    print vs echo



    Even both of these output mechanism are language constructs, if you
    benchmark the two you will quickly discover that print() is slower than
    echo(). The reason for that is quite simple, print function will return
    a status indicating if it was successful or not, while echo simply
    print the text and nothing more. Since in most cases (haven't seen one
    yet) this status is not necessary and is almost never used it is
    pointless and simply adds unnecessary overhead.



    printf



    Using printf() is slow for multitude of reasons and I would strongly
    discourage it's usage unless you absolutely need to use the
    functionality this function offers. Unlike print and echo printf() is a
    function with associated function execution overhead. More over
    printf() is designed to support various formatting schemes that for the
    most part are not needed in a language that is typeless and will
    automatically do the necessary type conversions. To handle formatting
    printf() needs to scan the specified string for special formatting code
    that are to be replaced with variables. As you can probably imagine
    that is quite slow and rather inefficient.



    heredoc



    This output method comes to PHP from PERL and like most features
    adopted from other languages it's not very friendly as far as
    performance is concerned. While this method allows you to easily output
    large chunks of text while preserving things like newlines and even
    allow for variable handling inside the text block this is quite slow
    and there are better ways to do that. Performance wise this is just
    marginally faster then printf() however it does not offer nearly as
    much functionality.



    ?><?


    When you need to output a large or even a medium sized static bit of
    text it is faster and simpler to put it outside the of PHP. This will
    make the PHP's parser effectively skipover this bit of text and output
    it as is without any overhead. You should be careful however and not
    use this for many small strings in between PHP code as multiple context
    switches between PHP and plain text will ebb away at the performance
    gained by not having PHP print the text via one of it's functions or
    constructs.



    4) Many scripts tend to reply on regular expression to validate the
    input specified by user. While validating input is a superb idea, doing
    so via regular expression can be quite slow. In many cases the process
    of validation merely involved checking the source string against a
    certain character list such as A-Z or 0-9, etc... Instead of using
    regex in many instances you can instead use the ctype
    extension (enabled by default since PHP 4.2.0) to do the same. The
    ctype extension offers a series of function wrappers around C's is*()
    function that check whether a particular character is within a certain
    range. Unlike the C function that can only work a character at a time,
    PHP function can operate on entire strings and are far faster then
    equivalent regular expressions.

    Ex.

    preg_match("![0-9]+!", $foo);

    vs

    ctype_digit($foo);



    5) Another common operation in PHP scripts is array searching. This
    process can be quite slow as regular search mechanism such as
    in_array() or manuall implementation work by itterating through the
    entire array. This can be quite a performance hit if you are searching
    through a large array or need to perform the searches frequently. So
    what can you do? Well, you can do a trick that relies upon the way that
    Zend Engine stores array data. Internally arrays are stored inside hash
    tables when they array element (key) is the key of the hashtables used
    to find the data and result is the value associated with that key.
    Since hashtable lookups are quite fast, you can simplify array
    searching by making the data you intend to search through the key of
    the array, then searching for the data is as simple as $value =
    isset($foo[$bar])) ? $foo[$bar] : NULL;. This searching mechanism is
    way faster then manual array iteration, even though having string keys
    maybe more memory intensive then using simple numeric keys.



    Ex.



    $keys = array("apples", "oranges", "mangoes", "tomatoes", "pickles");

    if (in_array('mangoes', $keys)) { ... }



    vs



    $keys = array("apples" => 1, "oranges" => 1, "mangoes" => 1, "tomatoes" => 1, "pickles" => 1);

    if (isset($keys['mangoes'])) { ... }



    The bottom search mechanism is roughly 3 times faster.



    If you know or have any additional optimization tricks let me know -).
    这里是几条优化技巧,你可以在你最终优化时用到它们。这些技巧并不是让你的代码变得更快,而只能使代码稍稍的优化一点。更重要的是它可以让你洞察到PHP内在的运行原理,使得ZEND引擎能够更好的对代码进行优化。提醒一下,这不是你在编码开始时就要执行的优化。有一些更简单更快的优化技巧,在使用后,however once those are exhausted and you don't feel like turning to C,
    these maybe tricks you would want to consider. So, without further
    ado...


    1) 当对字符串进行操作时,如果你需要检查字符串是否超过某一长度,你很容易理解如何使用strlen()函数。这个函数执行较快,没有进行任何计算操作,而仅仅返回C结果字符串的长度(在PHP中的变量与C变量内部结构相同)。但是strlen()是一个函数,于其他函数一样,在使用时需要进行几个操作,如全部小写化,函数查找。在某些场合,不可以使用isset()来提高你的代码速度。
    例子:

    if (strlen($foo) < 5) { echo "Foo is too short"; }

    vs.

    if (!isset($foo{5})) { echo "Foo is too short"; }

    调用isset()比strlen()要快,因为isset(0是一种语法结构而不是以个函数。在执行时不需要进行小写化和函数查找。这意味着你并没有真正取得字符串的长度。








    2)使用递增或递减时,$i++比++$i稍慢,和其他语言相比,在PHP是一个特例的,不要在C和JAVA中也使用这个技巧。在PHP中$++比++$i快的原因是$i++进行了四次计算,而++$i进行三次。后缀递加先申请了一个临时变量,然后增加。而前缀递加直接使用了原变量。这也是ZEND的PHP优化器优化的一部分,但这还是一个好的建议,因为并不是所有的优化器都优化了这一点,以及并不是所有ISP都支持优化器。







    3)当需要输出文本时,PHP有很多的方法。很多PHPer并不知道所有的方法,结果PHPers使用了他们以前所习惯的语法。这是可以理解的,虽然这并不是达到所有程序运行的最佳效率。




    print 对 echo

    两者都是语法结构,但print比echo 还是稍稍慢一点。理由很简单,不管是否需要,print会返回一个状态标示,而echo只是简单的输出而不做其他任何事情。而在绝大多数情况下,这个状态标示是没有用到的不必要的花费。





    printf

    使用printf()很慢,我强烈建议不在万不得已的时候不要使用这个函数。printf()对花费函数消耗。printf()是给需要进行参数格式化的情况下使用的。php是类型无关语言,大部分时间用在类型的隐形转化上。调用printf()来格式化字符串需要在字符串中扫描需要被替换的特殊字符,你可以预计的到这样的速度和效率。



    heredoc
    这种输出方式继承自PERL,这也被其他语言所采用。

    没时间翻译了,给个精要得了:
    ?><?
    替代
    ?>
    <?
    这样,PHP在执行时就不需要退出去处理那些空白输出。
    当然,我觉得这个可以直接删掉这一个?><?


















    4)很多脚本都喜欢使用正则表达式来验证用户输入数据,但正则匹配的速度是很慢的,在很多情况下,我们只是验证数据是不是象A-Z,0-9等等。我们可以使用ctype来替代正则。ctype扩展提供了一系列类C的is*()函数,不象C函数一次只能对一个字符进行验证,PHP的ctype函数可以对整个字符串操作,比正则的运行效率要高。
    例子:

    preg_match("![0-9]+!", $foo);

    vs

    ctype_digit($foo);









    5) 另一个PHP常用的操作是数组搜索,这个操作使用正则查找或完全遍历整个数组来实现,这在查找一个大数组或频繁查找时相当耗资源,我们该怎么做呢?你可以使用ZEND引擎技巧来优化它,关联数组元素实质上是以哈希表来存储的。哈希表查询速度是非常快的。所以查找数据可以简单的这样写:$value =
    isset($foo[$bar])) ? $foo[$bar] : NULL;这种查找模式比遍历查找要快,即便是字符关键字比数字关键字占用了更多内存。

    例子:



    $keys = array("apples", "oranges", "mangoes", "tomatoes", "pickles");

    if (in_array('mangoes', $keys)) { ... }



    vs



    $keys = array("apples" => 1, "oranges" => 1, "mangoes" => 1, "tomatoes" => 1, "pickles" => 1);

    if (isset($keys['mangoes'])) { ... }

    下一个查找比上一个要快3倍。








    如果你知道其他的优化技巧,请告诉我 -)
    Defined tags for this entry: , , ,

    Posted by rollenc

    Not modified
    2006/08 18
    Table 8-1. Comparing sequence and communication diagrams
    表8-1. 时间图与交互图比较

    Feature
    特征

    Sequence diagrams
    时间图

    Communication diagrams
    交互图

    The result
    比较结果

    Shows participants effectively

    有效的显示参与者

    Participants are mostly arranged along the top of page, unless the drop-box participant creation notation is used. It is easy to gather the participants involved in a particular interaction.

    如果没有使用构建符号时,参与者被排列在图顶层。很容易知道所有有联系的参与者。

    Participants as well as links are the focus, so they are shown clearly as rectangles.

    参与者被连成链状,在小范围区域内既可清楚的知道参与者间的联系。

    Communication diagrams barely win. Although both types of diagram can show participants as effectively as each other, it can be argued that communication diagrams have the edge since participants are one of their main focuses.

    交互图有些许优势,虽然两者都能有效的显示参与者间的关系,但交互图是以参与者为中心的。

    Showing the links between participants

    显示参与者间的联系

    Links are implied. If a message is passed from one participant to another, then it is implied that a link must exist between those participants.

    联系很隐晦。如果在两个参与者间有消息传递,即暗指两个参与者间有联系。

    Explicitly shows the links between participants. In fact, this is the primary purpose of these types of diagram.

    很清楚的显示了参与者间的联系,事实上,交互图的目的就是为了描述这种联系。

    Communication diagrams win because they explicitly and clearly show the links between participants.

    交互图有优势,因为它可以清楚明白的显示参与者间的关系。

    Showing message signatures

    显示消息

    Message signatures can be fully described.

    消息能够被完整的描述。

    Message signatures can be fully described.

    消息能够被完整的描述。

    Draw! Both types of diagram can show messages as effectively as each other.

    尽管去画,两种图都是有效的。

    Supports parallel messages

    显示并行信息

    With the introduction of sequence fragments, sequence diagrams are much better.

    使用时间小片断可以很好的显示并行信息。

    Shown using the number-letter notation on message sequences.

    使用数字-字母符号可以显示并行信息

    Draw! Both types of diagram show parallel messages equally well.

    尽管去画,两种图都是有效的。

    Supports asynchronous messages (fire and forget)

    支持异步信息

    Achieved using the asynchronous arrow.

    使用异步箭头完成

    Communication diagrams have no concept of the asynchronous message since its focus is not on message ordering.

    交互图并不关心异步信息传递,在交互图中没有异步的概念。

    Sequence diagrams are a clear winner here because they explicitly support asynchronous messages.

    时间图有明显优势,非常好的支持了异步信息。

    Easy to read message ordering

    信息顺序表达

    This is a sequence diagram's forté. Sequence diagrams clearly show message ordering using the vertical placement of messages down the diagram's page.

    这是一个时间图的特点。 时间图明确显示采用垂直图形清楚的现实了信息的顺序。

    Shown using the number-point-nested notation.

    使用数字字母组合符号显示。

    Sequence diagrams are a clear winner here since they really show off message ordering clearly and effectively.

    时间图清楚明白的显示了消息的顺序。

    Easy to create and maintain the diagram

    容易建立和维护

    Creating a sequence diagram is fairly simple. However, maintaining sequence diagrams can be a nightmare unless a helpful UML tool is being used.

    建立时间图是容易的,但是没有好的UML工具的话,维护时间图将是个恶梦。

    Communication diagrams are simple enough to create; however, maintenance, especially if message numbering needs to be changed, still ideally needs the support of a helpful UML tool.

    交互图很容易建立,但如果涉及到信息编号的话还是需要借助于一个好用的UML工具。

    This is a difficult one to judge and is largely based on personal preference. However, communication diagrams do have the edge on the ease-of-maintenance stakes.

    根据个人喜好,两种图优势表现不同。

    原文来源:
    Learning UML 2.0
    By Kim Hamilton, Russell Miles
    ...............................................
    Publisher: O'Reilly
    Pub Date: April 2006
    Print ISBN-10: 0-596-00982-8
    Print ISBN-13: 978-0-59-600982-3
    Pages: 286
    Defined tags for this entry: , , , ,

    Posted by rollenc

    Last modified on 2007-04-27 15:38