清单 4. access3.php
<?php class Configuration { protected $_items = array(); public function __construct() { $this->load(); } protected function load() { } public function get( $key ) { return $this->_items[ $key ]; } } class DBConfiguration extends Configuration { protected function load() { $this->_items[ 'imgpath' ] = 'images'; } } $c = new DBConfiguration(); echo( $c->get( 'imgpath' )."\n" ); ?> 这张清单显示了 protected 关键字的正确用法。基类定义了名为 load() 的方法。此类的子类将覆盖 load() 方法把数据添加到 items 表中。load() 方法对类及其子类是内部方法,因此该方法对所有外部使用者都不可见。如果关键字都是 private 的,则 load() 方法不能被覆盖。
我并不十分喜欢此设计,但是,由于必须让 DBConfiguration 类能够访问项阵列而选用了此设计。我希望继续由 Configuration 类来完全维护项阵列,以便在添加其他子类后,那些类将不需要知道如何维护项阵列。我做了以下更改。 清单 5. access4.php5
<?php class Configuration { private $_items = array(); public function __construct() { $this->load(); } protected function load() { } protected function add( $key, $value ) { $this->_items[ $key ] = $value; } public function get( $key ) { return $this->_items[ $key ]; } } class DBConfiguration extends Configuration { protected function load() { $this->add( 'imgpath', 'images' ); } } $c = new DBConfiguration(); echo( $c->get( 'imgpath' )."\n" ); ?> 现在,项阵列可以是 private 的,因为子类使用受保护的 add() 方法将配置项添加到列表中。Configuration 类可以更改存储和读取配置项的方法而不需要考虑它的子类。只要 load() 和 add() 方法以同样的方法运行,子类就应当不会出问题。
对于我来说,增加了访问控制是考虑移至 PHP V5 的主要原因。难道就因为 Grady Booch 说 PHP V5 是四大面向对象的语言之一么?不,因为我曾经接受了一个任务来维护 100KLOC C++ 代码,在这些代码中所有方法和成员都被定义为 public 的。我花了三天时间来清除这些定义,并在清除过程中,明显地减少了错误数并提高了可维护性。为什么?因为没有访问控制,就不可能知道对象怎样使用其他对象,也就不可能在不知道要突破什么难关的情况下做任何更改。使用 C++,至少我还有编译程序可用。PHP 没有配备编译程序,因此这类访问控制变得愈加重要。 契约编程 从 PHP V4 迁移到 PHP V5 时要利用的下一个重要特性是支持通过接口、抽象类和方法进行契约编程。清单 6 显示了一个版本的 Configuration 类,在该类中 PHP V4 编码人员尝试了构建基本接口而根本不使用 interface 关键字。 清单 6. interface.php4
<?php class IConfiguration { function get( $key ) { } } class Configuration extends IConfiguration { var $_items = array(); function Configuration() { $this->load(); } function load() { } function get( $key ) { return $this->_items[ $key ]; } } class DBConfiguration extends Configuration { function load() { $this->_items[ 'imgpath' ] = 'images'; } } $c = new DBConfiguration(); echo( $c->get( 'imgpath' )."\n" ); ?> |