Различия в static и self для методов класса, простым языком

  • Различия в static и self для методов класса, простым языком

    Антон Долганин 31 Марта 2015 19:55 10186
    Не знаю как вы, а я еще с детского садика плохо усваиваю определения, пока они мне не пригодятся в реальной жизни. Так и с ключевым словом static в классах PHP. Фактически, это замена self, появившееся не так давно (или давно) в PHP, за некоторыми исключениями. Но начну я со своей задачи.

    А задача у меня достаточно хитрая. Представим, что есть класс А, от него наследуется класс Б. И вот хорошо бы, в методе М1 класса Б, вызвать переопределяемый метод М1 родителя А, но с условием, чтобы родитель вызвал метод текущего класса, НО если он есть у пасынка. В ином случае вызывал бы свой метод. Ничего не понятно? Возможно. Но задача для архитектуры оказалась крайне важна, и тут я вспомнил про static, может он мне наконец пригодится.

    Я накидал "абстрактные" три класса, чтобы передать суть:
    class Father
    {
       public static function money()
       {
          return __CLASS__.'\'s money';
       }
    
       public static function life()
       {
          return self::money();//!!!
       }
    }
    
    class Son1 extends Father
    {
       public static function money()
       {
          return __CLASS__.'\'s money';
       }
    
       public static function life()
       {
          return parent::life();
       }   
    }
    
    class Son2 extends Father
    {
       public static function life()
       {
          return parent::life();
       }   
    }
    
    echo Son1::life();
    echo '<br>';
    echo Son2::life(); 

    Человеческим языком это озвучивается просто. У первого сына есть свои деньги, и в жизни он применяет их. А второй сын лоботряс, сидит у папы на шее, и у него нет денег. И вот моя задача, чтобы код вывел:
    Son's money
    Father's money
    Сначала запустим код выше как вы его видите, со старым знакомым self  в строке, помеченное тремя !!!.
    Вывело :(
    Father's money
    Father's money
    И тут вставляем наше слово static:
    class Father
    {
       public static function money()
       {
          return __CLASS__.'\'s money';
       }
    
       public static function life()
       {
          return static::money();//!!!
       }
    }
    
    class Son1 extends Father
    {
       public static function money()
       {
          return __CLASS__.'\'s money';
       }
    
       public static function life()
       {
          return parent::life();
       }   
    }
    
    class Son2 extends Father
    {
       public static function life()
       {
          return parent::life();
       }   
    }
    
    echo Son1::life();
    echo '<br>';
    echo Son2::life();
    .
    Задача решена :)
    Son1's money
    Father's money
    PS: Если вам действительно интересно, почему нельзя было дублировать код отца в сыновьях, применяя self, то вот ответ. В конструкторе обрабатывается много действий, классов может быть много, и некоторые методы отца могут переопределяться в сыновьях. Поэтому, вроде бы и self нужен, но не совсем self.
ivanpanfilov
1 Апреля 2015 8:39
Для выноса общей логики есть трейты. Код будет проще выглядеть вместо нагромождения статик методов.

php net/manual/ru/language.oop5.traits.php
Антон Долганин
1 Апреля 2015 8:56
Иван, спасибо, но это уже понижает понимание кода, да и я решил свою задачу :) нагромождения статик методов не оказалось, он в единственном месте только.
Andrew
1 Апреля 2015 10:03
Хм, а зачем переопределять метод "life" в наследниках?

<?php

class Father
{
   public static function money()
   {
      return __CLASS__.'\'s money';
   }
 
   public static function life()
   {
      return static::money();
   }
}
 
class Son1 extends Father
{
   public static function money()
   {
      return __CLASS__.'\'s money';
   }
}
 
class Son2 extends Father
{
}

printf("%s\n%s", Son1::life(), Son2::life());
   
 
// Output  
$ php -f test.php
Son1's money
Father's money
Антон Долганин
1 Апреля 2015 10:07
зачем переопределять метод "life" в наследниках?
Метод не обязательно переопределять, все верно. Просто до кучи написал. Но если он переопределен - я показал как вызвать еще и родительский.

(суть в том, что точка входа это метод life (как в родителе, так и в сыновьях))
Евгений
9 Апреля 2015 12:33
Внесу долю теории: static позволяет сохранить контекст вызова статического метода, в момент выполнения определяя вызывающий класс, в то время как self - всегда ссылается на текущий класс.