Si suponemos que no es deseable que la clase base sea una clase de interfaz pura, y usar los 2 ejemplos a continuación, ¿cuál es un mejor enfoque, usando la definición de clase de método abstracto o virtual?
La ventaja de la versión "abstracta" es que probablemente se vea más limpia y obligue a la clase derivada a dar una implementación con esperanza significativa.
La ventaja de la versión "virtual" es que otros módulos pueden extraerla fácilmente y usarla para probar sin agregar un montón de marco subyacente como requiere la versión abstracta.
Versión abstracta:
public abstract class AbstractVersion
{
public abstract ReturnType Method1();
public abstract ReturnType Method2();
.
.
public abstract ReturnType MethodN();
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
Versión virtual:
public class VirtualVersion
{
public virtual ReturnType Method1()
{
return ReturnType.NotImplemented;
}
public virtual ReturnType Method2()
{
return ReturnType.NotImplemented;
}
.
.
public virtual ReturnType MethodN()
{
return ReturnType.NotImplemented;
}
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
return ReturnType.NotImplemented
? ¿Seriamente? Si no puede rechazar el tipo no implementado en tiempo de compilación (puede; use métodos abstractos), al menos, arroje una excepción.Respuestas:
Mi voto, si estuviera consumiendo tus cosas, sería por los métodos abstractos. Eso va junto con "fallar temprano". Puede ser una molestia en el momento de la declaración agregar todos los métodos (aunque cualquier herramienta de refactorización decente lo hará rápidamente), pero al menos sé cuál es el problema de inmediato y lo soluciono. Prefiero eso que depurar 6 meses y el valor de los cambios de 12 personas más tarde para ver por qué de repente recibimos una excepción no implementada.
fuente
La versión virtual es propensa a errores y semánticamente incorrecta.
Abstract dice "este método no se implementa aquí. Debe implementarlo para que esta clase funcione"
Virtual dice "Tengo una implementación predeterminada pero puedes cambiarme si lo necesitas"
Si su objetivo final es la capacidad de prueba, las interfaces son normalmente la mejor opción. (esta clase hace x en lugar de esta clase es ax). Sin embargo, es posible que deba dividir sus clases en componentes más pequeños para que esto funcione bien.
fuente
Esto depende del uso de su clase.
Si los métodos tienen una implementación razonable "vacía", tiene muchos métodos y a menudo anula solo unos pocos, entonces usar
virtual
métodos tiene sentido. Por ejemplo,ExpressionVisitor
se implementa de esta manera.De lo contrario, creo que deberías usar
abstract
métodos.Idealmente, no debería tener métodos que no estén implementados, pero en algunos casos, ese es el mejor enfoque. Pero si decide hacer eso, tales métodos deberían arrojar
NotImplementedException
, no devolver algún valor especial.fuente
Sugeriría que reconsidere tener una interfaz separada definida que implementa su clase base, y luego siga el enfoque abstracto.
Código de imagen como este:
Hacer esto resuelve estos problemas:
Al tener todo el código que usa objetos derivados de AbstractVersion ahora se puede implementar para recibir la interfaz IVersion, esto significa que se pueden probar más fácilmente en la unidad.
La versión 2 de su producto puede implementar una interfaz IVersion2 para proporcionar funcionalidad adicional sin romper el código de sus clientes existentes.
p.ej.
También vale la pena leer acerca de la inversión de dependencias, para evitar que esta clase contenga dependencias codificadas que eviten pruebas de unidad efectivas.
fuente
La inyección de dependencia depende de las interfaces. Aquí hay un breve ejemplo. Class Student tiene una función llamada CreateStudent que requiere un parámetro que implementa la interfaz "IReporting" (con un método ReportAction). Después de crear un alumno, llama a ReportAction en el parámetro de clase concreto. Si el sistema está configurado para enviar un correo electrónico después de crear un estudiante, enviamos una clase concreta que envía un correo electrónico en su implementación de ReportAction, o podríamos enviar otra clase concreta que envíe resultados a una impresora en su implementación de ReportAction. Genial para la reutilización de código.
fuente