盾怪网教程:是一个免费提供流行杀毒软件教程、在线学习分享的学习平台!

开始知道 PHP V5 中的对象

时间:2024/11/30作者:未知来源:盾怪网教程人气:

[摘要]! is_writable( dir ) ) return false; self::iodir = dir; public st...
! is_writable( $dir ) ) { return false; } self::$iodir = $dir; } public static function getSaveDirectory( ) { return self::$iodir; } // ...}

用户不再能访问 $iodir 属性目录了。通过创建特殊方法来访问属性,可以确保所提供的任何值是健全的。在本例中,方法在进行分配前检查给定字符串指向可写入的目录。

注意,两个方法都使用关键字 self 和访问解析操作符来引用 $iodir 属性。不能在静态方法中使用 $this,因为 $this 是对当前对象实例的引用,但静态方法是通过类而不是通过对象调用的。如果 PHP 引擎在静态方法中看到 $this,它将抛出致命错误和一条提示消息。

要从类外部调用静态方法,可使用类名加上范围解析符和方法名。

Dictionary::setSaveDirectory("/tmp");print Dictionary::getSaveDirectory();

需要使用静态方法有两个重要原因。首先,实用程序操作可能不需要对象实例来做它的工作。通过声明为静态,为客户机代码节省了创建对象的工作量。第二,静态方法是全局可用的。这意味着可以设置一个所有对象实例都可以访问的值,而且使得静态方法成为共享系统上关键数据的好办法。

尽管静态属性通常被声明为 private 来防止别人干预,但有一种方法可以创建只读静态范围的属性,即声明常量。与全局属性一样,类常量一旦定义就不可更改。它用于状态标志和进程生命周期中不发生更改的其他东西,比如 pi 或非洲的所有国家。

const 关键字声明类常量。例如,因为 Dictionary 对象的实际实现背后几乎肯定有一个数据库,所以还可以假设项和翻译有最大长度。清单 11 将其设置为类常量。

清单 11. 将 MAXLENGTH 设置为类常量
class Dictionary {    const MAXLENGTH = 250;    // ... }print Dictionary::MAXLENGTH;

类常量始终为 public,所以不能使用可见度关键字。这并是问题,因为任何更改其值的尝试都将导致解析错误。还要注意,与常规属性不同,类常量不以美元符号开始。

继承
如果熟悉面向对象编程,您将知道我一直把最好的留到最后。类及其生成的动态对象之间的关系使得系统更灵活。例如,每个 Dictionary 对象封装不同的翻译数据集合,但是这些不同实体的模型定义在单个 Dictionary 类中。

但有时候需要记下类级别的差异。是否记得 DictionaryIO 类?扼要重述一下,它从 Dictionary 对象中获取数据,将其写入文件系统,从一个文件中获取数据,将其合并回到 Dictionary 对象中。清单 12 显示使用序列化来保存和加载 Dictionary 数据的快速实现。

清单 12. 使用序列化的快速实现
class Dictionary {    // ...    function asArray() {        return $this->translations;    }    function getType() {        return $this->type;    }    function export() {        $this->dictio->export( $this );    }    function import() {        $this->dictio->import( $this );    }}class DictionaryIO {    function path( Dictionary $dictionary, $ext ) {        $path  = Dictionary::getSaveDirectory();        $path .= DIRECTORY_SEPARATOR;        $path .= $dictionary->getType().".$ext";        return $path;    }    function export( Dictionary $dictionary ) {        $translations = $dictionary->asArray();        file_put_contents( $this->path(                           $dictionary, 'serial'),                            serialize( $translations ) );      }    function import( Dictionary $dictionary ) {        $path = $this->path( $dictionary, 'serial' );        if ( ! is_file( $path ) ) return false;         $translations = unserialize(                         file_get_contents( $path ) );        foreach ( $translations as $term => $trans ) {            $dictionary->set( $term, $trans );        }    }}$dict = new Dictionary( "En", new DictionaryIO() );$dict->set( "TREE", "tree" );$dict->export();

本例引入两个简单的 Dictionary 方法,具体来说,asArray() 返回 $translations 数组的副本。DictionaryIO 实现具有简约的优点。因为在示例代码中通常省略了错误检查,即便如此,这仍是将数据保存到文件中的快速简单的方法。

一旦部署了这种库之后,则需要立即支持它的保存格式。让格式过时会冒犯那些可能以这种方式存储备份的用户的愿望。但要求改变了,而且还可能收到输出格式不方便用户编辑的抱怨。这些用户希望将导出文件以 XML 格式发送给第三方。

现在面临一个问题。如何在 DictionaryIO 接口中支持两种格式?

一个解决方案是在 export()import() 方法中使用条件语句,测试类型标志,如清单 13 所示。

清单 13. 在 export()import() 方法中使用条件语句
function export( Dictionary $dictionary ) {    if ( $this->type == DictionaryIO::SERIAL ) {        // write serialized data    } else if ( $this->type == DictionaryIO::XML ) {        // write xml data    }}function import( Dictionary $dictionary ) {    if ( $this->type == DictionaryIO::SERIAL ) {        // read serialized data    } else if ( $this->type == DictionaryIO::XML ) {        // read xml data    }}

这种结构是坏“代码味道”的一个例子,原因在于它依赖于复制。在一个地方进行更改(比如,添加新类型测试)需要在其他地方进行一组相应的更改(将其他类型测试带入行中),代码很快就会变得易错难读。

继承提供了更优雅的解决方案。可以创建一个新类 XmlDictionaryIO,该类继承由 DictionaryIO 设置的接口,但覆盖其中一些功能。

使用 extends 关键字创建子类。如下是 XmlDictionaryIO 类的最小实现:

XmlDictionaryIO extends DictionaryIO {}

XmlDictionaryIO 现在的功能与 DictionaryIO 完全相同。因为它从 DictionaryIO 继承了所有的公共(和保护)属性,所以可以将应用于 DictionaryIO 对象的相同操作应用于 XmlDictionaryIO 对象。这种关系扩展到对象类型。XmlDictionaryIO 对象显然是 XmlDictionaryIO 类的实例,但它也是 DictionaryIO 的实例 —— 同样地,以一般化的顺序,一个人同时是人类、哺乳动物和动物。可以使用 instanceof 操作符来测试这一点,如果对象是指定类的成员,则返回 true,如清单 14 所示。

清单 14. 使用 instanceof 操作符测试继承
$dictio = new XmlDictionaryIO();if ( $dictio instanceof XmlDictionaryIO ) {    print "object is an instance of XmlDictionaryIO\n";}if ( $dictio instanceof DictionaryIO ) {    print "object is an instance of DictionaryIO\n";}

输出如下:

object is an instance of XmlDictionaryIOobject is an instance of DictionaryIO

正如 instanceof 接受 $dictioDictionaryIO 对象,所以方法也将接受这些对象作为参数。这意味着 XmlDictionaryIO 对象可以被传递给 Dictionary 类的构造函数,即使 DictionaryIO 是由构造函数的签名指定的类型。

清单 15 是快而脏的 XmlDictionaryIO 实现,使用 DOM 来完成 XML 功能。

清单 15. XmlDictionaryIO 实现
class XmlDictionaryIO extends DictionaryIO {    function export( Dictionary $dictionary ) {        $translations = $dictionary->asArray();        $doc = new DOMDocument("1.0");        $dic_el = $doc->createElement( "dictionary" );         $doc->appendChild( $dic_el );        foreach ( $translations as $key => $val ) {            $term_el = $doc->createElement( "term" );            $dic_el->appendChild( $term_el );            $key_el = $doc->createElement("key", $key );            $val_el = $doc->createElement(                      "value", $val );            $term_el->appendChild( $key_el );            $term_el->appendChild( $val_el );        }        file_put_contents( $this->path(                            $dictionary, 'xml'),                            $doc->saveXML() );    }    function import( Dictionary $dictionary ) {        $path = $this->path( $dictionary, 'xml');        if ( ! is_file( $path ) ) return false;        $doc = DOMDocument::loadXML(                file_get_contents( $path ) );        $termlist = $doc                    ->getElementsByTagName( "term" );        foreach ( $termlist as $term ) {            $key = $term->getElementsByTagName( "key" )                   ->item( 0 )->nodeValue;            $val = $term                   ->getElementsByTagName( "value" )                   ->item( 0 )->nodeValue;            $dictionary->set( $key, $val );         }    }}

有关获得并生成 XML 的详细信息是当然要介绍的。有许多方法能完成这一操作,其中包括完美的 SimpleXML 扩展。简言之,import() 方法以 XML 文档为参数,并使用它来填充 Dictionary 对象。export() 方法从 Dictionary 对象中取得数据,并将其写入 XML 文件中。(在现实世界中,可能会使用叫做 XLIFF 的基于 XML 的格式,该格式适用于导入到第三方翻译工具中。)

注意,import()export() 都调用实用程序方法 path(),该方法不存在于 XmlDictionaryIO 类中。但没有关系,因为 path()DictionaryIO 中实现。当 XmlDictionaryIO 实现一个方法时,则当调用该方法时,会为 XmlDictionaryIO 对象调用该实现。当没有任何实现存在时,调用失败返回给父类。

图 2 显示了 DictionaryIOXmlDictionaryIO 类之间的继承关系。封闭的箭头表示继承,从子类指向父类。

图 2. 继承关系
继承关系

结束语
由于篇幅有限,因此不可能全部介绍。进一步研究有两个方向:广度和深度。广度指的是超出本文范围的那些特性,比如抽象类、接口、迭代器接口、反射、异常和对象复制。深度指的是设计问题。尽管理解 PHP 中可用于面向对象编程的工具范围很重要,但考虑如何最佳使用这些特性同样重要。幸运的是,专门讲述面向对象上下文中设计模式的可用参考资料很多(参阅“参考资料”)

如果仍在使用 PHP V4 编程,我希望您查找足够新的特性来证明迁移到 V5 及其面向对象核心特性是正确的。不久您就会惊讶地发现自己无法离开它们了。



关键词:开始知道 PHP V5 中的对象




Copyright © 2012-2018 盾怪网教程(http://www.dunguai.com) .All Rights Reserved 网站地图 友情链接

免责声明:本站资源均来自互联网收集 如有侵犯到您利益的地方请及时联系管理删除,敬请见谅!

QQ:1006262270   邮箱:kfyvi376850063@126.com   手机版