【问题标题】:PHP - Static vs Instance MethodPHP - 静态与实例方法
【发布时间】:2015-05-22 17:51:52
【问题描述】:

我有点困惑,因为我在 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、实例与静态等方面的内容。但我需要一个完整的答案来了解事物的如何和为什么。

【问题讨论】:

  • 对于static 方法,您必须传递所有它需要操作的信息。如果它必须操纵它,这包括类的“实例”。这只是一个正常的“传递一切的过程调用”。即“标准”过程调用。但是所有过程都集中在“类”名称中,因此当您在代码中使用它们时,“自我记录”它们“生活”在哪里。请记住:它们只是普通的函数调用。
  • 对于instance 方法调用,那么它是否完全不同...instance 是一个独立的实体,它拥有自己的数据并可以访问“类”的“静态”方法。当调用instance 方法时,它会将instance 作为非静态“类”方法的第一个参数传递。真的就是这么简单。
  • @RyanVincent 实际上,使用实例您必须传递操作所需的所有对象。使用静态方法,您可以通过单例轻松访问主对象

标签: php oop


【解决方案1】:

一般来说,正如您已经说过的,实例方法是更好的做法。这并不是说静态方法是彻头彻尾的邪恶,它们只是具有不同且独特的目的。

重要要注意,在处理实例方法时,您使用的是 object,而使用静态方法时,您使用的是 class。使用静态方法时,您将无法访问通常可用于实例的任何非静态属性。

以如下代码为例:

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 可用于object 而不是class。

// Calling this method would trigger an error
public static function changeBar($value)
{
    $this->bar = $value; // PHP will crash and burn if you try this.
}

如果您想从静态方法访问类属性,这些属性也必须声明为静态。这将在上述类的上下文中起作用。您还会注意到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

同样,静态代码意味着您正在直接使用类 - 实例意味着您正在使用对象。重要的是要注意,您编写的任何类型的想要对其具有某种状态的类都应该用作实例。

静态类可能有点难以调试和测试。测试可能会出现问题,因为在创建新实例时静态属性不会改变。调试也很困难,因为静态属性的值在类的所有实例中都是相同的。对其中一个进行更改,您将在所有这些中进行更改。跟踪哪个实例进行了更改是一件很痛苦的事情。

打个比方,使用像糖这样的静态类 - 谨慎使用,仅在必要时使用。

希望这有助于阐明这个主题。

【讨论】:

  • 优秀的答案。非常感谢楼主!!
  • 除此之外,PHPUnit 不能模拟/存根静态方法:(phpunit.de/manual/6.5/en/test-doubles.html),所以最好的做法是尽可能避免使用静态方法,除非你是认真的。如果你看一下 GitHub 上的 PHP 项目(例如 Symfony/Yaml),你会发现静态方法用于方便地实例化另一个对象,并且它内部没有复杂的逻辑(github.com/symfony/yaml/blob/master/Yaml.php#L53)
【解决方案2】:

实例方法并不比静态方法好,它们只是为了以不同的方式使用。例如,将工厂方法设为非静态是很疯狂的。

当方法与类的当前实例(使用此对象的某些状态,可能是配置参数)有关时,通常创建实例方法,如果它只是属于整个类,则为静态比如创建一个新对象,处理原语......

例如ActiveRecords 模式经常使用实例作为记录的映射,因此实例方法对记录进行读取/操作,以及搜索、批量更新/删除等静态方法。

【讨论】:

  • 你能给我一个静态和非静态的例子以及它们的区别吗?
  • @Makis 我回答的最后一段尝试了这一点。
  • 哦,对不起。误会了!
【解决方案3】:

我同意克苏鲁的观点。 制作静态方法意味着您不需要创建类的实例来启动函数。

我很想用一个基于他的 ActiveRecord 模式的小例子来解释一下。

坐下来,我们正在使用一个表示“订单”表的类来编写我们自己的 AR 模式,其中包含(就像您想象的那样)多个订单记录。

如果您将 PDO 用作连接类,则需要在某个地方启动它。 在本例中,初始化是在“订单”类的静态方法中启动的。因为您需要能够“运行它”才能查询数据库以获取订单。

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);

请忽略您可以对实际结果集进行很多优化或将其保存到实际对象的事实。这段代码的主要目的是解释我何时使用静态与实例。

【讨论】:

    【解决方案4】:

    首先,它并不总是更好,但在大多数情况下确实如此。 不使用静态方法有几个原因:

    1. 如果类具有静态方法,则更难将其置于测试中
    2. 如果您希望同一方法的行为稍有不同,则扩展此类会更加困难
    3. 通常,您在类中的静态方法只是使其成为全局变量的一种方式,这几乎总是一个坏主意
    4. 如果你突然决定在静态方法中需要一些类变量(将所有 Class::Method() 调用更改为 $c = new Class(); $c->Method())

    我强烈邀请您阅读更多关于单元测试的内容。当其他一些类使用您的静态方法时,将其用于测试可能是一个挑战。

    【讨论】:

    • 你能给我一个静态和非静态的例子以及它们的区别吗?
    猜你喜欢
    • 1970-01-01
    • 2012-11-12
    • 2013-12-14
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    相关资源
    最近更新 更多