Принудительное переопределение метода родительского класса Stack Overflow

У меня есть ситуация, которая, несмотря на значительное количество поисков в Google / SO’ing, кажется, ускользает от меня. Вот проблема в двух словах:

ситуация

PHP 5.3+ (но пока не конкретный — на самом деле я использую 5.5 / 6, но среда стремится быть гибкой).

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

У меня есть родительский класс:

class A {
public function doSomething() {
// does something
}
}

Стандартный вариант использования заключается в использовании A::doSomething,

В особых случаях требуется расширение.

class B extends A {
public function doSomething() {
// does something .. AND
$this->doSomethingElse()
}

private function doSomethingElse() {
// does something else
}
}

Если я захочу сделай что-нибудь, тогда я могу A::doSomething,
Если я захочу делать что-то и делать что-то еще, тогда я могу B::doSomething,

Для того, чтобы соответствовать сфере компетенции принуждение других разработчиков к лучшей практике, Я реализовал интерфейс:

interface C {

public function doSomething() {
// does something else
}

public function doSomethingElse() {
// does something else
}
}

Класс B затем реализует интерфейс C. Звучит достаточно просто?

class B implements C {
public function doSomething() {
// does something .. AND
$this->doSomethingElse()
}

private function doSomethingElse() {
// does something else
}
}

Я не хочу предполагать, что ребенок B инвентарь C,
Там может быть ряд вполне обоснованных причин, чтобы продлить A в E, где E не хочет реализовывать C,

Мой актуальный вопрос:

Если я сделаю следующее, и не реализую doSomething Метод, следующий не имеет ошибок:

class B implements C {
private function doSomethingElse() {
// does something else
}
}

.. что и следовало ожидать; A обеспечивает doSomething за B удовлетворить требование для C,

Проблема в том, что это позволило бы кому-то написать неполный метод, который не смог бы выдавать ошибки. В приведенном выше случае B::doSomething не звонит B::doSomethingElse,

Возможно ли для метода, интерфейса (или аналогичного метода) требовать ребенок, чтобы реализовать метод на текущем уровне наследования?

Конечно, я могу писать заметки, документацию и т. Д., Но смысл интерфейса — заставить людей делать это правильно!

Другие люди, которые задавали подобные вопросы, были либо (иногда понятно) неправильно поняты или же стандартный ответ был «архитектура неверна» … Что я хотел бы оспорить, как показано в примере ниже:

Вот пример с реальными предметами:

  • A = Магазин для продажи: ShopItem
  • B = Магазин типа «Пиво»: Пиво
  • C = BoozeInterface: интерфейс, который, скажем, запускает требования к идентификатору
  • D = Магазин типа «Овощи»: Овощ

Возможно, есть метод, называемый, скажем … A::attemptToBuy
C будет применять методы, такие как ID требуется.

Beer а также Vegetable оба типа ShopItem.
Это стандартное и логичное наследование.

Beer необходимо использовать BoozeInterface (или что там уместно). Это также относится к еще не выполненному, но, возможно, будущему требованию Wine, Spirit и т. д.

Некоторые предметы идеально вписываются в общий ShopItem учебный класс. Им не нужна дополнительная функциональность.
ShopItem::attemptToBuy это нормальный вариант использования.

Однако — я должен положиться на кого-то, кто помнит, чтобы переопределить Wine::attemptToBuy а также Spirit::attemptToBuy,

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

Я счастлив, если ответ «нет, ты не можешь этого сделать» … Я просто хочу знать, если ты можешь. У меня есть код работает, но только через прямое переопределение, которое является принудительным. Но я хочу, чтобы это было принудительно!

Заранее спасибо.
стог

3

Решение

Почему бы не сделать класс A абстрактным и не использовать эти методы следующим образом:

abstract class A
{
abstract protected function doSomething() {}
abstract protected function doSomethingElse() {}
}

Теперь любой класс, который расширяет класс A, должен определять эти два метода.

Ура!

1

Другие решения

Я думаю, что вы идете в обратном направлении — я не уверен, что вы хотите применить переопределение (легко сделать с помощью абстрактных методов) в созданных клиентом-разработчиком подклассах, вы хотите предоставить метод, который не могу быть переопределенным в предоставленных вами абстрактных классах, чтобы гарантировать, что он делает то, что вы хотите?

Если вы предоставляете абстрактные классы ShopItem а также BoozeItem таким образом:

<?php

// base ShopItem with default `attemptToBuy` functionality
abstract class ShopItem {

public function attemptToBuy() {
echo "buying ShopItem";
}
}

// Booze sub-class, with an override on `attemptToBuy`
// (e.g. applying age restrictions check)
abstract class BoozeItem extends ShopItem {

final public function attemptToBuy() {
echo "buying BoozeItem";
}
}

?>

Клиент-разработчик может затем счастливо создать Beer класс, расширяющий BoozeItem суб-класс.

<?php

class Beer extends BoozeItem { }

$oBevvy = new Beer();
$oBevvy->attemptToBuy();

?>

Выходы: покупка BoozeItem

поскольку BoozeItem::attemptToBuy() был объявлен final это не может быть отменено Booze::attemptToBuy() (попытка создать этот метод приведет к ошибке) — так что функциональность вашего объекта заблокирована.

Beer может быть расширен до Lager или же Ale (например), и те унаследуют attemptToBuy() от BoozeItem (поскольку Beer продолжается BoozeItem) но им не разрешат переопределить этот метод, поскольку он объявлен final — это просто будет наследоваться с определенным поведением. Это не то, что вы хотите, но это, вероятно, ближе всего, я думаю.

0

По вопросам рекламы [email protected]