Personalmente, no tengo muy claro como, cuando y donde utilizar una interfaz.
Desde el punto de vista de código no hay ningún problema, tengo claro como declarar una interfaz, como implementarla, las diferencias que existen entre una interfaz y una clase abstracta, pero “conceptualmente” me cuesta aún encajar el concepto. Casi siempre opto por la herencia que la tengo más interiorizada.
Una interfaz es un "contrato de implementación" entre clases. Es decir, si una clase implementa una interfaz se compromete a implementar todos los métodos declarados en la interfaz y a dotarlos de cuerpo para llevar a cabo cierta funcionalidad (porque la interfaz sólo contiene firmas de métodos y no su implementación). Es casi lo mismo que cuando heredamos de una clase abstracta e implementamos en la clase derivada los métodos abstractos que define la clase base. A este respecto cabe mencionar que una clase abstracta puede o no tener métodos abstractos, incluso puede tener métodos normales (que se heredarán sin más) y métodos abstractos que será necesario implementar en la clase derivada. Lo que está claro es que en el momento que una clase tiene algún método abstracto tiene que ser declarada como clase abstracta (es decir, no podrá ser instanciada y sólo servirá como clase base).
Leyendo por Internet he llegado a unas conclusiones que quizás me ayuden a entender mejor como encajan las interfaces en la POO.
La primera y más recurrente, y que incluso yo me subo al carro de aquellos que ya la sabían, es que las interfaces ayudan a “simular” la herencia múltiple en lenguajes orientados a objetos que no la soportan. Además, al implementar una interfaz tampoco se hereda ningún código, son sólo firmas a las que hay que dar cuerpo durante la implementación (no como con la herencia que podríamos haber heredado comportamiento y características).
La segunda y definitiva (en mi opinión) es que la herencia “agrupa” clases por “lo que son”, mientras que las interfaces “agrupan” clases por “lo que hacen”. Esto que parece tan obvio, en mi caso ha sido clave para entender las interfaces. Por ejemplo, un Deportivo y un Tractor son un tipo de Coche, luego utilizaríamos la herencia porque podemos agrupar por “lo que son”, existe una relación de parentesco entre las clases. Es decir, hay un padre “Coche” e hijos “Deportivo” y “Tractor”. Si por ejemplo Coche tiene un método Arrancar abstracto (que sería implementado por Deportivo y Tractor), podríamos declarar un método que recibiera objetos de tipo Coche (fijarse en que aquí podríamos asignar una referencia a un objeto de tipo Coche, pero también de tipo Deportivo o Tractor) y los arrancará sin tener que saber exactamente de qué tipo son. Esto es polimorfismo. Me da igual el tipo porque el código para arrancarlos será el mismo.
Otro concepto clave en la herencia es que donde espero un objeto de tipo Coche, siempre puedo poner un objeto de un tipo derivado, como Deportivo o Tractor.
Volviendo a las interfaces, dijimos que agrupaban por “lo que hacen”. Esto se ve claro con un ejemplo, si tenemos una interfaz IArrancable con un método Arrancar (lo de la I por delante es una convención, es decir, una interfaz siempre se llama I<Nombre>) y queremos que una clase Deportivo y una clase Tractor la implementen, tendremos que darle cuerpo al método en su implementación (distinto ya por ejemplo de una clase abstracta donde Arrancar podría ser abstracta y en ese caso igualmente tendríamos que implementarla en Deportivo y Tractor, pero también podría no ser abstracta y de este modo, Deportivo y Tractor ya sabrían arrancar de serie sin tener que hacer nada). Finalmente y como decía, implementamos IArrancable en Deportivo y Tractor, pero también la implementamos en un objeto Computadora, de este modo ahora el polimorfismo es más abierto, puesto que podríamos hacer un método que recibiera un objeto de tipo IArrancable y con el mismo código arrancaríamos un Deportivo, un Tractor y una Computadora (que nada tiene que ver con los coches).
Espero que sea útil.
Un saludo!
No hay comentarios:
Publicar un comentario