我有一个问题,更多的是一个设计问题。我有一个定义了一堆函数的类。但是,我希望某些函数的行为在运行时可以更改 - 或者至少在设计时像插件接口一样工作。例如:
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类“使用这个或那个算法给我一个元素” - 而不是“使用你通过自己的状态确定的算法给我一个元素”。希望有人能澄清这一点!
祝你好运, 丹尼尔
答案 0 :(得分:4)
你的方法是正确的。这是Strategy Pattern(UML 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证明它是可行的解决方案。