Las clases abstractas. ¿Qué son y para qué sirven?

Leonardo Jose Castillo Lacruz
4 min readMay 1, 2022

Cuando desarrollamos bajo el paradigma de la programación orientada a objetos, además de la herencia, tenemos otros recursos que nos permiten además de reutilizar el código de forma eficiente, este otro recurso son las clases abstractas, que nos ayudan a definir reglas de implementación de métodos de forma tal que, tengamos una primera clase base en la que definimos el esqueleto de comportamientos (métodos) que cualquier clase que herede o extiende de la nuestra, deba implementar.

Puede sonar confuso, entonces vamos a intentar resumirlo, una clase abstracta es un esqueleto que permite definir cuales métodos debe implementar cualquier clase hija.

Aspectos importantes a tener en cuenta:

  • Una clase abstracta no se instancia. Es decir no es posible crear objetos de una clase abstracta.
  • Las clases abstractas pueden implementar métodos, es decir pueden tener funcionalidad construida pero al menos uno de sus métodos debe ser abstracto. No todos los métodos de una clase abstracta deben ser abstractos.

Ahora que es un método abstracto?

En lenguaje simple, un método abstracto es su definición, parámetros y tipo de dato que retorna pero no tiene nada de funcionalidad.

Vamos a complementar nuestro conocimiento con un ejemplo:

Imaginemos que tenemos una aplicación de pedidos queremos implementar la gestión de los proveedores de servicios, entonces vamos a implementar una clase abstracta proveedor, de la siguiente forma:

clase abstracta Proveedor {
constructor(nombre, direccion, telefono, ramo);
abstracto hacerPedido(cliente, items){
//Esto debe ser implementado en la clase hija
//Si por alguna razón es ejecutado este método, podemos retornar
//un error
Error('Debe implementar esté método en su clase');
}
abrirReclamo(texto) {
//Por ser un método común a cualquier proveedor, este método
//puede ser implementado en esta clase, no es un método
//abstracto
registrarReclamoEnBaseDatos(texto);
}
}

De acuerdo a nuestro pseudocódigo, tenemos una clase abstracta que define los comportamientos esperados de los proveedores, pero en nuestra aplicación podemos tener: Restaurantes, Supermercados, Lavanderías, etc. Vamos a desarrollar clases para construir las implementaciones para tipo de proveedor que nuestra aplicación acepte. Por nuestra clase abstracta, sabemos que para cualquier proveedor vamos a poder hacer pedidos, podemos abrir reclamos, pero por el tipo de proveedor, hacer un pedido a un restaurant lleva algunas reglas, que van a ser diferentes a las de un supermercado y más aún a las de una lavandería.

Entonces con el ejemplo anterior, podemos tener una idea más clase de que lo significa o lo que engloba el uso de clases abstractas.
Completando el ejemplo anterior, vamos a implementar el pseudocódigo de un par de tipos de proveedores:

clase Restaurant extiende Proveedor {
hacerPedido(cliente, items){
//Validaciones propias del tipo de proveedor Restaurant
...
//Construimos la lógica de pedido de restaurant.
...
//Ejemplo, tamaños del plato, adicionales, etc.
...
//Registramos en algún soporte el pedido (base de datos,
//archivo)
RegistrarPedido(cliente,items);
}}

Ahora implementamos la clase para gestión de Lavanderías por ejemplo:

clase Lavanderia extiende Proveedor {
hacerPedido(cliente, items){
//Validaciones propias del tipo de proveedor Lavanderia
...
//Validamos que servicios está solicitando.
...
//Verificamos cantidad de piezas, tipo de piezas, etc.
...
//Registramos en algún soporte el pedido (base de datos,
//archivo)
RegistrarPedido(cliente,items);
}}

Lo interesante de las clases abstractas, es lo prácticas que son para que nuestros sistema escalen. Si agregamos la gestión de un nuevo tipo de proveedor, ya sabemos que debemos implementar. Incluso si observamos el pseudocódigo de la clase abstracta, si por alguna razón creamos una clase y extendemos de la clase abstracta y no implementamos el método de hacerPedido, al ser invocado en algún punto de la aplicación se va a generar un error informando de que no hemos implementado el método en nuestra clase hija.

Lamentablemente Javascript no tiene nativamente la gestión de clases abstractas, pero podemos mediante algunos ajustes implementar ello. Como quedaría nuestra clase Proveedor en Javascript.

export class Proveedor {
constructor(nombre, direccion, telefono, ramo) {
if (this.constructor == Proveedor) {
throw new Error('Por ser una clase abstracta, no se debe instanciar objetos de la clase Proveedor');
}
this.nombre = nombre;
this.direccion = direccion;
this.telefono = telefono;
this.ramo = ramo;
} hacerPedido(cliente, items){
//Método abstracto
throw new Error('Debe implementar el método hacerPedido en su clase');
}
abrirReclamo(texto) {
if (texto != '') {
//Enviamos a algun soporte como base de datos o API
...
}
}
}

Observamos que en el constructor de la clase, validamos si el tipo del constructor que se está invocando corresponde a Proveedor, esto lo hacemos porque desde una clase hija, es posible que se llame a este constructor, pero el tipo del constructor será el nombre de la clase hija, ejemplo:

export class Restaurant extends Proveedor {
constructor(nombre, direccion, telefono, ramo) {
//Cuando llamamos a super, decimos que queremos ejecutar el
//constructor de Proveedor

super(nombre, direccion, telefono, ramo);
}}

Al llamar a super, al ejecutarse el constructor de Proveedor, el tipo será Restaurant, por lo tanto no se va a generar ningún error. Estamos haciendo uso correcto de la clase abstracta.

Comparemos ahora la misma implementación pero ahora en C#

abstract class Proveedor
{
string nombre = "";
string direccion = "";
string telefono = "";
string ramo = "";
public Person(string _nombre, string _direccion, string _telefono, string _ramo) {
nombre = _nombre;
direccion = _direccion;
telefono = _telefono;
ramo = _ramo;
}
//Método abstracto
public abstract int hacerPedido(string cliente, List<string> items);
public int abrirReclamo(string texto) {
if (texto != "") {
//Enviamos a algun soporte como base de datos o API
//...
}
}
}

Observemos algunos detalles, más allá de la diferencia de sintaxis de los lenguajes, en C# hacemos explicita la característica o atributo de la clase de ser abstracta, como en nuestro pseudocódigo, así como en el método hacerPedido. Otro detalle interesante, en lenguajes de tipado fuerte, en el método abstracto ya definimos que tipo de dato va a ser devuelto, por lo tanto ya dejamos claras las reglas.

Para más ejemplos de clases abstractas, te invito a visualizar este vídeo:

--

--

Leonardo Jose Castillo Lacruz

Desarrollador de software desde 1998. Apasionado por la tecnología. Descubriendo que cuando enseñas aprendes mucho más