检查 class 中方法的存在是否违反了 SOLID 原则?
Is checking existence of a method inside a class a violation of SOLID principles?
我有一个名为 Bird 的 class,它在构造函数中接受鸟类数组。我正在尝试在其中实现一个功能,该功能将检查当前是否有任何鸟类在飞行,请记住所有代码都应遵守 SOLID 原则。
我有以下两个classes(鹦鹉和鸵鸟)
class Parrot extends FlyingBirds{}
class Ostrich extends BirdDetail{}
鸟类详情Class
abstract class BirdDetail {
protected $didNotSleepLastNight;
public function __construct(bool $didNotSleepLastNight)
{
$this->didNotSleepLastNight= $didNotSleepLastNight;
}
public function didNotSleepLastNight(): bool
{
return $this->didNotSleepLastNight;
}
}
FlyingBirds(不是所有的鸟都能飞,比如鸵鸟)
abstract class FlyingBirds extends BirdDetail{
protected $isFlyingNow;
public function __construct(bool $didNotSleepLastNight, bool $isFlyingNow)
{
parent::__construct($didNotSleepLastNight);
$this->isFlyingNow = $isFlyingNow;
}
public function isFlyingNow(): bool
{
return $this->isFlyingNow;
}
}
然后我有一个class叫小鸟
class Bird
{
private $details;
public function __construct(array $details)
{
$this->details = $details;
}
public function didNotSleepLastNight(): bool
{
foreach ($this->details as $detail) {
if ($detail->didNotSleepLastNight()) {
return true;
}
}
return false;
}
public function isFlyingNow(): bool
{
foreach ($this->details as $detail) {
if ($detail->isFlyingNow())
{
return true;
}
}
return false;
}
}
现在我将 Parrot 和 Ostrich 的实例传递给 Bird Constructor
$bird = new Bird([new Parrot(true, false), new Ostrich(false)]);
if($bird->isFlyingNow())
{
echo "Yes";
}
else
{
echo "No";
}
问题是上面的代码给我以下错误
Fatal error: Uncaught Error: Call to undefined method Ostrich::isFlyingNow()
那是因为Ostrich/BirdDetailclass没有一个叫isFlyingNow的方法。
可以通过将 Birds class 中的 isFlyingNow 方法替换为以下代码来解决此问题:
public function isFlyingNow(): bool
{
foreach ($this->details as $detail) {
if (method_exists($detail, 'isFlyingNow') && $detail->isFlyingNow())
{
return true;
}
}
return false;
}
你能告诉我上面的修复是否违反了 SOLID 原则吗?还是可以用更好的方式解决问题?
这不违反SOLID原则。如果 Bird 子类拒绝继承的方法,那将违反 Liskov 原则,但这不是你在做的。
我同意你的代码不够优雅。一个更简洁的解决方案是在 Bird 超类中定义一个 isFlyingNow
抽象方法,在 Ostrich 和其他非飞行 类 中实现 return false
。那么你就不需要在运行时检查对象是否提供了这个方法。
并没有真正破坏 SOLID,只是设计不是很好。
为了更简洁的方法,请使用接口。
因为不是所有的鸟都能飞,所以对所有的鸟都采用 public 面向方法 isFlying()
似乎相当浪费。
也许你可以这样做:
您的基地 Bird
class,您可以在其中定义所有鸟类的通用属性和方法。
class Bird {
}
然后,不同的接口来描述Bird子class不同的可能行为。有的鸟会飞,有的会游泳,有的会说话,有的会唱歌,有的会潜水,有的会运行,等等
对于传单:
interface FlyingBird {
function isFlying():bool;
function takeOff();
function land();
function fly($bearing, $distance);
}
对于谈话者:
interface TalkingBird {
function say($something);
}
歌手:
interface SingingBird {
function sing(array $notes);
}
游泳者(您可能需要区分那些在水面上游泳的人和那些可以潜入水面下的人)。
interface SwimmingBird {
function isSwimming(): bool;
// etc
}
对于运行人:
interface RunningBird {
function isRunning(): bool;
// etc
}
那么你可能会有 class 像 Parrot
(会飞会说话,但不会唱歌、运行不会游泳)
class Parrot extends Bird implements TalkingBird, FlyingBird {
// todo: actual implementation
}
或Ostrich
(会运行,但不会游泳、唱歌、说话或飞行):
class Ostrich extend Birds implements RunningBird { /* implementation */}
甚至Penguin
(会游泳,不会飞,不会运行,不会唱歌,不会说话):
class Penguin extends Bird implements SwimmingBird { /* implementation */ }
等等等等,整体@package Ornithology
初具规模
这些 classes 的用户应该检查实例是否实现了适当的接口:
if ($birdInstance instanceof FlyingBird && $birdInstance->isFlying()) {
echo "This bird is flying!";
}
为了简化这些 class 的组合,您可能需要创建一些特征:
例如
trait FlyingBirdTrait {
private $flying = false;
function isFlying():bool {
return $this->flying;
}
function takeOff() {
$this->flying = true;
}
function land() {
$this->flying = false;
}
function fly($bearing, $altitude, $distance) {
if (!$this->isFlying()) {
$this->takeOff();
}
// calculate new position;
}
}
然后 class 像 Parrot
可以使用:
class Parrot extends Bird implements TalkingBird, FlyingBird {
uses FlyingBirdTrait;
// rest of the implementation, etc;
}
我有一个名为 Bird 的 class,它在构造函数中接受鸟类数组。我正在尝试在其中实现一个功能,该功能将检查当前是否有任何鸟类在飞行,请记住所有代码都应遵守 SOLID 原则。
我有以下两个classes(鹦鹉和鸵鸟)
class Parrot extends FlyingBirds{}
class Ostrich extends BirdDetail{}
鸟类详情Class
abstract class BirdDetail {
protected $didNotSleepLastNight;
public function __construct(bool $didNotSleepLastNight)
{
$this->didNotSleepLastNight= $didNotSleepLastNight;
}
public function didNotSleepLastNight(): bool
{
return $this->didNotSleepLastNight;
}
}
FlyingBirds(不是所有的鸟都能飞,比如鸵鸟)
abstract class FlyingBirds extends BirdDetail{
protected $isFlyingNow;
public function __construct(bool $didNotSleepLastNight, bool $isFlyingNow)
{
parent::__construct($didNotSleepLastNight);
$this->isFlyingNow = $isFlyingNow;
}
public function isFlyingNow(): bool
{
return $this->isFlyingNow;
}
}
然后我有一个class叫小鸟
class Bird
{
private $details;
public function __construct(array $details)
{
$this->details = $details;
}
public function didNotSleepLastNight(): bool
{
foreach ($this->details as $detail) {
if ($detail->didNotSleepLastNight()) {
return true;
}
}
return false;
}
public function isFlyingNow(): bool
{
foreach ($this->details as $detail) {
if ($detail->isFlyingNow())
{
return true;
}
}
return false;
}
}
现在我将 Parrot 和 Ostrich 的实例传递给 Bird Constructor
$bird = new Bird([new Parrot(true, false), new Ostrich(false)]);
if($bird->isFlyingNow())
{
echo "Yes";
}
else
{
echo "No";
}
问题是上面的代码给我以下错误
Fatal error: Uncaught Error: Call to undefined method Ostrich::isFlyingNow()
那是因为Ostrich/BirdDetailclass没有一个叫isFlyingNow的方法。
可以通过将 Birds class 中的 isFlyingNow 方法替换为以下代码来解决此问题:
public function isFlyingNow(): bool
{
foreach ($this->details as $detail) {
if (method_exists($detail, 'isFlyingNow') && $detail->isFlyingNow())
{
return true;
}
}
return false;
}
你能告诉我上面的修复是否违反了 SOLID 原则吗?还是可以用更好的方式解决问题?
这不违反SOLID原则。如果 Bird 子类拒绝继承的方法,那将违反 Liskov 原则,但这不是你在做的。
我同意你的代码不够优雅。一个更简洁的解决方案是在 Bird 超类中定义一个 isFlyingNow
抽象方法,在 Ostrich 和其他非飞行 类 中实现 return false
。那么你就不需要在运行时检查对象是否提供了这个方法。
并没有真正破坏 SOLID,只是设计不是很好。
为了更简洁的方法,请使用接口。
因为不是所有的鸟都能飞,所以对所有的鸟都采用 public 面向方法 isFlying()
似乎相当浪费。
也许你可以这样做:
您的基地 Bird
class,您可以在其中定义所有鸟类的通用属性和方法。
class Bird {
}
然后,不同的接口来描述Bird子class不同的可能行为。有的鸟会飞,有的会游泳,有的会说话,有的会唱歌,有的会潜水,有的会运行,等等
对于传单:
interface FlyingBird {
function isFlying():bool;
function takeOff();
function land();
function fly($bearing, $distance);
}
对于谈话者:
interface TalkingBird {
function say($something);
}
歌手:
interface SingingBird {
function sing(array $notes);
}
游泳者(您可能需要区分那些在水面上游泳的人和那些可以潜入水面下的人)。
interface SwimmingBird {
function isSwimming(): bool;
// etc
}
对于运行人:
interface RunningBird {
function isRunning(): bool;
// etc
}
那么你可能会有 class 像 Parrot
(会飞会说话,但不会唱歌、运行不会游泳)
class Parrot extends Bird implements TalkingBird, FlyingBird {
// todo: actual implementation
}
或Ostrich
(会运行,但不会游泳、唱歌、说话或飞行):
class Ostrich extend Birds implements RunningBird { /* implementation */}
甚至Penguin
(会游泳,不会飞,不会运行,不会唱歌,不会说话):
class Penguin extends Bird implements SwimmingBird { /* implementation */ }
等等等等,整体@package Ornithology
初具规模
这些 classes 的用户应该检查实例是否实现了适当的接口:
if ($birdInstance instanceof FlyingBird && $birdInstance->isFlying()) {
echo "This bird is flying!";
}
为了简化这些 class 的组合,您可能需要创建一些特征:
例如
trait FlyingBirdTrait {
private $flying = false;
function isFlying():bool {
return $this->flying;
}
function takeOff() {
$this->flying = true;
}
function land() {
$this->flying = false;
}
function fly($bearing, $altitude, $distance) {
if (!$this->isFlying()) {
$this->takeOff();
}
// calculate new position;
}
}
然后 class 像 Parrot
可以使用:
class Parrot extends Bird implements TalkingBird, FlyingBird {
uses FlyingBirdTrait;
// rest of the implementation, etc;
}