jueves, 18 de agosto de 2022

¿Por qué la clase abstracta no puede ser instanciada?

Este post es una copia de una respuesta que di en Quora a la pregunta ¿Por qué la clase abstracta no puede ser instanciada?



Porque es una clase cuya información y características para que sea funcional está incompleta, con lo que no tendría sentido que fuera instanciada.

¿Qué es la abstracción?


La abstracción es un proceso del pensamiento en el que podemos distinguir las características de un objeto que lo hacen ser ese tipo de objeto en concreto.

Por ejemplo todos tenemos claro cómo debe ser un árbol; debe tener raíces, tronco, ramas y hojas pero esto no define ningún árbol en concreto, ni siquiera es un árbol, es la “idea que tenemos de un árbol”. Un árbol es un manzano, un roble, un nogal o un pino, todos de ellos bastantes diferentes entre sí e inconfundibles pero cuando los vemos los reconocemos como un árbol inmediatamente y sin dudar.

Scott McCloud en su libro “Entender el comic” lo explica muy bien:


Todas las imágenes anteriores son caras pero si nos vamos a la izquierda tenemos una cara en particular una “instancia” de una cara, según avanzamos a la derecha tenemos menos características de una cara, nos quedamos con la esencia de lo que es una cara. Eso es la “abstracción”. Al final tenemos algo que estrictamente no es una cara (y que no podría funcionar como tal) pero al verlo tenemos la idea de que si.

En la programación es parecido; extraemos ideas, procesos y características para convertirlos en clases. Las clases como tal son una abstracción del objeto que queremos construir, pero no son el objeto.

La clase define el objeto, define su comportamiento y los atributos que contiene pero no el valor de estos. La clase solo es funcional cuando la instanciamos y la convertimos en un objeto.

Por ejemplo podemos definir un cuadrado como una figura geométrica de 4 lados iguales. Pero esto no sería un cuadrado, seria la clase de un cuadrado. Para que fuera un cuadrado como tal debemos instancia la clase y crear un objeto que tenga un número de determinado de unidades concreto por ejemplo un cuadrado de lado 10.


Si profundizáramos mas en la esencia de un cuadrado veríamos que este es una peculiaridad de un rectángulo, una figura geométrica de 4 lados iguales dos a dos. Por ejemplo un rectángulo de 2x2

Así funciona la herencia, con mecanismos de abstracción cada vez mayores y menos concretos. Un rectángulo es una abstracción superior de un cuadrado o un cuadrado es una peculiaridad de un rectángulo.


Pero en este caso tanto las clases cuadradas y rectángulas pueden instanciarse y tener sentido por ellas mismas; existen los cuadrados y existen los rectángulos, son tangibles y se pueden medir, son objetos concretos

Si profundizamos más el concepto de rectángulo descubriremos que es un área cerrada de N lados, es decir un polígono.


Y aquí es donde llegamos al concepto de clase abstracta. Si la clase es una abstracción del objeto, la clase abstracta es una “abstracción” de la clase

La existencia del polígono no tiene sentido sin que tenga una forma particular, es decir no existe un polígono que no sea un cuadrado, triangulo, trapezoide o cualquier otra forma cerrada. No hay manera de instanciar solo el de polígono sin que sea además otra cosa más concreta.

Si definimos nuestro concepto de polígono como una clase que tiene n lados y además tiene un área, tendremos un idea aproximada de cómo es el polígono, pero no sabremos cuánto mide cada lado, ni si es regular o irregular y mucho menos como se calcula el área.

No tiene sentido instanciar las clase abstracta polígono, porque no es algo “real” y concreto con lo que podamos trabajar, necesitamos completar la clase con algo más particular para que tenga sentido, es decir un tipo particular de polígono.

Si vemos la clase abstracta “polígono” el número de lados es una atributo, y es una característica común de los polígonos, sin embargo como calcular el área es una particularidad de cada polígono.

Por ejemplo para calcular el área de los siguientes polígonos se calcula así:

  • Cuadrado: lado al cuadrado
  • Rectángulo: base por altura
  • Triangulo: Alto x Ancho /2
  • Pentágono: ((5 * longitud) * apotema) / 2;


Además tenemos un caso especial, una circunferencia que tiene área pero no es un polígono (por lo menos no como lo hemos definido) ya que no tiene lados. El hecho que se le pueda calcular el área se puede definir mediante una interfaz, así pues:

  • Una clase abstracta es una clase implementa características, como por ejemplo el hecho de tener numero de lados para nuestro polígono, pero no implementa otras como calcular un área (pero si las define). Las características no implementadas deben estar dentro de su jerarquía de herencia.
  • Las interfaces nos definen que características y funcionalidades debe tener una clase, pero en ningún caso nos indica como implementarlas, y no tiene que existir ninguna relación de herencia. Simplemente indicamos que una clase en particular debe poder resolver unas necesidades, pero no como hacerlo.


Un ejemplo más real


Un ejemplo ya alejado de lo simple de las definiciones teóricas podemos encontrarlo en la implementación de la clase DbConnection de ADO.NET

ADO.NET es la colección de clases de más bajo nivel (y más básicas) que usa .NET para el uso de base de datos. Definen de una forma clara y atómica como conectarse a un motor de base de datos y como ejecutar comandos además de ser un ejemplo excelente para estudiar abstracción.

La clase básica para conectarnos a una base de datos es la clase DbConnection que tiene las siguientes características:


Generalmente las clases abstracta definen e implementan código común a un conjunto de circunstancias pero no ponen atención a los "detalles", que son implementados por las clases hijas

Por ejemplo en la clase DbConnection los elementos marcados en rojo son abstractos, eso quiere decir que la clase no sabe cómo resolver su funcionalidad y al no saberlo la clase no puede ser instanciada, ya que esta “incompleta”.

Cada driver de base de datos debe definir como resolver esta funcionalidad, algunos de estos métodos son:

  • Open, Close, BeginDbTransacion: Estos métodos están extremadamente ligados al motor de base de datos a usar y al driver correspondiente.

Sin embargo otros métodos no dependen del driver en absoluto ya que definen flujos y funciones que son iguales para todos los tipos de base de datos, por ejemplo:

  • OpenAsync: Abre una conexión de forma asíncrona. Este método resuelve todo el flujo de los hilos y finalmente llame al método abstracto Open.
  • StateChange: Se lanza cada vez que se cambia el estado de la conexión.
  • Dispose: Cuando se ejecuta este método el flujo siempre es igual, se liberan los recursos, y siempre se llama al método Close.

Estos métodos realizan una serie de operaciones comunes en la clase base y finalmente “llama” a los métodos abstractos en algún momento.

Cada proveedor de base de datos debe ofrecernos un driver y una clase adecuada que implemente los métodos abstractos y opcionalmente sobrescriba los métodos virtuales (si esto es necesario).

Un diagrama de esto, por ejemplo usando conexiones para SQL, MySQL y Oracle, podría quedar así:



Puedes encontrar el código fuente de todo el artículo en:


Si te ha gustado el artículo, compártelo en tus redes sociales ;-)

No hay comentarios:

Publicar un comentario