Comprender las clases abstractas de C#
Las clases abstractas en C# son un concepto fundamental que a menudo suscita dudas entre los desarrolladores. En su vídeo, "C# Abstract Classes - What They Are, How to Use Them, and Best Practices", Tim Corey se sumerge en qué son las clases abstractas, cómo utilizarlas y cuáles son las mejores prácticas. Este artículo resume los puntos clave de su vídeo, utilizando marcas de tiempo como referencia.
Introducción
Tim (0:00) explica que las clases abstractas son a menudo cuestionadas en cuanto a su propósito, funcionalidad e importancia. Describe una clase abstracta como una mezcla entre una clase base completa y una interfaz, situada entre ambas en términos de funcionalidad.
Paseo por la aplicación demo
En el minuto (0:59), Tim muestra una aplicación de demostración de clases abstractas. La aplicación consta de una aplicación de consola y una biblioteca de clases con dos clases de acceso a datos que simulan operaciones con bases de datos. Estas clases tienen métodos para cargar y guardar datos, que Tim utiliza para ilustrar las similitudes y diferencias entre clases abstractas, clases base e interfaces.
Este es el código inicial para la clase base y las clases derivadas:
// Base Class Definition
public class DataAccess
{
// Method to load the connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Method to load data
public void LoadData()
{
Console.WriteLine("Loading data...");
}
// Method to save data
public void SaveData()
{
Console.WriteLine("Saving data...");
}
}
// Derived class that inherits from DataAccess
public class SQLDataAccess : DataAccess
{
// Overriding LoadData method to specify SQL data loading
public new void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Overriding SaveData method to specify SQL data saving
public new void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class that inherits from DataAccess
public class SQLiteDataAccess : DataAccess
{
// Overriding LoadData method to specify SQLite data loading
public new void LoadData()
{
Console.WriteLine("Loading SQLite data...");
}
// Overriding SaveData method to specify SQLite data saving
public new void SaveData()
{
Console.WriteLine("Saving SQLite data...");
}
}
// Base Class Definition
public class DataAccess
{
// Method to load the connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Method to load data
public void LoadData()
{
Console.WriteLine("Loading data...");
}
// Method to save data
public void SaveData()
{
Console.WriteLine("Saving data...");
}
}
// Derived class that inherits from DataAccess
public class SQLDataAccess : DataAccess
{
// Overriding LoadData method to specify SQL data loading
public new void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Overriding SaveData method to specify SQL data saving
public new void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class that inherits from DataAccess
public class SQLiteDataAccess : DataAccess
{
// Overriding LoadData method to specify SQLite data loading
public new void LoadData()
{
Console.WriteLine("Loading SQLite data...");
}
// Overriding SaveData method to specify SQLite data saving
public new void SaveData()
{
Console.WriteLine("Saving SQLite data...");
}
}
Creación de una clase base
Tim explica en (3:21) cómo crear una clase base. Refactoriza el código para mover métodos comunes, tales como LoadConnectionString, a una clase base llamada DataAccess. Al heredar de esta clase base, otras clases como SQLDataAccess y SQLiteDataAccess obtienen acceso a estos métodos compartidos, reduciendo la duplicación de código.
Cómo hacer que la clase base sea abstracta
En el minuto 5:56, Tim convierte la clase base en una clase abstracta para demostrar las diferencias. Cambia DataAccess a una clase abstracta, impidiendo que sea instanciada directamente. En su lugar, solo las clases que heredan de esta clase abstracta, como SQLiteDataAccess y SQLDataAccess, pueden implementar sus métodos y usar su funcionalidad compartida.
Así es como cambia el código cuando DataAccess se convierte en una clase abstracta:
// Abstract Base Class Definition
public abstract class AbstractDataAccess
{
// Shared method to load connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Abstract methods that must be implemented by derived classes
public abstract void LoadData();
public abstract void SaveData();
}
// Abstract Base Class Definition
public abstract class AbstractDataAccess
{
// Shared method to load connection string
public string LoadConnectionString()
{
Console.WriteLine("Loading the connection string...");
return "Test Connection String";
}
// Abstract methods that must be implemented by derived classes
public abstract void LoadData();
public abstract void SaveData();
}
Porción de interfaz en la clase abstracta
Tim (8:34) explica cómo las clases abstractas combinan las características de las interfaces y las clases base. Declara métodos abstractos dentro de la clase abstracta, tales como public abstract void LoadData(); y public abstract void SaveData();, sin implementarlos. Esto garantiza que cualquier clase derivada deba implementar estos métodos, de forma similar al funcionamiento de las interfaces.
Sobreescritura de métodos en clases abstractas
Tim (12:56) explica cómo anular métodos en una clase abstracta. Muestra que se puede declarar un método en la clase base como virtual, permitiendo que las clases derivadas lo sobrescriban. Este enfoque ofrece flexibilidad a la hora de implementar y ampliar métodos en clases derivadas.
Este es el código de la clase derivada que muestra las anulaciones de métodos:
// Derived class from abstract base class
public class SQLDataAccessWithAbstract : AbstractDataAccess
{
// Implementing the abstract LoadData method
public override void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Implementing the abstract SaveData method
public override void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
// Derived class from abstract base class
public class SQLDataAccessWithAbstract : AbstractDataAccess
{
// Implementing the abstract LoadData method
public override void LoadData()
{
Console.WriteLine("Loading SQL data...");
}
// Implementing the abstract SaveData method
public override void SaveData()
{
Console.WriteLine("Saving SQL data...");
}
}
Cuándo usar clases abstractas
Tim en (16:01) aconseja que las clases abstractas no se utilicen todos los días, pero que son valiosas en escenarios específicos. Advierte contra el uso de clases abstractas solo porque dos clases compartan código similar. En cambio, hace hincapié en mantener la relación "es un" y sugiere considerar métodos o clases de ayuda para el código compartido cuando sea apropiado.
Aquí está el código del programa principal que demuestra el uso:
// Main Program
class Program
{
static void Main(string[] args)
{
// Using Base Class
SQLDataAccess sqlData = new SQLDataAccess();
Console.WriteLine(sqlData.LoadConnectionString());
sqlData.LoadData();
sqlData.SaveData();
Console.WriteLine("--------------------------");
// Using Derived Class from Abstract Base Class
SQLDataAccessWithAbstract sqlDataAbstract = new SQLDataAccessWithAbstract();
Console.WriteLine(sqlDataAbstract.LoadConnectionString());
sqlDataAbstract.LoadData();
sqlDataAbstract.SaveData();
Console.WriteLine("--------------------------");
// You can't instantiate Abstract Base Class directly
// AbstractDataAccess abstractData = new AbstractDataAccess();
// Error: Cannot create an instance of the abstract class
}
}
// Main Program
class Program
{
static void Main(string[] args)
{
// Using Base Class
SQLDataAccess sqlData = new SQLDataAccess();
Console.WriteLine(sqlData.LoadConnectionString());
sqlData.LoadData();
sqlData.SaveData();
Console.WriteLine("--------------------------");
// Using Derived Class from Abstract Base Class
SQLDataAccessWithAbstract sqlDataAbstract = new SQLDataAccessWithAbstract();
Console.WriteLine(sqlDataAbstract.LoadConnectionString());
sqlDataAbstract.LoadData();
sqlDataAbstract.SaveData();
Console.WriteLine("--------------------------");
// You can't instantiate Abstract Base Class directly
// AbstractDataAccess abstractData = new AbstractDataAccess();
// Error: Cannot create an instance of the abstract class
}
}
Conclusión
La inmersión en profundidad de Tim Corey en C# abstract classes ofrece una comprensión clara y práctica de su propósito, funcionalidad y aplicaciones en el mundo real. A través de su aplicación de demostración, muestra cómo las clases abstractas salvan la distancia entre las clases base y las interfaces, permitiendo a los desarrolladores crear estructuras de código flexibles y mantenibles.
Al hacer hincapié en las mejores prácticas, como el uso de clases abstractas en los escenarios adecuados y evitar el uso excesivo, Tim proporciona a los desarrolladores las herramientas para tomar decisiones de diseño informadas. Con una sólida comprensión de estos conceptos, los desarrolladores pueden mejorar sus habilidades de programación orientada a objetos y crear aplicaciones sólidas en C#.
