Aunque la última respuesta fue hace un año, me gustaría hacer algunas revisiones/comentarios sobre este tema.
revisión Respuestas
Estoy de acuerdo con @CarlManaster acerca de la codificación de la declaración switch
una vez para evitar todos los problemas conocidos de tratar con código duplicado, en este caso relacionado con los condicionales (algunos de ellos mencionados por @thkala).
No creo que el enfoque propuesto por @ KonradSzałwiński o @AlexanderKogtenkov se adapte a este escenario por dos razones:
primer lugar, desde el problema que has descrito, que no es necesario cambiar dinámicamente la asignación entre el nombre de una acción y la instancia de una acción que lo maneja.
Observe que estas soluciones permiten hacer eso (simplemente asignando un nombre de acción a una nueva instancia de acción), mientras que la solución estática basada en conmutadores no (las asignaciones están codificadas). Además, todavía necesitará un condicional para verificar si una determinada clave se define en la tabla de asignación, si no se debe tomar una acción (la default
parte de una declaración de conmutación).
En segundo lugar, en este ejemplo particular, dictionaries
son implementaciones realmente ocultas de la instrucción switch. Aún más, podría ser más fácil leer/comprender la instrucción switch con la cláusula predeterminada que tener que ejecutar mentalmente el código que devuelve el objeto de manejo de la tabla de asignación, incluido el manejo de una clave no definida.
Hay una manera que usted puede deshacerse de todos los condicionales, incluyendo la instrucción switch:
Extracción de la instrucción switch (no utilizar los condicionales en absoluto)
Cómo crear el objeto de la acción derecha desde el nombre de la acción?
Voy a ser independiente del idioma por lo que esta respuesta no llega que larga, pero el truco es darse cuenta de clases también son objetos.
Si ya ha definido una jerarquía polimórfica, no tiene sentido hacer referencia a una subclase concreta de BaseAction
: ¿por qué no pedirle que devuelva la instancia correcta manejando una acción por su nombre?
Normalmente se implementa mediante la misma instrucción de cambio que había escrito (por ejemplo, un método de fábrica) ...pero ¿qué pasa con esto:
public class BaseAction {
//I'm using this notation to write a class method
public static handlingByName(anActionName) {
subclasses = this.concreteSubclasses()
handlingClass = subclasses.detect(x => x.handlesByName(anActionName));
return new handlingClass();
}
}
Entonces, ¿qué está haciendo ese método?
Primero, recupera todas las subclases concretas de esto (que apunta a BaseAction
). En su ejemplo, obtendría una colección con ViewAction
, EditAction
y SortAction
.
Observe que dije subclases concretas, no todas las subclases. Si la jerarquía es más profunda, las subclases concretas siempre serán las que se encuentran en la parte inferior de la jerarquía (hoja). Eso es porque son los únicos que se supone que no son abstractos y proporcionan una implementación real.
En segundo lugar, obtenga la primera subclase que responda si puede o no manejar una acción por su nombre (estoy usando una notación aromatizada lambda/closure). Un ejemplo de implementación del método handlesByName
clase para ViewAction
se vería así:
public static class ViewAction {
public static bool handlesByName(anActionName) {
return anActionName == 'view'
}
}
En tercer lugar, enviamos el mensaje de nuevo a la clase que se encarga de la acción, creando una instancia del mismo.
Por supuesto, tiene que tratar con el caso cuando ninguna de las subclases maneja la acción por su nombre. Muchos lenguajes de programación, incluidos Smalltalk y Ruby, permiten pasar el método de detección a una segunda lambda/cierre que solo se evaluará si ninguna de las subclases coincide con los criterios. Además, tendrá que tratar con el caso más de una subclase maneja la acción por su nombre (probablemente, uno de estos métodos fue codificado de manera incorrecta).
Conclusión
Una ventaja de este enfoque es que las nuevas acciones pueden ser apoyadas por escrito (y no modificar) el código existente: acaba de crear una nueva subclase de BaseAction
e implementar el método handlesByName
clase correctamente. Es efectivamente compatible con agregar una nueva característica al agregar un nuevo concepto, sin modificar la impedancia existente. Está claro que, si la nueva característica requiere que se agregue un nuevo método polimórfico a la jerarquía, se necesitarán cambios.
Además, puede proporcionar a los desarrolladores que utilizan los comentarios de su sistema: "La acción proporcionada no se trata mediante ninguna subclase de BaseAction, cree una nueva subclase e implemente los métodos abstractos". Para mí, el hecho de que el modelo mismo te diga qué es lo que está mal (en lugar de tratar de ejecutar mentalmente una tabla de consulta) agrega valor y aclara las instrucciones sobre lo que se debe hacer.
Sí, esto podría sonar sobrediseño. Mantenga una mente abierta y descubra que si una solución está sobrediseñada o no tiene que ver, entre otras cosas, con la cultura de desarrollo del lenguaje de programación particular que está utilizando. Por ejemplo, los chicos de .NET probablemente no lo usen porque .NET no permite tratar las clases como objetos reales, mientras que, por otro lado, esa solución se usa en culturas Smalltalk/Ruby.
Finalmente, use el sentido común y el gusto para determinar de antemano si una técnica en particular realmente resuelve su problema antes de usarlo. Es tentador que sí, pero deben evaluarse todos los intercambios (cultura, antigüedad de los desarrolladores, resistencia al cambio, apertura de la mente, etc.).
por favor, entregue más código si está buscando un tutorial de polimorfismo que puede encontrar en cualquier lugar de la web. Por favor, proporciona un ejemplo. – amirouche
No se pudo agregar todo el código aquí. Proporcioné un ejemplo a continuación (busque Charles) – Charles
Puede reemplazar las condiciones restantes utilizando la API de reflexión. Determine en el tiempo de ejecución qué tipo de acción de base desea crear una instancia dependiendo de la cadena. hay una caída hacia abajo a esto, su cadena debe coincidir con el nombre exacto de su acción base. También puede usar un mapa hash para asignar una cadena con acción base y seleccionar en tiempo de ejecución. –