+ -
当前位置:首页 → 问答吧 → [PDF双语版][Zend PHP 5认证学习指南]第八章 面向对象设计基础[本章已全部更新完毕]

[PDF双语版][Zend PHP 5认证学习指南]第八章 面向对象设计基础[本章已全部更新完毕]

时间:2008-05-13

来源:互联网

这一章将分为两个部分。第一部分主要讲述设计模式,第二部分介绍SPL。两部分先分别单独贴出,此处用于PDF版本的发布。第一部分先贴出,第二部分还没有翻译。

排版真不容易啊 ^_^
说明,当初只打算翻译第六、八两章,因为它们的内容都是有关OOP方面的。现在打算在这两章翻译完成后将按顺序完成其它章节的翻译。在这里要多谢Richard老大的鼓励 :-D
---------------------------------------
2008.05.13 第二部分已更新

本书其它章节:

第六章 PHP面向对象程序设计(PDF双语版)PDF

[ 本帖最后由 Altair 于 2008-6-19 05:58 编辑 ]

作者: Altair   发布时间: 2008-05-12

Chapter 8 Elements of Object-oriented Design
第八章 面向对象设计基础

The benchmark of a good programmer, regardless of what language they work with,is their ability to apply well-known and accepted design techniques to any given situation. Design Patterns are generally recognized as an excellent set of tried-and-true solutions to common problems that developers face every day.
好的程序员的标准是不管使用什么程序设计语言,他们都可以在特定情况下应用已被广泛接受的设计技术。设计模式被普遍认为是一组被证明为行之有效的的对付开发人员日常遇到的带有共性的问题的优秀解决方案。

In this chapter, we’ll focus on how some of PHP 5’s new facilities, such as proper object orientation, can make the development of pattern-driven applications easier. While the exam is not strewn with complex examples of pattern development, it does require you to have a firm grasp of the basics behind design patterns and their use in everyday applications.
本章我们将重点放点PHP 5的新机制如proper object orientation是如何简化模式驱动的应用程序的开发的。虽然考试不会满纸都是模式开发的复杂例子,但确实需要你很好的掌握设计模式的基本概念以及它们在日常开发过程中的应用方法。

Design Pattern Theory
设计模式原理
As we mentioned in the previous section, design patterns are nothing more than streamlined solutions to common problems. In fact, design patterns are not really about code at all―they simply provide guidelines that you, the developer, can translate into code for pretty much every possible language. In this chapter, we will provide a basic description of some of the simpler design patterns, but, as the exam concerns itself primarily with the theory behind them, we will, for the most part, stick to explaining how they work in principle.
在前一节我们提到,设计模式是对带有共性的问题的有效的解决方案。实际上,设计模式跟代码根本没有关系――它们仅仅提供了些能使你,也就是开发者,用几乎每一种可能的语言将其转化成代码的指导方法。在本章中,我们会对部分相对简单的设计模式给出一些基本说明,但因为认证考试主要关注的是这些模式背后的原理,因此在大部分时候我们还是用原理性的方式来解释模式是如何工作的。

Even though they can be implemented using nothing more than procedural code, design patterns are best illustrated using OOP. That’s why it’s only with PHP 5 that they have really become relevant to the PHP world: with a proper object-oriented architecture in place, building design patterns is easy and provides a tried-and-true method for developing robust code.
尽管设计模式也可以用过程化的代码来实现,但OOP还是设计模式的最好的表现形式。这也是为什么直到PHP5出现后,设计模式才真正进入到PHP世界:在proper object-oriented 的架构下,构建设计模式是一种容易并且是被证明行之有效的开发健壮代码的方法。

The Singleton Pattern
Singleton模式

The Singleton is, probably, the simplest design pattern. Its goal is to provide access to a single resource that is never duplicated, but that is made available to any portion of an application that requests it without the need to keep track of its existence. The most typical example of this pattern is a database connection, which normally only needs to be created once at the beginning of a script and then used throughout its code. Here’s an example implementation:
Singleton模式也许是最简单的设计模式。它的目标是为那些只有一个实例的资源提供访问方法,使得在程序的任何需要访问它的部分都可以直接访问它而不用跟踪其存在状态。这个模式最典型的例子就是数据库连接,通常只需要在脚本的开始的创建一次,然后在整个脚本中使用。下面是一个实现的例子:
复制内容到剪贴板
代码:
class DB {
private static $_singleton;
private $_connection;
private function __construct()
{
  $this->_connection = mysql_connect();
}
public static function getInstance()
{
  if (is_null (self::$_singleton)) {
   self::$_singleton = new DB();
  }
  return self::$_singleton;
}
}
$db = DB::getInstance();
Our implementation of the DB class takes advantage of a few advanced OOP concepts that are available in PHP 5: we have made the constructor private, which effectively ensures that the class can only be instantiated from within itself. This is, in fact, done in the getInstance() method, which checks whether the static property $_connection has been initialized and, if it hasn’t, sets it to a new instance of DB. From this point on, getInstance() will never attempt to create a new instance of DB, and instead always return the initialized $_connection, thus ensuring that a database connection is not created more than once.
DB类的实现利用了PHP5的一些高级的面向对象的概念:我们将构造函数声明为私有的(private),从而保证类只能在自身内部实例化。这是在getInstance()方法内完成的,它先检查类的静态属性$_connection是否已经初始化,如果没有,则将其设置为DB类的一个新实例。从这一点来说,getInstance()从不会试图去创建一个DB类的新实例,而代之以总是返回已经初始化过的$_connection,这就保证了数据库连接不会被创建多次。

The Factory Pattern
Factory模式

The Factory pattern is used in scenarios where you have a generic class (the factory) that provides the facilities for creating instances of one or more separate “specialized” classes that handle the same task in different ways.
Factory模式适用于这样的场合,有一个通用类(工厂,factory)来创建一个或多个独立的“专门的”类,而这些类以不同的方式来处理相同的任务。

A good situation in which the Factory pattern provides an excellent solution is the management of multiple storage mechanisms for a given task. For example, consider configuration storage, which could be provided by data stores like INI files, databases or XML files interchangeably. The API that each of these classes provides is the same (ideally, implemented using an interface), but the underlying implementation changes. The Factory pattern provides us with an easy way to return a different data store class depending on either the user’s preference, or a set of environmental factors:
Factory模式能提供极好的解决方案的一个情形是特定任务中的多种存储方式的管理。例如配置信息的保存,它可以交替保存到INI文件,数据库或XML文件中。每个类提供的API函数都相同(理想地是通过接口实现),但是其内部实现却不同。Factory模式能根据用户偏好或环境因素,提供一种很容易的返回不同的数据存储类的方式。
复制内容到剪贴板
代码:
class Configuration {
const STORE_INI = 1;
const STORE_DB = 2;
const STORE_XML = 3;
public static function getStore($type = self::STORE_XML)
{
  switch ($type) {
   case self::STORE_INI:
    return new Configuration_Ini();
   case self::STORE_DB:
    return new Configuration_DB();
   case self::STORE_XML:
    return new Configuration_XML();
   default:
    throw new Exception("Unknown Datastore Specified.");
  }
}
}
class Configuration_Ini {
// ...
}
class Configuration_DB {
// ...
}
class Configuration_XML {
// ...
}
$config = Configuration::getStore(Configuration::STORE_XML);
The Registry Pattern
Registry模式

By taking the Singleton pattern a little further, we can implement the Registry pattern. This allows us to use any object as a Singleton without it being written specifically that way.
如果我们对Singleton模式作进一步的思考,我们就可以实现Registry模式。该模式允许我们将任意对象当作一个Singleton,而不需要将它的代码按上面的方式重写一遍。

The Registry pattern can be useful, for example, if, for the bulk of your application, you use the same database connection, but need to connect to an alternate database to perform a small set of tasks every now and then. If your DB class is implemented as a Singleton, this is impossible (unless you implement two separate classes, that is)―but a Registry makes it very easy:
Registry模式在下面情况下非常有用:例如,如果在你程序的大部分时间你都使用同一个数据库连接,但是需要不时的连接到另一个数据库去执行一些任务。如果DB类设计成Singleton模式就不可能做到这一点(除非你实现了两个独立的类)――但是Registry模式却很容易做到这一点:
复制内容到剪贴板
代码:
class Registry {
private static $_register;
public static function add(&$item, $name = null)
{
  if (is_object($item) && is_null($name)) {
   $name = get_class($item);
  } elseif (is_null($name)) {
   $msg = "You must provide a name for non-objects";
   throw new Exception($msg);
  }
  $name = strtolower($name);
  self::$_register[$name] = $item;
}

public static function &get($name)
{
  $name = strtolower($name);
  if (array_key_exists($name, self::$_register)) {
   return self::$_register[$name];
  } else {
   $msg = "’$name’ is not registered.";
   throw new Exception($msg);
  }
}

public static function exists($name)
{
  $name = strtolower($name);
  if (array_key_exists($name, self::$_register)) {
   return true;
  } else {
   return false;
  }
}
}
$db = new DB();
Registry::add($db);
// Later on
if (Registry::exists(’DB’)) {
$db = Registry::get(’DB’);
} else {
die(’We lost our Database connection somewhere. Bear with us.’);
}
The Model-View-Controller Pattern
MVC模式

Unlike the patterns we have seen this far, Model-View-Controller (MVC) is actually quite complex. Its goal is that of providing a methodology for separating the business logic (model) from the display logic (view) and the decisional controls (controller).
跟目前我们已知的模式不同,MVC模式实际上很复杂。它的目标是提供一种方法学,将商业逻辑(model)与显示逻辑(view)和决策控制(controller)分离出来。

In a typical MVC setup, the user initiates an action (even a default one) by calling the Controller. This, in turn, interfaces with the Model, causing it to perform some sort of action and, therefore, changing its state. Finally, the View is called, thus causing the user interface to be refreshed to reflect the changes in the Model and the action requested of the Controller, and the cycle begins anew.
在一个典型的MVC方案中,用户通过控制器(Controller)来开始一个动作(甚至是一个默认的动作)。然后,与Model沟通,引发它执行一些操作,促使其状态发生改变。最后,View被调用,用户界面因此刷新以反映Model的改变以及Controller请求的动作的结果,然后这个循环重新开始。

The clear advantage of the MVC pattern is its clear-cut approach to separating each domain of an application into a separate container. This, in turn, makes your applications easier to maintain and to extend, particularly because you can easily modularize each element, minimizing the possibility of code duplication.
MVC模式的显而易见的优点是它清晰的将应用的各个部分分解成独立的容器(container)。这使得应用程序更容易维护和扩展,特别的是,因为你很容易将每个元素模块化,从而将代码重复的可能性降到最低。

The ActiveRecord Pattern
ActiveRecord模式

The last pattern that we will examine is the ActiveRecord pattern. This is used to encapsulate access to a data source so that the act of accessing its components―both for reading and for writing―is, in fact, hidden within the class that implements the pattern, allowing its callers to worry about using the data, as opposed to dealing with the database.
最后我们要考查的是ActiveRecord模式。它用来封装对数据源的访问使得其访问操作(包括读和写)对实现这个模式的类透明,这样允许它的调用者将精力放在使用数据上而不是如何处理数据库上。

The concept behind ActiveRecord is, therefore, quite simple, but its implementation can be very complicated, depending on the level of functionality that a class based on this pattern is to provide. This is usually caused by the fact that, while developers tend to deal with individual database fields individually and interactively, SQL deals with them as part of rows that must be written back to the database atomically. In addition, the synchronization of data within your script to the data inside the database can be very challenging, because the data may change after you’ve fetched it from the database without giving your code any notice.
ActiveRecord模式的概念很简单,但它的实现可能很复杂,这得看基于此模式的类要提供的功能的级别。这是因为,虽然开发人员是以独立地和交互式的处理每个数据库字段,SQL却将其当作数据行的一部分,它们必须以原子操作的形式重新写回数据库。另外,数据同步也非常具有挑战性,因为在你将数据从数据库中取出后它可能会发生改变,而你的代码却得不到任何通知。

[ 本帖最后由 Altair 于 2008-6-19 05:58 编辑 ]

作者: PHPChina   发布时间: 2008-05-12

The Standard PHP Library
标准PHP库

The Standard PHP Library (SPL) is a great addition to PHP 5. It provides a number of very useful facilities that expose some of PHP’s internal functionality and allow the “userland” developer to write objects that are capable of behaving like arrays, or that transparently implement certain iterative design patterns to PHP’s own core functionality, so that you, for example, use a foreach() construct to loop through an object as if it were an array, or even access its individual elements using the array operator [].
标准PHP库(SPL)更得PHP5增色不少。它通过暴露PHP的内部功能而提供了许多非常有用的工具,允许“用户态”开发人员可以写出行为象数组一样的对象,或透明的实现那些PHP内部核心功能的一些迭代器模式,使得你可以使用foreach()在对象上循环,好象它是数组一样,甚至可以使用数组操作符[]来访问它的单个元素。

SPL works primarily by providing a number of interfaces that can be used to implement the functionality required to perform certain operations. By far, the largest number of patterns exposed by SPL are iterators; they allow, among other things:
SPL工作方式是提供一组接口,通过实现这些接口来完成特定操作的功能。目前SPL提供最多的模式是迭代器;它们允许(但不限于这些):

? Array Access to objects
? Simple Iteration
? Seekable Iteration
? Recursive Iteration
? Filtered Iteration

?        用数组的方式访问对象
?        简单迭代器
?        可查找的迭代器
?        递归迭代器
?        过滤迭代器

Accessing Objects as Arrays
用数组的方式访问对象

The ArrayAccess interface can be used to provide a means for your object to expose themselves as pseudo-arrays to PHP:
ArrayAccess接口用来提供一种供对象将自己当作PHP伪数组的方法。
复制内容到剪贴板
代码:
interface ArrayAccess {
        function offsetSet($offset, $value);
        function offsetGet($offset);
        function offsetUnset($offset);
        function offsetExists($offset);
}
This interface provides the basic methods required by PHP to interact with an array:
? offsetSet() sets a value in the array
? offsetGet() retrieves a value from the array
? offsetUnset() removes a value from the array
? offsetExists() determines whether an element exists

该接口提供了下列接口,以便PHP与数组交互:
?        offsetSet() 设置一个数组元素的值
?        offsetGet() 获取一个数组元素的值
?        offsetUnset() 从数组中删除一个元素
?        offsetExists() 确定一个元素是否存在

As a very quick example, consider the following class, which “emulates” an array that only accepts elements with numeric keys:
下面这个简单例子“模拟”了一个数组,它仅接受键值(key)为数值的元素。
复制内容到剪贴板
代码:
class myArray implements ArrayAccess {
        protected $array = array();
        
        function offsetSet ($offset, $value) {
                if (!is_numeric ($offset)) {
                        throw new Exception ("Invalid key $offset");
                }
                $this->array[$offset] = $value;
        }

        function offsetGet ($offset) {
                return $this->array[$offset];
        }

        function offsetUnset ($offset) {
                unset ($this->array[$offset]);
        }

        function offsetExists ($offset) {
                return array_key_exists ($this->array, $offset);
        }
}
$obj = new myArray();
$obj[1] = 2; // 正确.
$obj[’a’] = 1; // 会抛出一个异常.
As you can see, this feature of SPL provides you with an enormous amount of control over one of PHP’s most powerful (and most useful) data types. Used properly, ArrayAccess is a great tool for building applications that encapsulate complex behaviours in a data type that everyone is used to.
你可以看到,SPL这个特点让你可以自由控制PHP中最强大(也最有用)的数据类型之一(数组)。合理运用的话,ArrayAccess将是一个强大的工具,它允许创建可以将复杂的行为封装到一个大家都熟悉的数据类型上的应用。

Simple Iteration
简单迭代器

The Iterator interface is the simplest of the iterator family, providing simple iteration over any single-dimension array. It looks like this:
Iterator接口是迭代器家族中最简单的一种,提供了一种在一维数组上简单循环的方法。它的接口如下:
复制内容到剪贴板
代码:
interface Iterator {
        function current();
        function next();
        function rewind();
        function key();
        function valid();
        function seek($key);
}
You can see a simple implementation of the interface that allows iteration over a private property containing a simple array:
你可以看到这个接口的一个简单应用,它允许在包括一个简单数组的私有属性上循环。
复制内容到剪贴板
代码:
class myData implements iterator {
        private $_myData = array(
                "foo",
                "bar",
                "baz",
                "bat");
        private $_current = 0;

        function current() {
                return $this->myData[$this->current];
        }

        function next() {
                $this->current += 1;
        }

        function rewind() {
                $this->current = 0;
        }

        function key() {
                return $this->current;
        }

        function valid() {
                return isset($this->myData[$this->current]);
        }
}
$data = new myData();
foreach ($data as $key => $value) {
        echo "$key: $value\n";
}
This example will iterate over each of the four elements in the myData private property in the exact same way foreach() works on a standard Array.
这个例子使用与普通数组一样的方式,用foeach()在包含四个元素的私有属性myData上循环。

Seekable Iterators
可查找迭代器

The next step up from a standard Iterator is the SeekableIterator, which extends the standard Iterator interface and adds a seek() method to enable the ability to retrieve a specific item from internal data store. Its interface looks like this:
下一个从标准迭代器演化而来的是SeekableIterator,它继续了标准的Iterator接口,增加了一个seek()方法,使得其可以从内部数据中获取一个指定的数据项。它的接口如下:
复制内容到剪贴板
代码:
interface SeekableIterator {
        function current();
        function next();
        function rewind();
        function key();
        function valid();
        function seek($index);
}
Recursive Iteration
递归迭代器

Recursive Iteration allows looping over multi-dimensional tree-like data structures. SimpleXML, for example, uses recursive iteration to allow looping through complex XML document trees.
递归迭代器允许在多维树状数据结构上循环。例如,SimpleXML就使用递归迭代器在复杂的XML文档树上循环。

To understand how this works, consider the following complex array:
为了理解它是如何工作的,看下面的这个复杂数组:
复制内容到剪贴板
代码:
$company = array(
        array("Acme Anvil Co."),
                array(
                        array(
                                "Human Resources",
                                array(
                                        "Tom",
                                        "Dick",
                                        "Harry"
                                )
                        ),
                        array(
                                "Accounting",
                                array(
                                        "Zoe",
                                        "Duncan",
                                        "Jack",
                                        "Jane"
                                )
                        )
                )
        );
Our goal is to print out something like this:
我们的目标打印出如下形式的内容:
复制内容到剪贴板
代码:
<h1>Company: Acme Anvil Co.</h1>
<h2>Department: Human Resources</h2>
<ul>
        <li>Tom</li>
        <li>Dick</li>
        <li>Harry</li>
</ul>
<h2>Department: Accounting</h2>
<ul>
        <li>Zoe</li>
        <li>Duncan</li>
        <li>Jack</li>
        <li>Jane</li>
</ul>
By extending RecursiveIteratorIterator, we can define the beginChildren() and endChildren() methods so that our class can output the start and end <ul> tags without any of the complexities normally associated with recursion (such as, for example, keeping track of multiple nested levels of nesting). The example shown below defines two classes, our custom RecursiveIteratorIterator and a very simple RecursiveArrayObject:
通过继承RecursiveIteratorIterator, 我们可以定义beginChildren()和endChildren()方法,使得我们的类可以输出开始和结束的<ul>标签从而避免由于递归而带来的复杂性(例如,记录多重嵌套的层次)。下面的例子定义了两个类,RecursiveIteratorIterator和一个非常简单的RecursiveArrayObject.
复制内容到剪贴板
代码:
class Company_Iterator extends RecursiveIteratorIterator {
function beginChildren()
{
        if ($this->getDepth() >= 3) {
                echo str_repeat("\t", $this->getDepth() - 1);
                echo "<ul>" . PHP_EOL;
        }
}

function endChildren()
{
        if ($this->getDepth() >= 3) {
                echo str_repeat("\t", $this->getDepth() - 1);
                echo "</ul>" . PHP_EOL;
        }
}
}

class RecursiveArrayObject extends ArrayObject {
        function getIterator() {
                return new RecursiveArrayIterator($this);
        }
}
Then, to produce our desired end result, we simply use this code:
为了产生我们需要的结果,简单使用下列代码即可:
复制内容到剪贴板
代码:
$it = new Company_Iterator(new RecursiveArrayObject($company));
$in_list = false;
foreach ($it as $item) {
        echo str_repeat("\t", $it->getDepth());
        switch ($it->getDepth()) {
                case 1:
                        echo "<h1>Company: $item</h1>" . PHP_EOL;
                        break;
                case 2:
                        echo "<h2>Department: $item</h2>" . PHP_EOL;
                        break;
                default:
                        echo "<li>$item</li>" . PHP_EOL;
        }
}
Filtering Iterators
过滤迭代器

The FilterIterator class can be used to filter the items returned by an iteration:
FilterIterator类可用来过滤从iteration返回的数据项:
复制内容到剪贴板
代码:
class NumberFilter extends FilterIterator {
        const FILTER_EVEN = 1;
        const FILTER_ODD = 2;
        private $_type;
        function __construct($iterator, $odd_or_even = self::FILTER_EVEN)
        {
                $this->_type = $odd_or_even;
                parent::__construct($iterator);
        }
        function accept()
        {
                if ($this->_type == self::FILTER_EVEN) {
                        return ($this->current() % 2 == 0);
                } else {
                        return ($this->current() % 2 == 1);
                }
        }
}
$numbers = new ArrayObject(range(0, 10));
$numbers_it = new ArrayIterator($numbers);
$it = new NumberFilter($numbers_it, NumberFilter::FILTER_ODD);

foreach ($it as $number) {
        echo $number . PHP_EOL;
}
The accept() method simply determines whether any given element should be allowed in the iteration; note that FilterIterator already implements all of the methods of ArrayAccess, so that, effectively, from the outside our class can still be used as an array.
accept()方法确定指定的元素是否允许参加循环;注意FilterIterator已经实现了ArrayAccess的全部方法,因此,在类的外部仍可以将其当作一个数组。

This example outputs only the odd numbers stored in the array:
本例仅输出数组中的奇数数字:
1
3
5
7
9

Summary
小结

Object Oriented programming, coupled with Design Patterns―including those provided by SPL―is the key to re-usable and highly modular code.
面向对象的程序设计,配合各种设计模式(包括SPL所提供的),是设计可重用、高度模块化代码的关键。

The more forethought you give your design, the more likely you are to be able to re-use at least some of it, later, saving time and effort in the future―not to mention that proper design techniques also make code easier to maintain and extend.
在设计阶段考虑得越充分,你可越有可能至少部分重用它,而在将来节省时间和精力――更不用说合理的设计技术会使得代码更容易维护和扩展。

Bringing this experience to the table is what makes you truly versatile; as we mentioned, design patterns are, after all, largely language- and problem-independent.
学会运用这些经验将使你无所不能;如我们所说,设计模式在很大程度上与程序设计语言和问题无关。

[ 本帖最后由 Altair 于 2008-6-19 05:58 编辑 ]

作者: Altair   发布时间: 2008-05-12

oo 是好东西啊!!!
oop (*^__^*) 嘻嘻……

作者: Altair   发布时间: 2008-05-13

作者: piaohh   发布时间: 2008-05-13

认真学习,十分感谢楼的贡献

作者: 小星   发布时间: 2008-05-13

本章已全部更新完毕。请多提宝贵意见。

作者: 网鬼   发布时间: 2008-05-13

真的非常不错,辛苦了!

作者: Altair   发布时间: 2008-05-28

太好了,顶!

作者: PHPChina   发布时间: 2008-06-04

学习E文的好机会啊。感谢。

作者: ylcz   发布时间: 2008-06-12

热门下载

更多