martes, 27 de enero de 2015

¿En qué se diferencia las interfaces de las clases abstractas?

Uno de mis roles profesionales es el de entrevistador técnico de candidatos a nuevos codificadores, esto es, evaluar las capacidades técnicas y las posibilidades que tienen los candidatos de convertirse en programadores estrella en un futuro. En mi caso hago las entrevistas relativas al uso de C#.

Entre las preguntas que hago, hay una que espero impaciente y casi siempre realizo con una sonrisa, la pregunta es “¿Me podría explicar en qué se diferencia las interfaces de las clases abstractas?”.

Las principales repuestas que recibo son:

-"Una clase abstracta tiene código y una interfaz no"

Lo cual es bastante impreciso, porque para empezar, ¿acaso las declaraciones no son código?

-"Las Interfaces sirve para implementar la herencia múltiple"

Esto es relativo. El concepto de herencia, implica “heredar” comportamiento, lo cual no se consigue con las interfaces, con lo que el tema de la “herencia múltiple” seguiría sin ser preciso, en todo caso heredamos la “posibilidad” de comportarnos como la clase padre, pero no el comportamiento en sí.

-"Las Interfaces representan un contrato que se debe cumplir"

lo cual es cierto y muy aproximado, pero muy de libro. Queda en el aire la verdadera necesidad de las interfaces.

-"La clase abstracta tiene métodos que deben ser implementados en las clases hijas"

Cierto, pero sigue sin quedar claro el motivo, y es otro enfoque de la aplicación anterior.

¿Qué es lo que realmente se está esperando?

Lo primero la pregunta es capciosa. Lo objetivos de las interfaces y las clases abstractas, aunque podrían resultar parecidos, no lo son en verdad y tanto una como otra, pueden explicarse sin ser comparadas entre ellas.

Casi todas las respuestas son, técnicamente hablando, correctas, pero no se busca que el candidato sepa de memoria que es una cosa o la otra, si no intentar vislumbrar su experiencia profesional y su capacidad de razonamiento.

Las interfaces y clases abstractas son elementos que son “olvidados” por muchos programadores en su vida profesional, pero ambos nos permiten solucionar problemas complejos de herencia y reutilización de una manera clara y ordenada. Un programador con algo de experiencia, que haya desarrollado un programa empresarial de mediano tamaño, se habrá encontrado con dichos problemas, y si no ha usado interfaces o clases abstractas, puede ser debido a varias cosas:

  • Realmente el programa no lo necesita, con lo que no sería un programa tan “grande”, lo cual puede arrojar datos sobre la experiencia del candidato.
  • Enredo el código de tal manera, que efectivamente no lo necesito, pero creo un código difícil de entender y ampliar (lo cual no es deseable, pues que va a cambiar y se va a mantener).
  • Una variación anterior, es que no hizo realmente un programa orientado a objetos, sino, un programa estructurado, con lo que no tuvo que enfrentarse a ningún problema relacionado con la programación orientada a objetos (esta problema es el más común).
  • Otro de los motivos de realizar esta pregunta, es analizar la respuesta del candidato, en cuanto a si esta es demasiado “académica”, o puede exponer un ejemplo por sí mismo (sin preguntarle). En el primer caso estaría demostrándome lo que sabe, en el segundo, estaría intentando resolver mi duda, lo cual marcaria una diferencia en un contexto real, porque habría demostrado, que sabe que lo importante son los por qué y no los cómo (lo importante es que hace y no como).

Ahora bien, veamos que es una cosa y la otra.

¿Qué es una clase abstracta?

Es una clase de cual no tiene sentido que se creen objetos, puesto que representa una colección de conceptos abstractos, que no adquieren verdadera identidad hasta que se herede, y se convierta en un elemento concreto.

La clase abstracta debe tener la mayor parte del “flujo de operación” que debe realizar el objeto, pero no tiene determinadas partes o “trozos” de comportamiento, que pudieran variar según la circunstancia, y allí viene la parte de la “abstracción”. Estas partes (ubicadas generalmente en métodos), se dejaron vacías, esperando que alguien herede la clase, y “concrete” la funcionalidad requerida en la clase hija.

¿Qué es una interfaz?

Una Interfaz por otro lado, define una serie de características que debe cumplir una clase, que será usada a su vez por otros métodos o clases. Aquí la necesidad es garantizar que nuestros objetos pueden realizar una determina acción, aunque no nos importa como la realiza, ni siquiera tiene que realizar de forma semejante a otras clases que implementen esa interfaz. En la interfaces se aplica la regla Lo importante es el "Que hace" y no el "Como lo hace"

Próximamente hablaremos con más detalle de ejemplo de interfaces y clases abstractas,

viernes, 2 de enero de 2015

Regla N°7 de la Ingeniería de Software: Primero los objetos, después las base de datos

La informática nació como respuesta a las diversas necesidades de gestión de datos (o información), ya sea para ordenarlos, compararlos, almacenarnos o procesarlos de diversos modos. Esto es un hecho que parece que ha tenido un fuerte impacto a través de generaciones de desarrolladores de software. Me refiero a enfocar un sistema a partir de los datos que procesa en lugar de las operaciones que hace.

Es común ver como se inicia la planeación de un sistema, diseñando tablas de base de datos, y las relaciones que tienen entre ellas, incluso antes de saber que podría hacerse con los datos que almacenan estas, o incluso tener claro los flujos de las operaciones requeridas. Los motivos para comenzar en este punto en particular suelen ser variados, entre los cuales podrían estar los siguientes:

  • Es sencillo: Realmente no requiere complicidad el diseño de tablas como diagramas o incluso directamente mediante diseñadores gráficos de base de datos. Un diagrama de base de datos es, además, fácil de entender a simple vista.
  • Sabemos que vamos a tener datos: Es un hecho, haga lo que haga nuestro sistema, seguramente guarde información (en algún lado), y las base de datos suelen ser un lugar idóneo.
  • Es uno de los límites de nuestro sistema: Generalmente una vez que estamos en la base de datos, está ya no se conecta a ningún otro punto de nuestro sistema, sino que los otros componentes son los que se comunican con la base de datos (de allí que use el término “limite”).
  • Además la conclusión del diagrama es un aporte a la construcción del sistema: Cuando términos el diagrama, en casi todos los servidores, se puede convertir casi directamente en scripts SQL, si es que este proceso no se hace simultáneamente, (crear el diagrama y a la vez las tablas en la base de datos).


Sin embargo el enfocar el diseño a través de la creación de la base de datos, es bastante limitado, precisamente porque se fija solo en un aspecto del sistema, es decir solo pone énfasis en los datos. Dicho enfoque tiene los siguientes inconvenientes:

  • Centrarnos en los datos, nos impide abstraer correctamente el problema, sobre todo si hacemos esta parte al principio del desarrollo. Dicho de otro, como guardamos nuestra información, es un concepto demasiado de “bajo nivel”, para la abstracción que necesitamos al comienzo del diseño. (Por ejemplo: cual es la Primary Key, de qué tamaño deben ser los campos, puede que no sea tan relevante, como el que tiene que hacer el sistema).
  • Las relaciones entre las tablas son limitadas: De hecho son solo tres, de uno a uno, de uno a muchos o de muchos a muchos, según el número de elementos de cada tabla que se ven involucrados en dichas relaciones. En cambio el enfoque orientado a objetos, tiene relaciones más ricas, como relaciones de herencia (lo que favorece la abstracción) o intercambio de mensajes entre objetos.
  • Además, un riesgo adicional es que extralimitemos las funcionalidad de nuestro servidor de base de datos y decidamos que tiene sentido que la lógica de negocio este en los procedimientos almacenados, con lo que desde allí estaremos perdiendo todas las ventajas de un lenguaje de programación orientado a objetos.
  • Los diagramas de base de datos solo representan datos, es decir no representan ningún comportamiento.


El enfoque más idóneo es comenzar el diseño de los objetos o las clases de los que se va a constituir nuestros sistemas. Generalmente al comienzo del diseño, tenemos una idea de cómo queremos hacer las cosas, dicha idea no tiene que estar completa, y cuanto más avancemos en ella, mas compresión tendremos sobre la misma. Podemos ir reduciendo el nivel de abstracción según los necesitemos. Igualmente podemos usar relaciones de objetos, ya sea de jerarquía (herencia) o de contención (un objeto contiene a otro, o a varios). Y lo más importante, se comprende que los datos, van de la mano de las operaciones (funcionalidad), y se diseña el sistema de dicha forma.

A veces he visto diseños de sistemas en los que se crea un “modelo” que contiene datos, pero ningún comportamiento, dejando la funcionalidad en otro conjunto de Modulos, con una programación más o menos estructurada, pero carente de ningún tipo de orientación de objetos. Justificándose este comportamiento con frases como “Los datos deben estar separados del código” o semejantes, completamente sacadas de contexto. La verdad es que esta forma de programar rompe completamente con el paradigma de la programación orienta a objetos, en que los datos y su comportamiento forma un todo inseparable (este “vicio”, tiene un nombre y es “Modelo de dominio anémico”, es un antipatrón de software).

Evidentemente comenzar el diseño por los objetos también tiene sus problemas, por ejemplo el diseño de diagramas UML es más complejo. Aunque pudieran llegar a parecer en principio sencillos, se pueden llegar a complicar mucho, son difíciles de entender por personas sin preparación (digan lo que diga), y casi siempre se convierte en un estorbo y se desactualizan llegado a un punto en el proyecto. La única manera de mitigar esto es que la conversión de UML a código y viceversa sea simultánea y al momento, y que de alguna forma las herramientas muestren información útil de forma gráfica. Las herramientas deben ser útiles al momento de programar, de forma que el UML esté integrado como parte de la programación, y no como un mero elemento documental.

Una vez que tengamos diseñado nuestras clases, con el comportamiento y los datos (o atributos) que estas poseen, podemos comenzar a pensar en cómo debemos persistirlas, y aquí entra, ya por fin, nuestro servidor de base de datos. De esta forma obtendremos un sistema más consistente y sin duda, mucho más escalable.