- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在使用 __get()使我的一些属性“动态”(仅在请求时初始化它们)。这些“假”属性存储在私有(private)数组属性中,我正在 __get 中检查它。
无论如何,您认为为每个属性创建方法比在 switch 语句中创建方法更好吗?
我只关心性能,@Gordon 提到的其他东西对我来说并不那么重要:
下面是我所做的测试,这让我认为性能命中论证是不合理的:
50.000 次调用的结果(在 PHP 5.3.9 上):
(t1 = 带开关的魔法,t2 = getter,t3 = 带进一步 getter 调用的魔法)
不确定 t3 上的“Cum”是什么意思。它不能是累积时间,因为 t2 应该有 2K 那么...
代码:
class B{}
class A{
protected
$props = array(
'test_obj' => false,
);
// magic
function __get($name){
if(isset($this->props[$name])){
switch($name){
case 'test_obj':
if(!($this->props[$name] instanceof B))
$this->props[$name] = new B;
break;
}
return $this->props[$name];
}
trigger_error('property doesnt exist');
}
// standard getter
public function getTestObj(){
if(!($this->props['test_obj'] instanceof B))
$this->props['test_obj'] = new B;
return $this->props['test_obj'];
}
}
class AA extends A{
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name); // give me a break, its just a test :P
if(method_exists($this, $getter))
return $this->$getter();
trigger_error('property doesnt exist');
}
}
function t1(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
function t2(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->getTestObj();
}
echo 'done.';
}
function t3(){
$obj = new AA;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
t1();
t2();
t3();
ps:为什么我要使用 __get() 而不是标准的 getter 方法?唯一的原因是 api 的美感;因为我没有看到任何真正的缺点,我想这是值得的 :P
这次我用microtime测量了一些平均值:
PHP 5.2.4 和 5.3.0(相似结果):
t1 - 0.12s
t2 - 0.08s
t3 - 0.24s
PHP 5.3.9,xdebug 处于事件状态,这就是它这么慢的原因:
t1 - 1.34s
t2 - 1.26s
t3- 5.06s
禁用 xdebug 的 PHP 5.3.9:
t1 - 0.30
t2 - 0.25
t3 - 0.86
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name);
if(method_exists($this, $getter)){
$this->$name = $this->$getter(); // <-- create it
return $this->$name;
}
trigger_error('property doesnt exist');
}
具有请求名称的公共(public)属性将在第一次 __get 调用后动态创建。这解决了速度问题——在 PHP 5.3 中获得 0.1s(比标准 getter 快 12 倍),以及 Gordon 提出的可扩展性问题。您可以简单地覆盖子类中的 getter。
缺点是属性变得可写:(
最佳答案
这是 Zend Debugger 在我的 Win7 机器上使用 PHP 5.3.6 报告的代码结果:
如您所见,对 __get
方法的调用比常规调用慢很多(3-4 倍)。对于总共 50k 个调用,我们仍然处理不到 1s,因此在小规模使用时可以忽略不计。但是,如果您打算围绕魔术方法构建整个代码,您将需要分析最终应用程序以查看它是否仍然可以忽略不计。
关于相当无趣的性能方面就这么多了。现在让我们来看看你认为“不那么重要”的是什么。我要强调这一点,因为它实际上比性能方面重要得多。
关于你写的不必要的复杂性
it doesn't really increase my app complexity
当然可以。您可以通过查看代码的嵌套深度轻松发现它。好的代码留在左边。你的 if/switch/case/if 有四层深。这意味着有更多可能的执行路径,这将导致更高的 Cyclomatic Complexity。 ,这意味着更难维护和理解。
这是 A 类的数字(没有常规的 Getter。输出从 PHPLoc 缩短):
Lines of Code (LOC): 19
Cyclomatic Complexity / Lines of Code: 0.16
Average Method Length (NCLOC): 18
Cyclomatic Complexity / Number of Methods: 4.00
值为 4.00 意味着这已经处于中等复杂性的边缘。每将一个额外的 case 放入 switch,这个数字就会增加 2。此外,它会将您的代码变成程序上的困惑,因为所有逻辑都在 switch/case 内部,而不是将其分成离散的单元,例如单 setter/getter 。
Getter,即使是延迟加载的 Getter,也不需要适度复杂。考虑具有普通旧 PHP Getter 的同一个类:
class Foo
{
protected $bar;
public function getBar()
{
// Lazy Initialization
if ($this->bar === null) {
$this->bar = new Bar;
}
return $this->bar;
}
}
在此运行 PHPLoc 将为您提供更好的圈复杂度
Lines of Code (LOC): 11
Cyclomatic Complexity / Lines of Code: 0.09
Cyclomatic Complexity / Number of Methods: 2.00
对于您添加的每一个额外的普通旧 Getter,这将保持为 2。
另外,考虑到当你想使用你的变体的子类型时,你将不得不重载 __get
并复制并粘贴整个 switch/case block 来进行更改,而使用普通的旧的 Getter,您只需重载需要更改的 Getters。
是的,添加所有 Getters 需要更多的打字工作,但它也更简单,最终会导致代码更易于维护,并且还具有为您提供显式 API 的好处,这将我们引向您的其他声明
I specifically want my API to be "isolated"; The documentation should tell others how to use it :P
我不知道您所说的“隔离”是什么意思,但如果您的 API 无法表达它的作用,那么它就是糟糕的代码。如果我必须阅读您的文档,因为您的 API 没有告诉我如何通过查看它来与它交互,那么您做错了。您正在混淆代码。在数组中声明属性而不是在类级别(它们所属的位置)声明它们会强制您为其编写文档,这是额外的和多余的工作。好的代码易于阅读和 self 记录。考虑购买 Robert Martin 的书“Clean Code”。
话虽如此,当你说
the only reason is the api beauty;
那我说:那就不要用__get
因为它会起到相反的效果。它会使 API 变得丑陋。魔术是复杂且不明显的,这正是导致那些 WTF 时刻的原因:
现在结束:
i don't see any real disadvantages, I guess it's worth it
希望您现在能看到它们。这不值得。
有关延迟加载的其他方法,请参阅 various Lazy Loading patterns from Martin Fowler's PoEAA :
There are four main varieties of lazy load. Lazy Initialization uses a special marker value (usually null) to indicate a field isn't loaded. Every access to the field checks the field for the marker value and if unloaded, loads it. Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real the object and then delegates. Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load. A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.
These approaches vary somewhat subtly and have various trade-offs. You can also use combination approaches. The book contains the full discussion and examples.
关于php - 使用 __get() (魔术)模拟只读属性和延迟加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9342543/
我在不同的硬件上测试 Cassandra 已经有一段时间了。 首先我有 2 个 CPU 和 6 GB RAM 然后我更改为 16 个 CPU 和 16 GB RAM(其中只有 6 GB 可供我的测试使
我只是想从二进制文件中读/写。我一直在关注 this教程,它的工作原理......除了它似乎正在将内容写入 txt 文件。我在测试的时候把文件命名为test.bin,但是记事本可以打开并正常显示,所以
我编写了一些简单的 Java 代码来从文本文件中读取字符串,将它们组合起来,然后将它们写回。 (有关输出没有变化的简化版本,请参见下面的片段) 问题是输入文件和输出文件中的特定字符(- 和 ...)是
我真的很感兴趣——你为什么要放 readln; 从键盘读取一些值到变量后的行?例如, repeat writeln('Make your choise'); read(CH); if (CH = '1
只要程序不允许同时写入存储在模块中的共享数据结构的相同元素,它是线程安全的吗?我知道这是一个菜鸟问题,但在任何地方都找不到明确解决的问题。情况如下: 在程序开始时,数据被初始化并存储在模块级可分配数组
我有一个数据结构,其操作可以归类为读取操作(例如查找)和写入操作(例如插入、删除)。这些操作应该同步,以便: 读操作不能在写操作执行时执行(除非在同一线程上),但是读操作可以与其他读操作并发执行。 在
我在Java套接字编程中有几个问题。 在读取客户端套接字中的输入流时,如果抛出IO异常;那么我们是否需要重新连接服务器套接字/再次初始化客户端套接字? 如果我们关闭输出流,它将关闭客户端套接字吗? 如
我正在尝试从客户端将结构写入带有套接字的服务器。 结构是: typedef struct R { int a; int b; double c; double d; double result[4];
我想知道是否可以通过 Javascript 从/向 Azure Active Directory 广告读取/写入数据。我读到 Azure 上有 REST 服务,但主要问题是生成与之通信的 token
我希望有人能提供完整的工作代码,允许在 Haskell 中执行以下操作: Read a very large sequence (more than 1 billion elements) of 32
我有一个任务是制作考试模拟器。我的意思是,在老师输入某些科目的分数后,学生输入他的名字、姓氏和出生,然后他决定学生是否通过科目。所以,我有一个问题,如何用新行写入文件文本并通过重写该文件来读取(逐行读
我需要编写巨大的文件(超过 100 万行)并将文件发送到另一台机器,我需要使用 Java BufferedReader 一次读取一行。 我使用的是 indetned Json 格式,但结果不太方便,
我在 Android 应用程序中有一个读写操作。在 onCreate 上,将读取文件并将其显示为编辑文本并且可以进行编辑。当按下保存按钮时,数据将被写入 onCreate 上读取的同一文件中。但我得到
我正在编写一个程序,该程序从一个文件读取输入,然后该程序将格式化数据并将其写入另一个文件。 输入文件: Christopher kardaras,10 N Brainard,Naperville,IL
我有一个 SCALA(+ JAVA) 代码,它以一定的速率读写。分析可以告诉我代码中每个方法的执行时间。如何衡量我的程序是否达到了最大效率?为了使我的代码优化,以便它以给定配置可能的最大速度读取。我知
嗨,我想知道如何访问 java/maven 中项目文件夹中的文件,我考虑过使用 src/main/resources,但有人告诉我,写入此目录中的文件是一个坏主意,并且应该只在项目的配置中使用,所以我
我想读\写一个具有以下结构的二进制文件: 该文件由“RECORDS”组成。每个“RECORD”具有以下结构:我将以第一条记录为例 (红色)起始字节:0x5A(始终为 1 字节,固定值 0x5A) (绿
我想制作一个C程序,它将用一些参数来调用;每个参数将代表一个文件名,我想在每个参数中写一些东西。 FILE * h0; h0 = fopen(argv[0],"w"); char buff
我有一个包含团队详细信息的文件。我需要代码来读取文件,并将获胜百分比写入第二个文件。我还需要使用指示的搜索功能来搜索团队的具体信息。该代码未写入百分比文件。当菜单显示时,第一个文件的内容被打印,但代码
我正在使用 read() 和 write() 函数来处理我的类,并且我正在尝试使用一个函数来写入它所读取的内容以及我作为参数给出的前面的内容。 例如,我想给出 10 作为我的程序的参数 int mai
我是一名优秀的程序员,十分优秀!