【发布时间】:2013-07-15 19:11:22
【问题描述】:
这可能是一个基本问题,但它让我想了很长时间。
我应该将所有私有/局部变量声明为私有吗?还是只有“重要”变量才需要这样做?
例如,我有一个(临时)计算结果。我应该预先声明这个变量吗?
希望有人能指出这一点。
【问题讨论】:
-
变量(在
function内)或对象属性?
这可能是一个基本问题,但它让我想了很长时间。
我应该将所有私有/局部变量声明为私有吗?还是只有“重要”变量才需要这样做?
例如,我有一个(临时)计算结果。我应该预先声明这个变量吗?
希望有人能指出这一点。
【问题讨论】:
function 内)或对象属性?
既然您在谈论private、protected 和public,我认为您在谈论属性,而不是变量。
在这种情况下:是,您应该事先声明它们。
由于 PHP 对象的设计方式,在编译时会创建一个数组 (properties_table)。该数组可确保尽可能快地访问给定属性。但是,如果您在进行过程中添加属性,PHP 也需要跟踪这一点。因此,一个对象也有一个简单的properties 表。
第一个 (properties_table) 是一个指针数组,而后者是一个简单的键 => 值表。
所以呢?好吧,因为properties_table 只包含指针(具有固定大小),所以它们存储在一个简单的数组中,并且使用它们各自的偏移量获取指针。偏移量存储在另一个 HashTable 中,which is the ce->properties_info pointer。
正如 bwoebi 在 cmets 中向我指出的那样:获取偏移量(哈希表查找)是最坏情况下的线性运算 (O(n)),而预定义的属性查找是常数时间复杂运算 (O(1))。另一方面,动态属性需要另一个 HashTable 查找,即最坏情况的线性运算 (O(n))。这意味着,访问动态属性平均需要大约两倍的时间。不过,维基百科的作者可以explain Time-Complexity 比我做得更好。
起初,访问修饰符似乎无关紧要。随着您的进行,您很快就会发现,有时您只是不想冒险某个对象的某些属性被某些代码修改。那时您会看到 private 的值。
如果一个对象包含另一个对象,它包含您的代码将依赖的各种设置,例如,您可能会使用 getter 方法从外部访问这些设置,但您会将实际属性很好地隐藏起来使用private。
如果更进一步,您要向项目中添加数据模型和服务层,那么您将编写一个(抽象)父类,如果只是用于类型提示的话,这是一个很好的改变。
如果这些服务实例包含类似配置属性的东西,您可能会在父类中定义该 getter(仅定义一次)。 private 表示只有当前类可以访问属性,但是由于您不会使用父类的实例,而是子类的实例,因此您将看看为什么protected 在处理大型项目时也非常宝贵。
就临时变量而言,无论是在方法、函数还是其他任何地方,您都没有必须预先声明它们,除非在某些情况下数组:
public function foo()
{
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
完全有效,如果你要写,也不会更好
public function foo()
{
$temp;// or $temp = null;
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
但是,这样的事情并不少见:
public function bar($length = 1)
{
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
此代码依赖于 PHP 足够友好地创建一个数组,并在到达 $return[] = rand(); 语句时将其分配给 $return。 PHP 会这样做,但是将您的 ini 设置为 E_STRICT | E_ALL 会表明它不会在不抱怨的情况下这样做。将0 传递给方法时,不会创建数组,PHP 在到达return $return; 语句时也会报错:未声明的变量。不仅乱七八糟,还会拖慢你的速度!最好将 $return 声明为范围顶部的数组:
public function bar($length = 1)
{
$return = array();//that's it
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
为了安全起见,我还要检查参数类型:
/**
* construct an array with random values
* @param int $length = 1
* @return array
**/
public function bar($length = 1)
{
$length = (int) ((int) $length > 0 ? $length : 1);//make length > 0
$return = array();
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
【讨论】:
$this 是指针)。查看 Zend 引擎的黑客指南了解详情
struct。
在大多数情况下(如果不是全部):是。
如果变量是类属性,它们绝对应该在使用前声明。
如果变量是函数的局部变量,请在使用之前在该函数中声明它。函数变量被限制在函数的范围内(局部变量)。它们在使用前必须声明,但这样做是一种很好的做法,如果你这样做,它会消除警告消息。如果它们没有在其他任何地方使用,它们不应该不过是属性,
【讨论】:
class Foo { private $bar })它是一个类属性。如果它在独立函数或类方法中,则它是局部变量
如果你在整个类的上下文中使用它,那么是的,你应该将你的变量定义为类的成员。
但是,如果您在单个函数的上下文中谈论局部变量,并且该变量不需要在其他地方使用(或不返回),那么不需要。
本质上,您需要先确定变量的重要性和范围,然后再决定是否将其设为类属性。
例如:
<?php
class Test {
private $test; // Private property, for use in the class only
public $public_test; // Public Property, for use both internally and external to the class as a whole
public function testing() {
$local = 5; // Local variable, not needed outside of this function ever
$this->test = rand(1, 5);
$calc = $local * $this->test; // Local variable, not needed outside of this function ever
$this->public_test = $calc / 2; // The only thing that the whole class, or public use cares about, is the result of the calculation divided by 2
}
}
【讨论】:
在使用之前定义和初始化变量通常是一个很好的经验法则。这不仅包括定义和初始值,还包括输入值的验证和过滤,以便在对这些变量包含的数据进行具体处理之前建立代码块所基于的所有前提条件。
同样适用于对象成员(属性),因为它们是整个对象的变量。所以它们应该已经在类中定义了(默认情况下它们的值在 PHP 中是NULL)。动态值/过滤可以在构造函数和/或 setter 方法中完成。
可见性规则类似于代码中的任何规则:尽可能少(很难实现的简单规则)。因此,先保持本地化,然后再保持私有 - 取决于它是函数变量还是对象属性。
也许你要记住,在 PHP 中,你可以从同一个类中访问私有属性——而不仅仅是同一个对象。知道这一点很有用,因为它可以让您将事情保密更长时间。
例如,我有一个(临时)计算结果。我应该预先声明这个变量吗?
这通常是函数或方法中的局部变量。它是在接收到计算方法的返回值时定义的。所以没有必要预先声明它(本身)。
...
function hasCalculation() {
$temp = $this->calculate();
return (bool) $temp;
}
...
如果计算很昂贵,那么存储(缓存)该值可能是有意义的。当您将其封装时,例如在对象中,这很容易工作。在这种情况下,您将使用私有属性来存储计算后的值。
对这些规则持保留态度,它们是针对一般方向的,您可以从中轻松修改,因此可以扩展,因此是保持灵活性的好方法。
【讨论】:
$this 的用法,而不仅仅是从类中。这是你的意思吗?
A-$a1和$a2类的对象-那么$a1方法的代码可以访问$a2的私有属性。这就是我的意思。同一类的所有对象都可以访问私有成员。
private 的$a2 属性难道不是$a1 无法触及的吗?我什至不明白这是如何工作的。如果$a1 调用$this->privateProperty,则该属性属于$a1。但是如果它调用$a2->privateProperty,那应该会失败,因为该属性是私有的。我看错了吗?能否提供信息链接?