Hoy recibí una revisión de código de un desarrollador sénior que preguntaba "Por cierto, ¿cuál es su objeción al envío de funciones mediante una declaración de cambio?" He leído en muchos lugares cómo bombear un argumento a través del cambio a métodos de llamada es una mala OOP, no tan extensible, etc. Sin embargo, realmente no puedo encontrar una respuesta definitiva para él. Me gustaría resolver esto por mí mismo de una vez por todas.
Aquí están nuestras sugerencias de código de la competencia (se usa php como ejemplo, pero puede aplicarse de manera más universal):
class Switch {
public function go($arg) {
switch ($arg) {
case "one":
echo "one\n";
break;
case "two":
echo "two\n";
break;
case "three":
echo "three\n";
break;
default:
throw new Exception("Unknown call: $arg");
break;
}
}
}
class Oop {
public function go_one() {
echo "one\n";
}
public function go_two() {
echo "two\n";
}
public function go_three() {
echo "three\n";
}
public function __call($_, $__) {
throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
}
}
Parte de su argumento fue: "(el método de cambio) tiene una forma mucho más limpia de manejar los casos predeterminados que la que tiene en el método mágico genérico __call ()".
No estoy de acuerdo con la limpieza y de hecho prefiero llamar, pero me gustaría escuchar lo que otros tienen que decir.
Argumentos que se me pueden ocurrir en apoyo del Oop
esquema:
- Un poco más limpio en términos del código que tiene que escribir (menos, más fácil de leer, menos palabras clave para considerar)
- No todas las acciones se delegan a un solo método. No hay mucha diferencia en la ejecución aquí, pero al menos el texto está más compartimentado.
- En el mismo sentido, se puede agregar otro método en cualquier lugar de la clase en lugar de un lugar específico.
- Los métodos tienen espacios de nombres, lo cual es bueno.
- No se aplica aquí, pero considere un caso donde se
Switch::go()
opera en un miembro en lugar de un parámetro. Tendría que cambiar el miembro primero, luego llamar al método. ParaOop
que pueda llamar a los métodos de forma independiente en cualquier momento.
Argumentos que se me pueden ocurrir en apoyo del Switch
esquema:
- En aras de la discusión, un método más limpio para tratar una solicitud predeterminada (desconocida)
- Parece menos mágico, lo que puede hacer que los desarrolladores desconocidos se sientan más cómodos
¿Alguien tiene algo que agregar para cualquier lado? Me gustaría tener una buena respuesta para él.
fuente
Oop
permite tener phpdoc para describir cada método, que algunos IDE pueden analizar (p. ej., NetBeans).Respuestas:
Un cambio se considera no OOP porque a menudo el polimorfismo puede hacer el truco.
En su caso, una implementación de OOP podría ser esta:
fuente
para este ejemplo:
OK, solo bromeo aquí. El argumento a favor / en contra del uso de una declaración de cambio no puede hacerse con un ejemplo tan trivial, porque el lado de la POO depende de la semántica involucrada, no simplemente del mecanismo de despacho .
Las declaraciones de cambio son a menudo una indicación de clases o clasificaciones faltantes, pero no necesariamente. A veces, una declaración de cambio es solo una declaración de cambio .
fuente
Quizás no sea una respuesta, pero en el caso del código sin interruptor, parece que esta sería una mejor coincidencia:
Una gran parte del rompecabezas para evaluar es lo que sucede cuando necesitas crear
NewSwitch
oNewOop
. ¿Tus programadores tienen que saltar a través de aros por un método u otro? Qué sucede cuando cambian tus reglas, etc.fuente
is_callable
Actualicé a , pero dejé call_user_func ya que (no es parte de esta pregunta), puede haber otros argumentos para transmitir. Pero ahora que esto se ha transferido a los programadores, definitivamente estoy de acuerdo en que la respuesta de @ Andrea es mucho mejor :)En lugar de simplemente poner su código de ejecución en funciones, implemente un patrón de comando completo y coloque cada uno de ellos en su propia clase que implemente una interfaz común. Este método le permite usar IOC / DI para conectar los diferentes "casos" de su clase y le permite agregar y eliminar fácilmente casos de su código con el tiempo. También le proporciona un buen código que no viola los principios de programación SOLID.
fuente
Creo que el ejemplo es malo. Si hay una función go () que acepta el argumento $ where, si es perfectamente válido usar switch en la función. Tener una sola función go () facilita la modificación del comportamiento de todos los escenarios go_where (). Además, conservará la interfaz de la clase: si utiliza varios métodos, está modificando la interfaz de la clase con cada nuevo destino.
En realidad, el interruptor no debe reemplazarse con un conjunto de métodos, sino con un conjunto de clases: esto es polimorfismo. Luego, el destino será manejado por cada subclase, y habrá un solo método go () para todos ellos. Reemplazar condicional por polimorfismo es una de las refactorizaciones básicas descritas por Martin Fowler. Pero es posible que no necesite polimorfismo, y cambiar es el camino a seguir.
fuente
go()
, puede violar fácilmente el principio de sustitución de Liskov. Si va a añadir más funcionalidad ago()
, usted no desea cambiar la interfaz por lo que yo soy concerend.