【发布时间】:2011-09-12 22:56:25
【问题描述】:
我很好奇以下行为是否有“更好”的设计:
<?php
class Foo {
public function foo() {
// Foo-specific foo stuff.
}
}
class Bar extends Foo {
public function foo() {
// Bar-specific foo stuff.
parent::foo();
}
}
class Baz extends Bar {
public function foo() {
// Baz-specific foo stuff.
parent::foo();
}
}
$boz = new Foo();
$boz->foo(); // should do the stuff in Foo::foo()
$biz = new Bar();
$biz->foo(); // should do the stuff in Bar::foo() and Foo::foo()
$buz = new Baz();
$buz->foo(); // should do the stuff in Baz::foo(), Bar::foo(), and Foo::foo()
// etc...
基本上,我有一个基类Foo,其方法Foo::foo() 包含一些应始终运行的通用代码。我还有各种继承自 Foo 的子类,每个子类都有自己的特定代码,这些代码也应该始终运行。
我这里使用的设计使用了 DRY 原则,确保Foo::foo() 中的代码在Bar::foo() 和Baz::foo() 中不重复,Bar::foo() 中的代码在@987654330 中不重复@,等等。
这种设计的问题(?)是我依赖子类在每种情况下始终显式调用parent::foo(),以及扩展这些类以执行相同操作的类,等等无限。但是,没有办法(据我所知)实际执行此操作。
所以我的问题是 - 是否有更好的设计来完成相同的行为,或者以某种方式在父/子类之间强制执行这种“合同”?
更新
有些人要求提供用例。多年来,我在多个项目中遇到过这种范例,但由于 NDA 等原因无法给出真实世界的示例,所以这里有一个超级基本的示例,可能有助于更好地说明这个问题:
<?php
// Vehicle
class Vehicle {
public function start() {
// Vehicle engines are on when you start them.
// Unless they belong to me, that is :-(
$this->setEngineStatus(Vehicle::ENGINE_ON);
}
}
// Vehicle > Automobile
class Automobile extends Vehicle {
public function start() {
// Automobile engines are on when you start them.
parent::start();
// Automobiles idle when you start them.
$this->setEngineRpm(Automobile::RPM_IDLE);
}
}
// Vehicle > Airplane
class Airplane extends Vehicle {
public function start() {
// Airplane engines are on when you start them.
parent::start();
// Airplanes also have radios that need to be turned on when started.
$this->setRadioStatus(Airplane::RADIO_ON);
}
}
// Vehicle > Automobile > Car
class Car extends Automobile {
public function start() {
// Cars engines are on and idle when you start them.
parent::start();
// Cars also have dashboard lights that turn on when started.
$this->setDashLightsStatus(Car::DASH_LIGHTS_ON);
}
}
// Vehicle > Airplane > Jet
class Jet extends Airplane {
public function start() {
// Jet engines and radios are on when you start them.
parent::start();
// Jets also arm their weapons when started.
$this->setWeaponsHot(true);
}
}
// Vehicle > Automobile > BobsSuperAwesomeCustomTruck
class BobsSuperAwesomeCustomTruck extends Automobile {
public function start() {
// Uh-oh... Bob didn't call parent::start() in his class, so his trucks
// don't work, with no errors or exceptions to help him figure out why.
// Bob's trucks also need to reset their pinball machine highscores when started.
$this->resetPinballScores();
}
}
【问题讨论】:
-
您可以将其设计为从基类抽象继承:php.net/manual/en/language.oop5.abstract.php
-
我认为不仅在 php 中,而且在任何类似 C 的编程语言中都没有。实际上,我认为 OOP 概念中没有更好的方法。
-
@Lawrence,这并不强制在扩展类中使用 parent::foo()。
-
这不是 PHP 特有的。它是 OOP 范式的一部分。就像在现实生活中一样,孩子继承父母的特质。父母不能对孩子、孙子、曾孙等强制特征。
-
这是一个很好的比喻,赫伯特。