让我们创建一个动物类型列表:
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()
而闻起来......这意味着每次出现新类型时,都应该更新项目。然而,基类知道它的后代是一种不好的做法。什么是优雅的方式?
答案 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()
方法都只会使用类结构分析。它将大大减少进一步的规模增长。
进一步阅读:
答案 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;
你觉得怎么样?