PHP - метод Static vs Instance

Я немного смущен, так как у меня нет большого опыта в ООП в PHP. Я всегда слышу, что использование методов экземпляров лучше, чем использование статических методов. Почему?

Мне нужен глубокий ответ с объяснением, пожалуйста.

Например, почему это нужно сделать с помощью метода экземпляра?

Контроллер:

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, экземпляре vs static и т.д. Но мне нужен полный ответ, чтобы понять, как и почему.

Ответ 1

В целом, как вы уже сказали, методы экземпляров - лучшая практика. Это не означает, что статические методы являются совершенно злыми, они просто имеют другую и уникальную цель.

Важно отметить, что при работе с методами экземпляра вы работаете с объектом, тогда как со статическими методами вы работаете с классом. При использовании статических методов у вас не будет доступа к каким-либо из ваших нестатических свойств, которые обычно доступны в экземпляре.

В качестве примера возьмем следующий код:

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.
}

Если вы хотите получить доступ к свойствам класса из статических методов, эти свойства также должны быть объявлены статическими. Это будет работать в контексте класса выше. Вы также заметите, что экземпляр 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

Итак, статический код означает, что вы работаете непосредственно с классом - экземпляры означают, что вы работаете с объектом. Важно отметить, что любой тип класса, который вы пишете, который должен иметь какое-то состояние в нем, должен использоваться как экземпляр.

Статические классы могут быть немного сложными для отладки и тестирования. Тестирование может быть проблематичным, поскольку статические свойства не изменяются при создании нового экземпляра. Отладка также может быть затруднена, поскольку значение статического свойства одинаково для всех экземпляров класса. Внесите изменения в один, и вы сделаете это изменение во всех них. Отслеживание того, какой экземпляр внес изменения, - это боль.

Говоря метафорически, используйте статические классы, такие как сахар, - экономно и только при необходимости.

Надеюсь, что это поможет пролить свет на эту тему.

Ответ 2

Метод экземпляра не лучше статического, они просто предназначены для использования по-разному. Например, было бы безумным сделать метод factory нестатическим.

Как правило, создайте метод экземпляра, когда метод имеет какое-то отношение к текущему экземпляру класса (может использовать некоторое состояние этого объекта, возможно, параметры конфигурации) и static, если он просто принадлежит классу в целом, как создание нового объекта, обработка примитивы...

например. Шаблон ActiveRecords часто использует экземпляр в качестве отображения записи, поэтому методы экземпляра читают/работают в записи и статические методы, такие как поиск, массовое обновление/удаление и т.д..

Ответ 3

Я согласен с Ктулху. Создание статического метода означает, что вам не нужно создавать экземпляр класса для запуска функции.

Я хотел бы объяснить, используя небольшой пример, основанный на его шаблоне ActiveRecord.

Пусть sat мы пишем наш собственный шаблон 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

Прежде всего, это не всегда лучше, но в большинстве случаев это так. Существует несколько причин не использовать статические методы:

  • Сложнее поставить класс на тесты, если у него есть статические методы
  • Труднее расширить этот класс, если вы хотите немного по-разному поведению для того же метода.
  • Очень часто ваши статические методы в классе - это всего лишь способ сделать его глобальной переменной, которая почти всегда является плохой идеей.
  • Рефакторинг этого класса станет ад, если вдруг вы решите, что вам нужна переменная класса внутри статического метода (измените все вызовы Class:: Method() на $c = new Class(); $c- > Method())

Я настоятельно рекомендую вам прочитать немного больше об модульных тестах. Когда какой-то другой класс использует ваш метод со статическим методом, то его тестирование может быть проблемой.