Php,根据对象类型运行代码会导致依赖

时间:2017-08-04 08:47:24

标签: php dependencies

让我们创建一个动物类型列表:

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

有点看起来更好,因为现在消除了TestItem依赖项。它仍然因为isMammal()isReptile()而闻起来......这意味着每次出现新类型时,都应该更新项目。然而,基类知道它的后代是一种不好的做法。什么是优雅的方式?

3 个答案:

答案 0 :(得分:2)

使用界面

定义一个界面并确保所有动物都实现它。

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());

答案 1 :(得分: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. What is the point of interfaces in PHP?
  2. Build seven good object-oriented habits in PHP

答案 2 :(得分:0)

我找到了一个很好的解决方法。我将有同源名单:

select t.*
from (select t.*,
             lag(time) over (order by time) as prev_time
      from t
     ) t
where prev_time is null or
      time > prev_time + interval '0.001' second;
你觉得怎么样?