2010-01-31 16 views
7

Tengo una pregunta que es más una cuestión de diseño. Tengo una clase que define un grupo de funciones. Sin embargo, quiero que el comportamiento de algunas funciones se modifique durante el tiempo de ejecución, o al menos funcione como una interfaz de complemento durante el tiempo de diseño. por ejemplo:Clases de PHP polimórficas/conectables

class MyClass { 
    public function doSomething(); 
} 

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

Tenía varias ideas sobre cómo implementar algo como esto en el código "real". Sin embargo, no estoy seguro, cuál es el camino correcto a seguir. Primero pensé que podría usar una interfaz para este propósito.

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! 

Este enfoque podría ampliarse para no tiene el proveedor de doSomething pasado como parámetro, pero para ajustar ya sea como un miembro de objeto o incluso miembro de la clase. Sin embargo, no me gustó que tuviera que crear una instancia de n clase que ni siquiera contenga una sola variable miembro y el único método podría ser fácilmente una función de clase estática. Simplemente no hay necesidad de un objeto en ese punto. Luego iba a tratar de usar variables de función, pero me caería de OOP entonces, lo que tampoco me gustó. Mis preguntas son: ¿cuál es la mejor manera de implementar un sistema como el que describí? ¿Qué enfoque probarías o usarías? ¿Hay algo obvio que quizás no haya pensado? ¿O debería ir con el enfoque de interfaz y mi mal presentimiento sobre la instanciación de objetos "sin cuerpo" es simplemente preciosismo nerd ?! ¡Tengo curiosidad por tus respuestas!

Editar:

Porque estoy realmente interesado en aclarar si esto realmente es (o debería ser) un Estado o un patrón de estrategia, voy a entrar en más detalle sobre la aplicación. Tengo una clase base de colecciones de la cual derivo diferentes implementaciones específicas. Quiero ser capaz de seleccionar un solo elemento de cualquier aplicación específica - sin embargo, yo quiero ser capaz dinámicamente alterar cómo este elemento se selecciona de la colección, por ejemplo:

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 

Esto es básicamente lo que quiero lograr . ¿Es esto más una implementación de un patrón de estrategia o el patrón de estado, aunque utilicé el término estrategia porque, de todos modos, encaja más en este caso? Por lo que yo entiendo, la estrategia y el patrón de estado son básicamente los mismos, excepto que su intención es diferente. El enlace proporcionado por Gordon establece que la intención de un patrón de estado es "Permitir que un objeto modifique su comportamiento cuando su estado interno cambia", pero este no es el caso aquí. Lo que quiero es poder decirle a mi clase MyCollection "usar este o aquel algoritmo para darme un elemento", no "darme un elemento usando un algoritmo que usted determine a través de su propio estado". Espero que alguien pueda aclarar esto!

Saludos cordiales, Daniel

+1

+1, Ese cariño va a ser mi final. xP –

+0

Bueno, usted mismo ya respondió su pregunta. No desea administrar estados. Desea intercambiar comportamientos individuales, por lo que la estrategia es apropiada. En una nota al margen, ya que parece ser alemán, puedo recomendar el libro phpdesignpatterns.de – Gordon

Respuesta

4

Su enfoque es correcto. Es un Strategy Pattern (UML diagram):

Compruebe hacia fuera mi respuesta a esta pregunta (omitir hasta donde dice Desde desea cambiar dinámicamente el comportamiento):

Un Una alternativa a su UseCase específico sería encapsular las estrategias de selección en una única clase de servicio, en lugar de asignarlas a su clase de colección. Luego, en lugar de pasar las estrategias a la colección, pase la colección a la clase de servicio, p.

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

Dejo en tus manos la posibilidad de utilizarlo con métodos estáticos o no. Para evitar la creación de instancias múltiples de estrategias de Selección, puede almacenar los objetos de Selección dentro del Servicio para su reutilización.

Para otros patrones de comportamiento comprobar

+0

¡Muchas gracias! Ahora solo voy a tener que silenciar que "No ... Necesito .. Objeto ... Aquí ... Urgs ..." - sentimiento;) –

+0

A partir del 5.3, también podrías usar cierres para este enfoque, pero entonces Ya no puede escribir sugerencia o programa contra una interfaz. – Gordon

+0

La estrategia se utiliza para entregar algoritmos optimizados, como por ejemplo la Búsqueda binaria cuando se trata de una matriz ordenada en lugar de la Búsqueda lineal para una matriz no ordenada. Además, la selección de estrategias a menudo se implementa utilizando métodos sobrecargados, que no son compatibles con PHP (la llamada __call tampoco hace un gran trabajo aquí). Creo que el patrón de estado es más apropiado en este caso: aplica la implementación correcta en función de un estado externamente configurable. –

3

La solución que esbozar es más o menos bien. Puede que no sepa esto, pero esto es de hecho "State" Design Pattern. El estado del sistema está representado por el objeto. Si necesita cambiar el estado, simplemente reemplace el objeto con otro Estado.

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

recomiendo que se alojen con lo que tiene, ya que está probado por GoF ser solución viable.

+0

También gracias, aunque creo que el "Patrón de Estrategia" es el patrón y el término correctos. –

+0

Hola Dan, en este punto probablemente nos estamos volviendo excesivamente pedantes, pero estoy bastante seguro de que State es exactamente lo que estás buscando. El punto de estado es que el objeto de estado (implementador) puede establecerse en el objeto maestro desde fuera de él, mientras que en Estrategia la decisión sobre qué implementación usar se realiza dentro del objeto maestro sin ninguna forma de cambiarlo desde el exterior. De todos modos, lo que tienes en este momento tiene perfecto sentido. –

+0

De cualquier manera, esto todavía vale la pena un voto popular. Para mí, la pandilla de cuatro patrones no son mandatos estrictos de "hágalo por esta vía", sino sugerencias que sugieren buenos principios de diseño para ser aplicados de manera flexible. Hay una coincidencia entre el estado y los patrones de estrategia, ya que ambos tienen implementaciones conmutables como un objetivo clave. Ser preciso sobre qué patrón tiene valor, pero las ideas subyacentes son la recompensa real. – Steve314