多态/可插入的PHP类

Dan*_*lig 7 php oop polymorphism plugins

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

class MyClass {  
  public function doSomething();
}

$obj = new MyClass();
// PSEUDO CODE
$obj->doSomething = doSomethingElse;
$obj->doSomething(); // does something else
Run Code Online (Sandbox Code Playgroud)

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

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!
Run Code Online (Sandbox Code Playgroud)

这种方法可以扩展为不将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 
Run Code Online (Sandbox Code Playgroud)

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

最好的问候,丹尼尔

Gor*_*don 4

你的做法是正确的。这是一个策略模式UML图):

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

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

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');
Run Code Online (Sandbox Code Playgroud)

我让您自行决定是否将其与静态方法一起使用。为了防止选择策略的多次实例化,您可以将选择对象存储在服务中以供重用。

对于其他行为模式检查