PHP - 静态与实例方法

时间:2015-05-22 17:51:52

标签: php oop

我有点困惑,因为我在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>

4 个答案:

答案 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

由于$fooFoo的实例,因此它可以访问整个类。调用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)

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

  1. 如果有静态方法,那么将课程放在测试上会更加困难
  2. 如果你想为同一个方法做一点点不同的行为,那么扩展这个类就更难了
  3. 很多时候,你的静态方法只是让它成为全局变量的一种方式,这几乎总是一个坏主意
  4. 如果突然您决定在静态方法中需要一些类变量(将所有Class :: Method()调用更改为$ c = new Class(); $ c-&gt; Method()),重构此类将变得很糟糕
  5. 我强烈邀请您阅读有关单元测试的更多信息。当其他一些类使用静态方法时,将其置于测试中可能是一个挑战。