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