Php, запуск кода в зависимости от типа объекта вызывает зависимости

Давайте создадим список типов животных:

abstract class Item
{
public function run()
{
echo __FUNCTION__.'<br>';
}
}

class Reptile extends Item
{
public function putEgg()
{
echo __FUNCTION__.'<br>';
}
}

class Mammal extends Item
{
public function born()
{
echo __FUNCTION__.'<br>';
}
}

$list = [];
for ($i = 1; $i <= 10; $i++)
{
switch(mt_rand(1,2))
{
case 1 :
$o = new Reptile();
break;
case 2 :
$o = new Mammal();
break;
}
$list[] = $o;
}

теперь в другом месте я хотел бы перечислить их:

class Test
{
public function dump(array $list)
{
foreach ($list as $o)
{
/**
* @var Item $o
*/
echo '<hr>';
echo get_class($o).':<br>';
$o->run();
if ($o instanceof Mammal)
{
$o->born();
}
if ($o instanceof Reptile)
{
$o->putEgg();
}
}
}
}

(new Test())->dump($list);

сейчас моя проблема сейчас Test класс связан с Item и все его потомки. Если я сделаю рефакторинг целого так:

abstract class Item
{
public function run()
{
echo __FUNCTION__.'<br>';
}

public function isReptile()
{
return $this instanceof Reptile;
}

public function isMammal()
{
return $this instanceof Mammal;
}
}

class Reptile extends Item
{
public function putEgg()
{
echo __FUNCTION__.'<br>';
}
}

class Mammal extends Item
{
public function born()
{
echo __FUNCTION__.'<br>';
}
}

$list = [];
for ($i = 1; $i <= 10; $i++)
{
switch(mt_rand(1,2))
{
case 1 :
$o = new Reptile();
break;
case 2 :
$o = new Mammal();
break;
}
$list[] = $o;
}

//
class Test
{
public function dump(array $list)
{
foreach ($list as $o)
{
/**
* @var Item $o
*/
echo '<hr>';
echo get_class($o).':<br>';
$o->run();
if ($o->isMammal())
{
$o->born();
}
if ($o->isReptile())
{
$o->putEgg();
}
}
}
}

(new Test())->dump($list);

с тех пор выглядит немного лучше Test а также Item зависимости устранены. Все еще пахнет из-за isMammal(), isReptile()… это означает, что каждый раз, когда рождается новый тип, Item должен обновляться. Тем не менее, это плохая практика, которую базовый класс знает о своих потомках. Что такое элегантный способ?

1

Решение

Я полагаю, вы не хотите, чтобы конечный метод рос в вертикальном размере. Я предлагаю использовать интерфейсы для анализа Item структура, а не его класс.

<?php
/*
* Interfaces first.
* They will allow us to build a "contract" between calling class and
* actual implementations. Also, only interfaces MUST be used in end class.
*/
interface ViviparousInterface
{
public function giveBirth();
}

interface OviparousInterface
{
public function layEgg();
}

interface SpawningInterface
{
public function layCaviar();
}

/*
* Now implemetation classes:
*/
abstract class Item
{
public function run()
{
echo __FUNCTION__ . '<br>';
}
}

class Reptile extends Item implements OviparousInterface
{
public function layEgg()
{
echo __FUNCTION__ . '<br>';
}
}

class Mammal extends Item implements ViviparousInterface
{
public function giveBirth()
{
echo __FUNCTION__ . '<br>';
}
}

class Fish extends Item implements SpawningInterface
{
public function layCaviar()
{
echo __FUNCTION__ . '<br>';
}
}

class ShomethingElse extends Item implements ViviparousInterface
{
public function giveBirth()
{
echo __FUNCTION__ . '<br>';
}
}

/**
* Test class:
*/
class Test
{
public function dump(array $list)
{
foreach ($list as $o)
{
/**
* @var Item $o
*/
echo '<hr>', get_class($o) . ':<br>';

$o->run();

/*
* Here we do not care about actual classes.
* We do know, that if they implement one of the interfaces,
* then they will have required methods.
*/
if ($o instanceof ViviparousInterface) {
$o->giveBirth();
} elseif ($o instanceof OviparousInterface) {
$o->layEgg();
} elseif ($o instanceof SpawningInterface) {
$o->layCaviar();
}
}
}
}

/*
* Test case:
*/
$list = [];

for ($i = 1; $i <= 10; $i++)
{
switch(mt_rand(1, 4))
{
case 1:
$o = new Reptile();
break;

case 2:
$o = new Mammal();
break;

case 3:
$o = new Fish();
break;

case 4:
$o = new ShomethingElse();
break;
}

$list[] = $o;
}

(new Test())->dump($list);

В конце концов, независимо от того, насколько актуально Item потомки у вас будут в будущем, ваши Test:dump() Метод будет использовать только анализ структуры класса. Это значительно уменьшит дальнейший рост размеров.

Дальнейшее чтение:

  1. Какой смысл интерфейсов в PHP?
  2. Построить семь хороших объектно-ориентированных привычек в PHP
1

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

Используйте интерфейс

Определите интерфейс и убедитесь, что все животные реализуют это.

interface Animal {
public function born();
}

Теперь все животные должны реализовать это и реализовать функции, определенные в интерфейсе.

class Reptile implement Animal {
public function born()
{
return 'new baby reptile';
}
}

class Mammal implement Animal {
public function born()
{
return 'new baby mammal';
}
}

class Test {
public function makeBaby(Animal $animal)
{
echo $animal->born();
}
}

(new Test())->makeBaby(new Reptile());
(new Test())->makeBaby(new Mammal());
2

Я нашел хороший обходной путь. У меня будут однородные списки:

$listReptiles = [];
$listMammals = [];
for ($i = 1; $i <= 10; $i++)
{
switch(mt_rand(1,2))
{
case 1 :
$listReptiles[] = new Reptile();
break;
case 2 :
$listMammals[] = new Mammal();
break;
}
}

что ты думаешь?

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector