Board logo

标题: 开始了解 PHP V5 中的对象(4) [打印本页]

作者: look_w    时间: 2018-7-15 14:07     标题: 开始了解 PHP V5 中的对象(4)

继承如果熟悉面向对象编程,您将知道我一直把最好的留到最后。类及其生成的动态对象之间的关系使得系统更灵活。例如,每个 Dictionary 对象封装不同的翻译数据集合,但是这些不同实体的模型定义在单个 Dictionary 类中。
但有时候需要记下类级别的差异。是否记得 DictionaryIO 类?扼要重述一下,它从 Dictionary 对象中获取数据,将其写入文件系统,从一个文件中获取数据,将其合并回到 Dictionary 对象中。清单 12 显示使用序列化来保存和加载 Dictionary 数据的快速实现。
清单 12. 使用序列化的快速实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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() 方法中使用条件语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 类的最小实现:
1
2
XmlDictionaryIO extends DictionaryIO {
}




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




输出如下:
1
2
object is an instance of XmlDictionaryIO
object is an instance of DictionaryIO




正如 instanceof 接受 $dictio 是 DictionaryIO 对象,所以方法也将接受这些对象作为参数。这意味着 XmlDictionaryIO 对象可以被传递给 Dictionary 类的构造函数,即使 DictionaryIO 是由构造函数的签名指定的类型。
清单 15 是快而脏的 XmlDictionaryIO 实现,使用 DOM 来完成 XML 功能。
清单 15. XmlDictionaryIO 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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 显示了 DictionaryIO 和 XmlDictionaryIO 类之间的继承关系。封闭的箭头表示继承,从子类指向父类。
图 2. 继承关系




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0