模拟对象的属性
[code:1:4d03e04cdd]
<?php
// +---------------------------------------------------------------------------+
// | 这个文件是 pfc 项目的一部分。 |
// | Copyright (c) 2004 廖宇雷。 |
// | |
// | 要查看完整的版权信息和许可信息,请查看源代码中附带的 COPYRIGHT 文件, |
// | 或者访问 http://www.dualface.com/ 获得详细信息。 |
// +---------------------------------------------------------------------------+
/**
* 定义 ObjectProperties 型别
*
* @copyright Copyright (c) 2004 dualface.com
* @author 廖宇雷 <daut@dualface.com>
* @version $Id: ObjectProperties.class.php,v 1.1 2004/09/26 17:09:27 dualface Exp $
* @package pfc
* @since 3.0
*/
// {{{ constants
/**#@+
* 属性访问规则
*/
/**
* 只读属性
*/
define ('PROP_READONLY', 1);
/**
* 可读可写属性
*/
define ('PROP_READWRITE', 2);
/**#@-*/
// }}}
/**
* ObjectProperties 型别定义了具有访问规则控制属性的对象
*
* 所有 ObjectProperties 的继承类都必须在类定义文件的最后执行 overload (继承类名称),否则无法正常工作。
*
* @author 廖宇雷 <daut@dualface.com>
* @package pfc
* @version 1.0
*/
class ObjectProperties
{
/**
* 保存所有可用属性的名称和访问规则
*
* @access private
* @since 1.0
* @var array
*/
var $_properties;
/**
* 保存属性值
*
* @access private
* @since 1.0
* @var array
*/
var $_properties_value;
/**
* 构造函数
*
* @param array $properties 确定可用的属性及其访问规则
*
* @return ObjectProperties
*
* @access public
* @since 1.0
*/
function ObjectProperties($properties, $value) {
$this->_properties = $properties;
$new_value = array();
foreach ($properties as $prop_name => $prop_rule) {
if (isset($value[$prop_name])) {
$new_value[$prop_name] = $value[$prop_name];
} else {
$new_value[$prop_name] = null;
}
}
$this->_properties_value = $new_value;
}
/**
* 回调函数,用于获取属性值
*
* @param string $prop_name 属性名称
* @param mixed $prop_value 保存属性的值
*
* @return boolean 指示是否成功
*
* @access public
* @since 1.0
*/
function __get($prop_name, &$prop_value)
{
if (isset($this->_properties[$prop_name])) {
$prop_value = $this->_properties_value[$prop_name];
return true;
} else {
return false;
}
}
/**
* 回调函数,用于设置属性值
*
* @param string $prop_name 属性名称
* @param mixed $prop_value 属性的值
*
* @return boolean 指示是否成功
*
* @access public
* @since 1.0
*/
function __set($prop_name, $prop_value)
{
if (isset($this->_properties[$prop_name]) && $this->_properties[$prop_name] == PROP_READWRITE) {
$this->_properties_value[$prop_name] = $prop_value;
return true;
}
return false;
}
}
overload('ObjectProperties');
?>
[/code:1:4d03e04cdd]
使用的例子:
[code:1:4d03e04cdd]
<?php
// {{{ includes
/**
* 载入 ObjectProperties 型别定义
*/
require_once ('ObjectProperties.class.php');
// }}}
/**
* User 型别封装了基本的用户信息
*
* PFC3 使用者可以继承 User 型别,以获得对扩展的用户信息的处理能力。
*
* @author 廖宇雷 <daut@dualface.com>
* @package pfc
* @version 1.0
*/
class User extends ObjectProperties
{
/**
* 保存基本数据的属性名
*
* @access private
* @since 1.0
* @var array
*/
var $_base_prop_names;
/**
* 保存扩展数据的属性名
*
* @access private
* @since 1.0
* @var array
*/
var $_extra_prop_names;
/**
* 构造函数
*
* 所有的基本数据都是只读的,而所有的扩展数据都是可读可写的
*
* @param array $base_data 用于构造用户对象的基本数据
* @param array $extra_data 用于构造用户对象的扩展数据
*
* @return User
*
* @access public
* @since 1.0
*/
function User($base_data, $extra_data) {
$base_prop_names = array('UserId', 'UserName', 'Password',
'Email', 'PasswordQuestion', 'PasswordAnswer',
'CreationDate', 'LastLogonDate', 'LogonCount',
'LastPasswordChangedDate', 'LastActivitiesDate',
'IsApproved');
$this->_base_prop_names = $base_prop_names;
$properties = array();
foreach ($base_prop_names as $prop_name) {
$properties[$prop_name] = PROP_READONLY;
}
$this->_extra_prop_names = array();
reset($extra_data);
while ($prop_name = key($extra_data)) {
if (!isset($properties[$prop_name])) {
$this->_extra_prop_names[] = $prop_name;
$properties[$prop_name] = PROP_READWRITE;
}
next($extra_data);
}
parent::ObjectProperties($properties, array_merge($extra_data, $base_data));
}
}
overload('User');
?>
<?php
require_once ('User.class.php');
$base_data = array('UserId' => 123);
$extra_data = array();
$user =& new User($base_data, $extra_data);
echo $user->UserId;
$user->UserId = 1;
$user2 =& new User($base_data, $extra_data);
echo $user2->UserId;
?>
[/code:1:4d03e04cdd]
| dualface 回复于:2004-09-27 22:51:11 |
| 伤心啊,都没人发言 |
| numlock 回复于:2004-09-28 09:24:57 |
| 先说一下设计动机!也许看的人会更多! |
| 飞雪恨水 回复于:2004-09-28 09:55:00 |
| 看的有点糊涂
/ +---------------------------------------------------------------------------+ // | 这个文件是 pfc 项目的一部分。 | // | Copyright (c) 2004 廖宇雷。 | // | | // | 要查看完整的版权信息和许可信息,请查看源代码中附带的 COPYRIGHT 文件, | // | 或者访问 http://www.dualface.com/ 获得详细信息。 | // +---------------------------------------------------------------------------+ /** * 定义 ObjectProperties 型别 * * @copyright Copyright (c) 2004 dualface.com * @author 廖宇雷 <daut@dualface.com> * @version $Id: ObjectProperties.class.php,v 1.1 2004/09/26 17:09:27 dualface Exp $ * @package pfc * @since 3.0 */ 你上面的注释 是用程序生成的还是自己 * * 写的? |
| dualface 回复于:2004-09-28 09:58:00 |
| [quote:dedb19fcd3="飞雪恨水"]看的有点糊涂..........[/quote:dedb19fcd3]
自己写的,只是文件的描述信息。 |
| 飞雪恨水 回复于:2004-09-28 10:19:36 |
| 我感觉自己写那些符号很累啊
/******************************* @* @* @* */ |
| dualface 回复于:2004-09-28 10:22:52 |
| 用ZDE的模板功能撒。
良好的注释对于代码的维护有很好的作用。 |
| 飞雪恨水 回复于:2004-09-28 10:29:00 |
| 我用EDITPLUS
这是我的摸版 很简单 <?php /*@文件名称: *@文件描述: *@生成日期: *@修改日期: *@备注: **/ ?> |
| dualface 回复于:2004-09-28 10:33:21 |
| @文件名称
这样的标记不是标准的,文档生成工具不认识的。 |
| 深空 回复于:2004-09-28 11:36:32 |
| 不知道楼主干嘛用哦
PS:关于注释可以去看看PEAR的文档规范和phpdoc,嘿嘿 |
| dualface 回复于:2004-09-28 11:52:52 |
| 在 PHP4 中,自定义的对象是没有属性的。
我们要么用一个字段来替代属性,但是无法控制对象的使用者是否能够修改这些字段的值。 [code:1:1005c9e0a0] class test { var $Value; function test() { $this->Value = 100; } } $t = new test(); echo $t->Value; $t->Value = 200; // 实际上我们根本不希望用户修改这个字段值。这个字段应该是只读的。 [/code:1:1005c9e0a0] 如果用函数调用替代属性,虽然可以实现“只读属性的效果”,但对于可读可写的属性来说就要增加相应的读取函数和写入函数。 [code:1:1005c9e0a0] class test2 { var $_value; function test2() { $this->_value = 100; } function GetValue() { return $this->_value; } function SetValue($new_value) { $this->_value = $new_value; } } $t = new test2(); echo $t->GetValue(); $t->SetValue(200); [/code:1:1005c9e0a0] 这样总是不直观,而且代码也不容易维护。 使用我上面贴出来的代码就可以实现这样的效果: [code:1:1005c9e0a0] class test3 extends ObjectProperties { function test3() { $properties = array("Value" => PROP_READONLY); $value = array("Value" => 100); parent::ObjectProperties($properties, $value); } } overload("test3"); $t = new test3(); echo $t->Value; $t->Value = 200; // 此处会导致错误 [/code:1:1005c9e0a0] |
| dualface 回复于:2004-09-28 22:51:55 |
| [b:dbc1e57bd2]这一切看上去很美![/b:dbc1e57bd2]
很不幸,这种方式有几个严重的缺陷: 1、由于采用了 overload() 提供的重载机制模拟对象的属性,因此访问对象的属性时实际上就会调用该对象的 __get() 或者 __set() 函数。如果进行大量这样的属性访问操作,效率毫无疑问比使用成员变量低得多。 2、也许 PHP4 的 overload() 设计上有一些缺陷。当我们试图将一个 overload 化了(也就是对该型别调用了 overload() 函数)的对象的【引用】赋值给另一个 overload 化对象的成员变量时,PHP4 会报告错误。 [code:1:dbc1e57bd2] <?php class test3 { function __get() {...}; // 这里是简化后的代码 function __set() {...}; } overload('test3'); class test4 { function __get() {...}; function __set() {...}; var $_x; function dosomething(& $obj) { $this->_x =& $obj; // 这里会报错,如果去掉 & 就可以运行了 } } overload('test4'); ?> [/code:1:dbc1e57bd2] 3、此外,如果在多重继承中应用 overload(),会导致 PHP 运行环境出错。 看来这种方式也只能作为一个试验,实际应用还是不行的。至于 PHP5 会不会有后面两个问题,我没有测试过。 |
| HonestQiao 回复于:2004-09-29 14:26:26 |
| 首先,你没有提前给出设计文档,所以看你的这个东西比较模模糊糊 |
| longnetpro 回复于:2004-09-29 17:42:05 |
| [quote:3a9a5ad0df="dualface"]
这一切看上去很美! 很不幸,这种方式有几个严重的缺陷: 1、由于采用了 overload() 提供的重载机制模拟对象的属性,因此访问对象的属性时实际上就会调用该对象的 __get() 或者 __set() 函数。如果进行大量这样的属性访问操作,效率毫无疑问比使用成员变量低得多。 2、也许 PHP4 的 overload() 设计上有一些缺陷。当我们试图将一个 overload 化了(也就是对该型别调用了 overload() 函数)的对象的【引用】赋值给另一个 overload 化对象的成员变量时,PHP4 会报告错误 3、此外,如果在多重继承中应用 overload(),会导致 PHP 运行环境出错。 看来这种方式也只能作为一个试验,实际应用还是不行的。至于 PHP5 会不会有后面两个问题,我没有测试过。[/quote:3a9a5ad0df] 对楼主表示钦佩!能做出这样的总结。 本来昨天想发一长篇来泼楼主一大盆冷水的,不过又觉得花的时间太多不好,且深更半夜想问题比较模糊,只想到三点问题,总算轻轻放过了。今天有点精神,深夜再写。 今天看楼主自我批评,方发觉楼主果然分析问题透彻!一语就道破昨天我想写的第一个问题。用__set及__get函数重载我是不赞成的,因为它们将拦截所有对对象属性的操作,不论是你否预先定义过,一方面增加了已存在属性处理时的复杂度,二是大大增加了对非指定属性处理的复杂度,比用直接的setXXX或getXXX的调用还要复杂。不但对属性要进行各种判断,还要倒一手进到一个数组,这种方式用到一个具体的类里面完全是得不偿失,资源浪费加大多倍。我的意见是凡没有特殊的必须的要求,不要轻易用重载。 而我想说的第二点是如果楼主因为想实现属性的限制存取而用重载,则根本没有这个必要。说无必要有两点,一是没有必要用这个方法;二是连这个限制都没有必要。第一点无必要是基于第二点的,如果连限制都没有必要,那就更没有这个必要去实现它了。为什么没有必要限制呢?因为没有人会故意改那些本来要用函数来改的属性。一般来说,你只提供类库,你给使用者或是二次开发者的只是接口,根本不必涉及到内部的成员变量——如果你的类设计得足够好。而属性这个东西在OO里的本义与楼主说的是不同的,它只是从控件的某些概念中引申出来的,所谓可读或可读写也是如此。而DELPHI中属性的概念与OO的概念差不多,只是一些特殊变量或是函数按一定规则组合起来的一种机制的简单化,而并非只指单个变量,对属性的读写,不仅仅是对某个变量进行读写,而是一组操作过程。这个过程有简单有复杂,最简单的一种就是对单个变量进行读写,这个变量本身在作用域内是可读写的,但操作规则如果规定只能用某种方式对该变量进行读操作,而禁止任何方式对它进行写操作,那么从规则上,就只能读取该变量的值,将这组操作定义为一个属性,这个属性就是只读的。在Delphi或VB中,属性全是公共的,而变量则全是私有的。在JAVA与C++中则没有明确的属性这个概念。在PHP4中之所以说没有必要,因为PHP4的成员变量全是公共的,根本无法限制其作用域,它可以在任何时间任何地点被读写,因此限制它纯属多余。那在PHP5中有作用域的限制,是否有必要呢?那么就请看第三点。 第三点,在PHP5中也没有必要。因为PHP5完全可以用JAVA或C++的方式来对成员变量进行读写访问限制,而且在一般情况下,也根本没有可能动态存取那么多原先未定义的属性。那么,这样会不会导致楼主说的不直观或维护不易呢?也不会。不直观,楼主的意思是说用$obj->status = 'Middle Class' 比用$obj->setStatus('Middle Class') 来得直观,其实只是个习惯问题,两者都直观。而说到维护问题,相反,是楼主的方法更不易,一方面是因为用楼主的方法处理复杂度增加,二是因为用JAVA或C++方式更容易控制,调试更清楚。唯一的不方便则是第二种方法代码可能会写得多一点。 可能还有一些别的什么原因,不过脑子又迷糊了,还是去睡觉好了。 |
| dualface 回复于:2004-09-29 22:49:55 |
| [quote:562847bba1="longnetpro"]
对楼主表示钦佩!能做出这样的总结。 本来昨天想发一长篇来泼楼主一大盆冷水的,不过又觉得花的时间太多不好,且深更半夜想问题比较模糊,只想到三点问题,总算轻轻放过了。今天有点精神,深夜再写。 今天?.........[/quote:562847bba1] 确实如此。感觉有时候是为了OO而OO,应该叫做过度OO吧?呵呵 主要是这段时间都在用 C# 搞开发,所以感觉 .NET 那样纯 OO 的环境确实很爽。 |
| fzj-w 回复于:2004-11-03 12:31:35 |
| 我用Eclipse写PHP,设置自动生成注释
<?php # +----------------------------------------------------------------------+ # | PHP Version 5 | # +----------------------------------------------------------------------+ # | Copyright (c) 2004 redundancy code | # +----------------------------------------------------------------------+ # | This source file is subject to version 2.02 of the PHP license, | # | that is bundled with this package in the file LICENSE, and is | # | available at through the world-wide-web at | # | http://www.php.net/license/2_02.txt. | # | If you did not receive a copy of the PHP license and are unable to | # | obtain it through the world-wide-web, please send a note to | # | license@php.net so we can mail you a copy immediately. | # +----------------------------------------------------------------------+ # | Authors: redundancy code <redundancycode@gmail.com> | # $Id: test.php,v 0.1 2004-11-3 12:31:21 pajoye Exp $ # ?> |

