Переопределение методов в PHP?

#php #oop #inheritance #overriding

Вопрос:

В других языках OO, таких как Java , мы можем переопределить функцию, возможно, используя ключевые слова/аннотации, такие как implements и @override т. Д.

Есть ли способ сделать это в PHP? Я имею в виду, например:

 class myClass {
    public static function reImplmentThis() { //this method should be overriden by user
    }
}
 

Я хочу, чтобы пользователь реализовал свой собственный myClass::reImplementThis() метод.

Как я могу это сделать на PHP? Если это возможно, могу ли я сделать это необязательным?

Я имею в виду, если пользователь не реализует метод, могу ли я указать метод по умолчанию или могу определить, что метод не определен (могу ли я сделать это с помощью method_exists )?

Комментарии:

1. Именно для этого и существует наследование. php.net/manual/en/language.oop5.inheritance.php

2. В общем, я бы сделал это нестатичным, а затем пользователь может просто расширить MyClass, выбирая, переопределять его или нет reImplmentThis() . Вам не нужно проверять наличие реализации пользователя, если ваша реализация по умолчанию приемлема.

3. Вы можете использовать и абстрактный класс для расширения (как говорит халфер), и класс интерфейса ( killerphp.com/articles/php-interfaces ) чтобы убедиться, что конечный класс имеет, по крайней мере, указанные вами функции. Я только сейчас узнаю об этих (интерфейсах) Возможно, я ошибаюсь, но из того, что я прочитал, кажется, что именно в этом причина их наличия.

Ответ №1:

 <?php
abstract class Test
{
    abstract protected function test();

    protected function anotherTest() {

    }
}

class TestTest extends Test
{
    protected function test() {

    }
}

$test = new TestTest();
?>
 

Таким образом, тест класса TestTest должен переопределять функциональный тест.

Комментарии:

1. В случае операции reImplementThis() было бы объявлено неабстрактным, поскольку реализация по умолчанию в порядке. Но да, 1.

2. @Dillen это не ответ на вопрос, который задал пользователь, это просто вариант того, как это должно быть сделано, но не удовлетворяет тому, что спрашивает пользователь

Ответ №2:

Это затрагивает несколько тем ООП.

Во-первых, простое переопределение метода, объявленного в родительском классе, так же просто, как повторное объявление метода в наследующем классе.

Например:

 class Person {

    public function greet(string $whom) {
        echo "hello $whom!";
    }

}

class Tommy extends Person {
    public function greet(string $whom = "everyone") {
        echo "Howdy $whom! How are you?";
    }
}

$a = new Tommy();
$a->greet('World');

// outputs:
// Howdy World! How are you?
 

Если в переопределяющем методе вы хотите повторно использовать логику переопределенного, это просто вопрос вызова родительского метода из расширяющегося класса::

 class Tommy
{
    public function greet(string $whom)
    {
        // now with more emphasis!!!
        echo parent::greet(strtoupper($whom)) . "!!!!";
    }
}
 

Теперь Tommy::greet() вызывает Person::greet() , но изменяет результат перед его возвратом.

Следует отметить, что переопределяющие методы должны быть совместимы с переопределенными: видимость метода не может быть более ограничительной, чем исходная (можно увеличить видимость), а количество и тип требуемых аргументов не могут конфликтовать с исходным удалением.

Это работает, потому что тип аргументов не конфликтует с исходным, и у нас меньше требуемых аргументов, чем у родительского:

 class Leo extends Person {
    public function greet(string $whom = "gorgeous", string $greet = "Whatsup" ) {
        echo "$greet $whom. How are you?";
    }
}
 

Но это не так, поскольку существуют дополнительные необходимые аргументы. Это сделало бы невозможным прозрачное переключение исходного класса на этот и, таким образом, привело бы к Warning :

 
class BadBob extends Person {
    public function greet(string $whom, string $greet ) {
        echo "$greet $whom. How are you?";
    }
}
 

Кроме того, вы упоминаете в своем вопросе, что «этот метод должен быть переопределен пользователем». Если вам требуются клиентские классы для реальной реализации метода, у вас есть несколько вариантов:

Абстрактные классы и методы

Это методы, в которых реализация оставлена пустой и которые должны быть реализованы расширяющимися классами, чтобы быть допустимыми. В мы изменили наш первоначальный класс Person на:

 abstract class Person {

    public function greet(string $whom) {
        echo "hello $whom!";
    }

    public abstract function hide();

}
 
  • Поскольку теперь класс содержит абстрактный метод, его также необходимо объявить как абстрактный класс.
  • Теперь невозможно создать экземпляр Person напрямую, вы можете только расширить его в других классах.
  • Теперь все наши существующие Person расширяющиеся классы были бы неправильными, и попытка выполнить предыдущий код привела бы к фатальной ошибке.

Примером допустимого расширения класса Person сейчас было бы:

 class Archie extends Person {

    public function hide() {
        echo "Hides behind a bush";
    }
}
 

Любой класс, который расширяется, Person должен объявить открытый hide() метод.

Интерфейсы

Наконец, вы упомянули интерфейсы. Интерфейсы — это контракты, которые должны выполнять реализующие классы. Они объявляют группу общедоступных методов без органа по реализации.

Напр.:

 
interface Policeman {
    public function arrest(Person $person) : bool;
        
    public function help($what): bool;
}
 

Теперь у нас мог бы быть класс, который был бы расширен Person и реализован Policeman :

 class Jane extends Person implements Policeman {
    
    public function hide() {
        echo "Jane hides in her patrol-car";
    }
    
    public function arrest(Person $person): bool{
        // implement arrest method
        
        return false;
    }
    
    public function shoot($what): bool {
        // implements shoot() method
        
        return false;
    }
}

 

Важно отметить, что, хотя можно расширить только один класс (в PHP нет множественного наследования), можно реализовать несколько интерфейсов, и требования для каждого из них должны быть выполнены, чтобы класс был действительным.

Ответ №3:

Да, есть. У вас есть возможность переопределить метод, расширив класс и определив метод с тем же именем, сигнатурой функции и спецификатором доступа (открытым или защищенным), который был у него в базовом классе. Метод не должен быть объявлен абстрактным в базовом классе, иначе вам потребуется реализовать его в производном классе. В вашем примере это выглядело бы примерно так:

 class MyClass {
    public static function reImplmentThis() { //this method should be overriden by user
    }
}

class MyDerivedClass extends MyClass {
    public static function reImplmentThis() { //the method you want to call
    }
}
 

Если пользователь не переопределит его, у MyDerivedClass все равно будет метод reimplementthis (), унаследованный от MyClass.

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

Да, можно проверить, реализован метод или нет, и получить гораздо больше информации о классе, используя отражение PHP.