Является ли проверка существования метода внутри класса нарушением принципов SOLID? - PullRequest
2 голосов
/ 24 марта 2019

У меня есть класс с именем Bird, который принимает массив птиц в конструкторе. Я пытаюсь реализовать внутри него функцию, которая будет проверять, летит ли какая-нибудь птица, имея в виду, что весь код должен соответствовать принципам SOLID.

У меня есть два следующих класса (Попугай и Страус)

class Parrot extends FlyingBirds{}   
class Ostrich extends BirdDetail{}

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;
 }
}   

Тогда у меня есть класс, который называется Bird

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;
  }
}

Теперь я передаю экземпляры попугая и страуса конструктору птиц

$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 / BirdDetail не имеет метода isFlyingNow.

Проблема может быть исправлена ​​путем замены метода isFlyingNow в классе Birds следующим кодом:

public function isFlyingNow(): bool
{

    foreach ($this->details as $detail) {

        if (method_exists($detail, 'isFlyingNow') && $detail->isFlyingNow())             
        {
            return true;
        }
    }

    return false;

}

Не могли бы вы сказать мне, является ли указанное выше исправление нарушением принципов SOLID? Или можно решить проблему лучше?

Ответы [ 2 ]

3 голосов
/ 25 марта 2019

На самом деле не ломает SOLID, но просто не очень хорошо продуман.

Для более аккуратного подхода используйте интерфейсы.

Так как не все птицы могут летать, использование метода публичной борьбы isFlying() для всех птиц представляется довольно расточительным.

Может быть, вы могли бы сделать что-то вроде этого:

Ваш базовый Bird класс, в котором вы определяете общие свойства и методы для всех птиц.

class Bird {
}

Затем, различные интерфейсы для описания различных возможных поведений подклассов Bird. Некоторые птицы летают, некоторые плавают, некоторые разговаривают, некоторые поют, некоторые ныряют, некоторые могут бежать и т. Д. И т. Д.

Для флаеров:

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
}

Тогда у вас могут быть такие классы, как 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 обретает форму.

Пользователи этих классов должны проверить, реализуют ли экземпляры соответствующие интерфейсы:

if ($birdInstance instanceof FlyingBird && $birdInstance->isFlying()) {
   echo "This bird is flying!";
}

Чтобы упростить составление этих классов, вы можете создать некоторые черты:

* 1051 Е.Г. *

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;

    }
}

Какие классы типа Parrot могли бы использовать:

class Parrot extends Bird implements TalkingBird, FlyingBird {
    uses FlyingBirdTrait;
    // rest of the implementation, etc;
}
0 голосов
/ 25 марта 2019

Это не является нарушением принципа SOLID. Было бы нарушением принципа Лискова, если подкласс Bird отклонил унаследованный метод, но это не то, что вы делаете.

Я согласен, что ваш код не элегантный. Более чистым решением было бы использование абстрактного метода isFlyingNow, определенного в суперклассе Bird, для возврата false в страусиных и других нелетающих классах. Тогда вам не нужно будет проверять во время выполнения, предоставляет ли объект этот метод или нет.

...