多态/可插入的PHP类

时间:2010-01-31 11:31:33

标签: php oop plugins polymorphism

我有一个问题,更多的是一个设计问题。我有一个定义了一堆函数的类。但是,我希望某些函数的行为在运行时可以更改 - 或者至少在设计时像插件接口一样工作。例如:

class MyClass {  
  public function doSomething();
}

$obj = new MyClass();
// PSEUDO CODE
$obj->doSomething = doSomethingElse;
$obj->doSomething(); // does something else

我有各种各样的想法如何在“真实”代码中实现这样的东西。但是,我不太确定,这是正确的方法。我首先想到我可以为此目的使用接口。

interface IDoSomethingInterface {  
  public function doSomething();
}

class DoSomethingClass implements IDoSomethingInterface
{
  public function doSomething()
  {
    // I do something!
  }
}

class DoSomethingElseClass implements IDoSomethingInterface
{
  public function doSomething()
  {
    // I actually do something else!
  }
}

class MyClass {
  public doSomething(IDoSomething $doSomethingProvider)
  {
    $doSomethingProvider->doSomething();
  }
}

$obj = new MyClass();
$doSomethingProvider = new DoSomethingClass();
$doSomethingElseProvider = new DoSomethingElseClass();

$obj->doSomething($doSomethingProvider); // I do something!
$obj->doSomething($doSomethingElseProvider); // I do something else!

这种方法可以扩展为不将doSomething提供程序作为参数传递,而是将其设置为对象成员甚至是类成员。但是,我不喜欢我必须创建一个n类的实例,它甚至不包含单个成员变量,唯一的方法很容易就是静态类函数。 imo那时根本不需要一个物体。 然后我打算尝试使用函数变量 - 但是我不知道OOP了,我也不喜欢。我的问题是:哪个是实现我所描述的系统的最佳方式?您尝试或使用哪种方法?有什么明显的我可能没有想过吗?或者我应该采用界面方法,而我对“无体”物体实现真实感觉的不良感觉只是书呆子的珍贵?!我很好奇你的答案!

编辑:

因为我真的很想澄清这实际上是(或应该是)一个国家还是一个战略模式,我将详细介绍一下实施情况。我有一个基类的集合,我从中派生出不同的特定实现。我希望能够从任何特定实现中选择单个元素 - 但是,我希望能够动态地改变从集合中选择该元素的方式,例如:

class MyCollection implements Countable, ArrayAcces, Iterator
{
  // Collection Implementation   
  public function select(ISelectionStrategy $strategy)
  {
    return $strategy->select($this);
  }   
} 

interface ISelectionStrategy
{
  public function select(MyCollection $collection);
}  

class AlphaSelectionStrategy implements ISelectionStrategy
{
  public function select(MyCollection $collection);
  {
    reset($collection);
    if (current($collection))
      return current($collection);
    else
      return null;
  }
}

class OmegaSelectionStrategy implements ISelectionStrategy
{
  public function select(MyCollection $collection)
  {
    end($collection);
    if (current($collection))
      return current($collection)
    else
      return null;
  }
}

class RandomSelectionStrategy implements ISelectionStrategy
{
  public function select(MyCollection $collection)
  {
    if (!count($collection))
      return null;
    $rand = rand(0, count($collection)-1);
    reset($collection);
    while($rand-- > 0)
    {
      next($collection);
    }
    return current($collection);
  }
}

$collection = new MyCollection();
$randomStrategy = new RandomSelectionStrategy();
$alphaStrategy = new AlphaSelectionStrategy();
$omegaStrategy = new OmegaSelectionStrategy();

$collection->select($alphaStrategy); // return first element, or null if none
$collection->select($omegaStrategy); // return last element, or null if none
$collection->select($randomStrategy); // return random element, or null if none 

这基本上就是我想要实现的目标。这现在更像是一种策略模式实现还是状态模式 - 虽然我使用了术语策略,因为无论如何它在这种情况下更适合。据我所知,策略和状态模式基本相同,除非他们的意图不同。 Gordon提供的链接声明状态模式的意图是“允许对象在其内部状态发生变化时改变其行为” - 但这不是这里的情况。我想要的是能够告诉我的MyCollection类“使用这个或那个算法给我一个元素” - 而不是“使用你通过自己的状态确定的算法给我一个元素”。希望有人能澄清这一点!

祝你好运, 丹尼尔

2 个答案:

答案 0 :(得分:4)

你的方法是正确的。这是Strategy PatternUML diagram):

查看我对这个问题的回答(跳到它所说的,因为你想动态改变行为):

您的特定UseCase的替代方法是将选择策略封装到单个服务类中,而不是将它们分配给您的Collection类。然后,不将策略传递给集合,而是将集合传递给服务类,例如

class CollectionService implements ICollectionSelectionService
{
    public static function select(MyCollection $collection, $strategy) {
        if(class_exists($strategy)) {
            $strategy = new $strategy;
            return $strategy->select($collection);
        } else { 
            throw new InvalidArgumentException("Strategy does not exist");
        }    
    }
}
$element = CollectionService::select($myCollection, 'Alpha');

无论是否使用静态方法,我都会告诉您。为了防止选择策略的多个实例化,您可以将Selection对象存储在Service中以供重用。

对于其他行为模式检查

答案 1 :(得分:3)

您概述的解决方案或多或少是正确的。你可能不知道这一点,但实际上这是"State" Design Pattern。系统状态由对象表示。如果需要更改状态,只需将对象替换为另一个状态。

alt text http://dofactory.com/Patterns/Diagrams/state.gif

我建议保留你所拥有的东西,因为GoF证明它是可行的解决方案。