我有点困惑,因为我在PHP中没有太多OOP经验。我总是听说使用实例方法比使用静态方法更好。为什么呢?
我需要一个深刻的答案和解释。
例如,为什么下面这个应该用实例方法完成?
控制器:
public function getProperty($id){
$property = Property::getProperty($id);
return $property;
}
型号:
public static function getProperty($id){
//$query = DB::table('properties')...
//Some Code;
return $query;
}
我正在阅读有关setter / getter,instance vs static等的内容。但我需要一个完整的答案才能理解 和为什么。< / p>
答案 0 :(得分:17)
一般来说,正如您已经说过的那样,实例方法是更好的做法。这并不是说静态方法是彻头彻尾的邪恶,它们只是具有不同且独特的目的。
重要要注意,在处理实例方法时,您正在使用对象,而使用静态方法,您正在使用类 >。使用静态方法时,您将无法访问通常可用于实例的任何非静态属性。
以下面的代码为例:
class Foo
{
private $bar;
private static $tavern;
public function changeBar($value)
{
$this->bar = $value;
}
public function getBar()
{
return $this->bar;
}
public static function changeTavern($value)
{
self::$tavern = $value;
}
public static function getTavern()
{
return self::$tavern;
}
}
类Foo
具有静态属性$tavern
和非静态属性$bar
。
如果创建了Foo
的实例,则该对象可以使用所有属性和方法。
如果Foo
被静态引用,那么类只能使用$tavern
属性,changeTavern()
方法和getTavern()
方法。
让我们看看以下代码:
$foo = new Foo();
$foo->changeBar('Tipsy Turvy');
echo $foo->getBar(); // prints Tipsy Turvy
由于$foo
是Foo
的实例,因此它可以访问整个类。调用changeBar()
将修改$bar
属性。要直接从静态方法更改$bar
属性,将触发错误,因为对象而不是类可以使用$bar
。
// Calling this method would trigger an error
public static function changeBar($value)
{
$this->bar = $value; // PHP will crash and burn if you try this.
}
如果要从静态方法访问类属性,则还必须将这些属性声明为static。这将在上面的类的上下文中工作。您还会注意到Foo
的实例在读取静态属性时没有问题。
Foo::changeTavern('Stumble Inn');
echo Foo::getTavern(); // prints Stumble Inn
echo $foo->getTavern(); // also prints Stumble Inn
关于静态代码要记住的另一件事是它不像一个实例。构建Foo
的第一个实例时,属性$bar
和$tavern
都没有值。如果要创建另一个Foo
实例,您会发现其中只有一个属性不再包含值。 (我相信你现在可以猜到哪一个。)
$anotherFoo = new Foo();
echo $anotherFoo->getBar(); // prints nothing
echo $anotherFoo->getTavern(); // prints Stumble Inn
echo Foo::getTavern(); // prints Stumble Inn
同样,静态代码意味着您直接使用类 - 实例意味着您正在使用对象。重要的是要注意,您编写的任何类型的类都应该使用某种类型的状态作为实例。
静态类可能有点难以调试和测试。测试可能会有问题,因为在创建新实例时静态属性不会更改。调试也很困难,因为静态属性的值在类的所有实例中都是相同的。在一个中进行更改,您将在所有这些中进行更改。追踪哪个实例做出改变是一件痛苦的事。
比喻说,使用像糖一样的静态类 - 只在必要时使用。
希望这有助于阐明这个话题。
答案 1 :(得分:4)
实例方法并不比静态更好,它们只是意味着以不同的方式使用。例如,使工厂方法非静态是疯狂的。
一般来说,当方法与类的当前实例(使用此对象的某些状态,可能是配置参数)和静态(如果它只是属于整个类)有关时,请使用实例方法比如创建一个新对象,处理原语......
E.g。 ActiveRecords模式通常使用实例作为记录的映射,因此实例方法读取/操作记录,以及静态方法,如搜索,批量更新/删除等。
答案 2 :(得分:3)
我同意克苏鲁的看法。 创建静态方法意味着您不需要创建类的实例来启动该函数。
我很乐意根据他的ActiveRecord模式使用一个小例子进行解释。
让我们坐下来,我们正在编写自己的AR模式,使用代表表“订单”的类,它包含(如您所能想象的)多个订单记录。
如果您将PDO用作连接类,则需要在某处启动它。 在此示例中,初始化在“order”类的静态方法中启动。因为在能够查询数据库以获取订单之前,您需要能够“运行此”。
class order {
public $data;
public static $connection;
public static function dbConnect($host, $database, $username, $password)
{
try {
self::$connection = new PDO(
'mysql:host='.host.';dbname='.database,
$username,
$password
);
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
public function getOrderById($id)
{
$stmt = self::$connection->prepare('SELECT * FROM orders WHERE id = :id LIMIT 1');
$stmt->execute(
array('id', $id)
);
$this->data = $stmt->fetch();
}
}
$order::dbConnect('localhost', 'myDb', 'user', '123test');
$order1 = new Order();
$order1->getOrderById(1);
$order2 = new order();
$order2->getOrderById(2);
print_r($order1->data);
print_r($order2->data);
请忽略这样一个事实,即您可以对实际结果集进行大量优化或将其保存到实际对象。这段代码的主要目的是解释当我使用静态与实例时的事实。
答案 3 :(得分:1)
首先,它并不总是更好,但在大多数情况下确实如此。 不使用静态方法背后有几个原因:
我强烈邀请您阅读有关单元测试的更多信息。当其他一些类使用静态方法时,将其置于测试中可能是一个挑战。