Hola a todos, este post me lo venía pidiendo el cuerpo desde hace tiempo. Es común oírme decir que la herencia de implementación («extends» para los javeros) es una mierda, y cada vez que lo digo veo caras de estupefacción e incluso recibo alguna que otra crítica. La verdad es que no lo entiendo bien ya que esto se sabe desde hace bastante tiempo, es más hasta tiene un nombre: «problema de la clase base frágil», aunque yo prefiero decir que «extends es una mierda». Incluso, algunos dicen que James Goslin, el padre de JAVA, reconoció que incluir «extends» en el lenguaje fue un gran error. Yo considero que «extends» no tiene nada que ver con la OO (al menos no con el concepto original), sino que se incluyó porque para algo tenían que servir las clases, y se pensó que se podrían usar para reutilizar código mediante la herencia de implementación.
Vamos al lío, os voy a contar una historia. Suponed la siguiente interfaz:
public interface Lista { void insertar(String elemento); void insertarVarios(Collection elementos); String describirContenido(); }
Se trata de una interfaz sencilla que representa una lista, con dos métodos para añadir contenido. Veamos ahora una implementación de tal interfaz:
public class ListaSencilla implements Lista { private List datos = new ArrayList(); public ListaSencilla() { super(); } @Override public void insertar(String elemento) { datos.add(elemento); } @Override public void insertarVarios(Collection elementos) { for (String elemento : elementos) this.insertar(elemento); } @Override public String describirContenido() { StringBuilder descripcion = new StringBuilder("Contenidos: "); for (String elemento : datos) { descripcion.append("'"); descripcion.append(elemento); descripcion.append("' "); } return descripcion.toString(); } }
¿Sencillo verdad? Es una implementación muy directa y no tiene mayor misterio. Ahora supongamos que a otro miembro del equipo, que no escribió la clase «ListaSencilla», le encargan desarrollar una implementación de «Lista» que cada vez que se añada un elemento a ésta, informe a un objeto auditor de tal suceso. Como nuestro desarrollador está enfocado en el objetivo de esta nueva clase, que es informar al auditor, y no le interesa volver a reimplementar y duplicar la lógica de inserción de datos, decide reutilizar código. ¿Qué mejor manera de utilizar el venerable «extends»? Al fin y al cabo, la herencia de implementación es la verdadera ventaja de la OO y su mecanismo más potente para reutilizar código. Mirando la especificación de «Lista» y conociendo que ya hay una implementación «ListaSencilla» disponible, le sale esto:
public class ListaAuditable extends ListaSencilla { private Auditor auditor; public ListaAuditable(Auditor auditor) { super(); this.auditor = auditor; } @Override public void insertar(String elemento) { super.insertar(elemento); this.auditor.elementoInsertado(elemento); } @Override public void insertarVarios(Collection elementos) { super.insertarVarios(elementos); for (String elemento : elementos) this.auditor.elementoInsertado(elemento); } }
El desarrollador ni se molesta en probarlo, obviamente va a funcionar, él ha respetado el contrato de la interfaz y está usando a los amiguetes «extends» y «super». Más tarde le llega un bug, ¡resulta que el auditor es notificado por cada elemento por duplicado! Desconcertado nuestro programador recurre a esa poderosa arma de la ingeniería del software, el depurador, y consigue aclarar el misterio. La implementación del método «insertarVarios» en la superclase llama internamente a «insertar», que como está sobreescrito en la clase hija también notifica al auditor, pero claro, «insertarVarios» de la clase hija también notifica al auditor por su cuenta, y de ahí la doble notificación al auditor. Mas claro agua, y si no lo habéis entendido, ¡depurad!
El desarrollador aprende la valiosa lección de que no sólo debe saber el contrato del código que quiere reutilizar, sino que debe analizar con detalle como está diseñada internamente la clase a reutilizar. Ya sabía él que todo esto del diseño por contrato y las interfaces no eran más que paparruchas para vender libros y cursos. Tras pensar detenidamente cambia el código y le queda:
public class ListaAuditable extends ListaSencilla { private Auditor auditor; public ListaAuditable(Auditor auditor) { super(); this.auditor = auditor; } @Override public void insertar(String elemento) { super.insertar(elemento); this.auditor.elementoInsertado(elemento); } }
Genial, bug resuelto de la mejor manera posible: borrando código. Más contento que unas castañuelas, el desarrollador se va a casa con la satisfacción del buen trabajo cumplido.
Pasa el tiempo, y un desarrollador senior, revisando la clase «ListaSencilla», e ignorante de que esta clase tiene más descendientes que Gengis Khan, decide optimizar el método «insertarVarios». Decide ahorrarse el bucle, y que leches, reutilizar código de la clase «ArrayList» que para eso viene en la JDK, estos desarrolladores junior sufren del síndrome NIH… La nueva versión:
public class ListaSencilla implements Lista { private List datos = new ArrayList(); public ListaSencilla() { super(); } @Override public void insertar(String elemento) { datos.add(elemento); } @Override public void insertarVarios(Collection elementos) { datos.addAll(elementos); } @Override public String describirContenido() { StringBuilder descripcion = new StringBuilder("Contenidos: "); for (String elemento : datos) { descripcion.append("'"); descripcion.append(elemento); descripcion.append("' "); } return descripcion.toString(); } }
¡ Qué sencillez ! ¡ Qué elegancia ! No por nada es un «lead developer» que se conoce la JDK al dedillo. Otro que se va para casa inconsciente de la que acaba de armar. ¿Por qué debería preocuparse? Al fin y al cabo el contrato de la interfaz «Lista» se cumple, y para demostrarlo tiene un test automatizado de hermoso color verde. Mientras tanto, la «ListaAuditable» y posiblemente muchas más subclases de «ListaSencilla» comienzan a fallar sin previo aviso.
Al día siguiente, el «lead developer» decide arreglar el problema, mira a ver el código de «ListaAuditable» y lo solventa en menos que canta un gallo. El código queda así:
public class ListaAuditable extends ListaSencilla { private Auditor auditor; public ListaAuditable(Auditor auditor) { super(); this.auditor = auditor; } @Override public void insertar(String elemento) { super.insertar(elemento); this.auditor.elementoInsertado(elemento); } @Override public void insertarVarios(Collection elementos) { super.insertarVarios(elementos); for (String elemento : elementos) this.auditor.elementoInsertado(elemento); } }
¿Os resulta familiar 😉 ?
«Tonto desarrollador junior», piensa el lead developer, «se nota que en vez de fijarse en la interfaz y los tests, se ha mirado la implementación de la superclase, y por ahorrarse un método la que liado…». En fin, muy triste 😦
Algunos de vosotros pensará que he hecho trampas, que no he usado «bien» la herencia de implementación y que soy un petardo de programador. En el siguiente post explicaré como hacerlo correctamente. Pero ésto no es lo importante, lo que quiero mostrar es que tenemos un mecanismo de reutilización de código, la herencia de implementación, que es más peligrosa que una caja de bombas. Yo prefiero no usar un mecanismo con el que tengo que andarme con pies de plomo, aunque me haya leído y entendido el contrato del código que quiera reutilizar. La herencia de implementación no es segura porque rompe la encapsulación, y necesito saber cómo está implementada la clase padre para poder heredar de ella de forma segura. Todo esto se produce porque mezcla el estado e implementación privada de la superclase con la de la clase hija en una única instancia, en vez de mantenerlas separadas. ¿No sería mejor tener un mecanismo de reutilización de código que siempre fuera seguro? Sí, claro que existe, y lo veremos más adelante.
Demoledor.
Si no me equivoco, toda esta historia se resuelve tan fácilmente como con un «no invocaré a métodos públicos desde otros métodos públicos, a menos que sean finales». Vaya, que el amigo «final» también existe y quizá sea un gran desconocido.
No sé, yo no veo que el extends sea tan malo, símplemente que no estamos acostumbrados a diseñar para herencia. Quizá el error es que por defecto los métodos o clases son «no final» en vez de lo contrario.
Sí, ya digo en el post que en el siguiente explicaré como hacerlo «bien». Pero mi conclusión es que tener un mecanismo que sólo permite la reutilización de código según como esté diseñada internamente la clase no es bueno, sobre todo existiendo otro que no tiene ese problema. En cualquier caso fíjate que eso de «no invocar a públicos si no son finales» no es del todo correcto, ya que «describirContenidos» es público y no final y no es peligroso. También podría poner «insertar» a protected y tener problemas, o sea que el problema no está en invocar a métodos públicos no finales. Además si pones a final «insertar» e «insertarVarios», ¿cómo reutilizas el código de estos usando «extends» si no los puedes sobreescribir? Recuerda que debes cumplir la interfaz «Lista». Bueno, me estoy adelantando a mi próximo post.
¡Hola!
What I don’t gusta of that código is el Spanglish. Creo that it’s mejor when all the código is written en inglés. Otherwise, se lee like this comentario.
🙂
Je, je, totalmente de acuerdo, pero así es más realista. En la organización donde trabaja el desarrollador junior y el «lead developer» el código en castellano, ¡como ordena la política corporativa!
Tienes razón, donde dije «públicos» quería decir «no privados». Vaya, «no invocar a métodos heredables desde otros métodos», donde heredable es «no privado ni final».
Respecto a cómo reutilizar el código, si no quieres que sean final (es decir, si quieres que tu clase sea heredable), debes diseñar para herencia. En este caso la solución creo que es bien conocida: hacer un par de métodos privados equivalentes que contengan la implementación en si, y hacer que tus métodos públicos llamen a dichos métodos privados (es decir, evitas que tu método no privado insertarVarios llame al método no privado y no final insertar).
Es decir:
private insertarEnArrayList(element) { datos.add(element) }
private insertarVariosEnArrayList(elements)
{
for (element in elements) this.insertarEnArrayList(element)
}
public insertar(element) { this.insertarEnArrayList(element) }
public insertarVarios(elements) { this.insertarVariosEnArrayList(elements) }
Ocultas la implementación en métodos privados, y de paso si alguien te sobreescribe un método no privado, sabes que eso no va modificar el comportamiento de otros métodos tuyos no privados.
A lo que voy al final es que me parece un poco incongruente la postura de «jo, es que si pongo final no me pueden heredar», pero por otro lado no diseñar tu clase para herencia. Por eso decía que antes de quitar extends, prefería hacer que los métodos fuesen final por defecto.
Sí, esta claro, hay que diseñar con la herencia en la mente, y antes de extender de una clase hay que mirar no sólo su contrato, sino si está diseñada correctamente para herencia, es decir, inspeccionar su implementación, y no sólo ver si cumple el contrato de la interfaz. ¿No sería mejor si pudiéramos reutilizar código sin tener que mirar cómo está diseñada una clase por dentro? ¿No sería mejor poder centrarme en cumplir el contrato de la interfaz cuando implemento una clase y que el hecho de que alguien me vaya a reutilizar en el futuro no condicione para nada mi implementación interna? Lo dicho, me adelanto.
Jeje ok, no te tiro más de la lengua. Aun estando medianamente de acuerdo contigo (medianamente porque para mi la solución sería otra :-), me enciende los «esto es un truño» cuando la realidad es que «esto no sé usarlo» (y por favor que quede claro que no va por ti, que está claro que no es el caso). Quedo atento a esa segunda sobre el tema para retomar el debate si procede :-D.
solo puedo añadir una cosa al respecto:
soLid!!!!
con acento en la L.
aclaracion antes de que amodeo me regañe:
la L se refiere al problema contrario, que la clase hija pueda sustituir a la padre, pero si lo tienes en cuenta a la hora de implementar la clase hija, seguramente detectaras rapidamente cualquier «efecto» inesperado en tu «extension» de la implemetación!!!
¡ Serás hijo de Liskov ! 😀 Liskov se refiere a que cuando heredes cumplas al menos el contrato de la superclase, y si puedes más. De esta manera aseguras que puedas sustituir al padre con cualquier instancia de la clase hija. Si te fijas en los ejemplos no he roto Liskov por ningún sitio, porque el contrato «Lista» se sigue cumpliendo. Puedo sustituir ListaSencilla por ListaAuditable en cualquier sitio, ambas cumplen la interfaz, y no rompen los posibles tests de «Lista». El problema viene al intentar implementar una extensión del contrato: informar al auditor de los nuevos elementos.
correctisimo!!! sabia que al final pillaba!!!
La verdad es que no me había planteado a fondo la problemática que surge con el extends.
Yo hasta ahora ando desarrollando como deigote y creo que muchos de los desarrolladores Java lo hacemos así. Realmente siempre he creído que esta era la manera más correcta pero después de leer el post pienso que si existe una forma más segura de utilizar la herencia bienvenida sea 🙂
También estoy de acuerdo con deigote que decir que extends es una mierda te hace entrar a flamear pero creo que es una manera muy acertada de atraer a la gente a leer el post XD
De momento no he visto a nadie flameando 🙂 El problema no está en los desarrolladores JAVA, el extends está también en otros lenguajes, como RUBY, o como JavaScript, pero con otro nombre.
Si hubiera llamado a este post, «¡ Prototype es una mierda !», y hubiera puesto los ejemplos en JavaScript, mi argumento hubiera sido exactamente el mismo. Eso sí, tal vez hubiera tenido menos visitas.
No no el post es totalmente correcto lo q me llamo la atención fue el titulo jejeje.
Cuando lo he leído he pensado también en lenguajes como Ruby que también tendrían el mismo problema.
Espero interesado el siguiente post 🙂
Sí, el problema planteado es de la herencia en general, sea el lenguaje que sea….
El problema realmente es mas general: esta en cualquier mecanismo de composicion entre unidades de codigo que suponga que una dependa de la implementacion (interna o incluso externa si nos ponemos radicales) de la otra. Ese mismo problema puede surgir usando delegacion, decoracion o lo que sea
Discutiremos esto más adelante.
Brutal Enrique!!!
Gracias por el post 🙂
Aunque es cierto que el problema que describes existe, en este caso (y similares) el problema es que listaAuditable no debería ser una especialización. Lo que hace es añadir una feature no relacionada con la lista en sí y por tanto lo mas lógico es que fuera un decorador. De todos modos una prueba que incluya un mock para el auditor revelaria el problema y nadie se iría a casa habiendo roto el código.
Efectivamente el decorator es mejor opción. ¿A qué te refieres con especialización?¿Te refieres a herencia de tipos? Efectivamente ListaSencilla y ListaAuditable son exactamente del mismo tipo, «Lista», y no hay herencia de tipos entre ellas ni especialización. Mi punto es el siguiente, si la herencia de tipos es mejor hacerla a través de interfaces, «extends» entre interfaces, e «implements», y «extends» entre clases es peligroso, ¿para que quiero la herencia de implementación?¿Para que me sirve?
Sobre lo de los tests, lo que tu dices es cierto, pero sólo en el caso de que ListaSencilla y ListaAuditable estén en la misma unidad build. Si ListaSencilla formara parte de un framework open source, y ListaAuditable de un proyecto que usa ese framework, tenemos una situación bien distinta. La gente que mantiene el framework no puede ejecutar todos los tests de todos los proyectos que usan el framework, sólo los tests del framework en si.
Buen aviso a navegantes aunque el ejemplo (y la conclusion) es un poco parcial. Parcial en cuanto al contexto en el que se produce el problema: Se crea una clase base de una api «publica». Se extiende, por otros programadores, en otras unidades de compilacion y se modifica la clase base sin tener en cuenta esas extensiones.
Ese es precisamente el contexto donde mas peligrosa es la herencia y casi automaticamente nos da el contexto en el que la herencia es mucho menos peligrosa: usar la herencia como delegacion «barata»(en codigo) en la implementacion interna de un componente y realizada por un equipo cohesionado.
Y ya que nos ponemos a mejorar la composabilidad y la no cohesion entre componentes por que no dejar de forzar el uso de tipos definidos por el usuario y usar solamente unos poco tipos que pertenezcan al nucleo del lenguaje utilizado (Collection,Map,etc). O sea fuera extends y fuera implements tambien.
El crear una lista propia habiendo una lista ya hecha en Java es un mero ejemplo. Intento poner un ejemplo sencillo donde no se genere mucho ruido. De todas formas, en un sistema real es común definir cosas como «CatalogoDeProductos», con un código muy similar.
Mmm no me refiero al ejemplo concreto de la lista sino en que la situacion que has descrito las clase esta siendo usada desde fuera de su hogar original (o sea esta expuesta, conscientemente o no en el api publica de ese «hogar») y es usada por un programador y modificada posteriormente por otro sin que haya comunicacion entre ellos. Hablo pues del contexto no tecnico del ejemplo, que es lo que en mi opinion hace realmente peligrosa la herencia (pero tambien otras tecnicas de uso entre unidades de software).
Eso se une al problema de que en ese uso se dependa de la implementacion interna y se una en sagrado matrimonio a dos fragmentos de codigo que viven en paises muy lejanos, lo que haga que cualquier pequeño cambio destroce la relacion. ¿Quiere decir eso que el matrimonio es peligroso en todo caso? Pues tal vez en muchos si pero no en todos. 😛
Hay clases (o modulos o lo que sea) que estan hechas las unas para las otras desde su nacimiento.
Dicho esto tengo que admitir tambien que hay tecnicas que tienden mas que otras estrechar el acoplamiento y a crear dependencias de implementacion: la herencia hace visibles mas miembros de la clase y permite usarlos de manera mas comoda que con otros usos (p.e la delegacion que es su sustituto natural). Tambien hay que admitir que se ha abusado alegremente de la herencia (en java sobre todo) y yo he sido el primero en sufrirla (y lo sigo haciendo) con librerias como struts.
Sin embargo para mi lo esencial del problema esta en la organizacion del trabajo del equipo (o de tu propio trabajo si hablamos de un unico programador) y en crear dependencias de la implementacion interna entre unidades de codigo que no tienen que tenerlas.
Cierto, cierto. Tienes razón, lo más peligroso no es el «extends», sino los equipos que no hablan entre si.
Tu problema es que estás buscando a Dios.
No hay una solución a todos tus problemas. Cada problema requiere un tipo de solución. En algunos casos problemas parecidos requieren soluciones completamente distintas.
Quizás debieras echar un vistazo al Collection Framework de Java y luego nos cuentas las líneas de código que habrías acabado tirando sin extends…
Imagino que has leído recientemente «Favor composition over inheritance» y, sí, es algo bueno, pero no es la solución a todos tur problemas!
Espero sepas entender el sentido de mi mensaje. Aplica las soluciones a tus problemas que tengan sentido en cada momento. Si extends es la solución a alguno de tus problemas (Goslig así lo vio…), USALO!!!
Un saludo,
El Collection Framework de JAVA lo conozco bastante bien. Como he dicho antes esto es un mero ejemplo, quería un ejemplo sencillo que no confundiera el mensaje, y cómo dije también antes, a veces cuando modelas negocio es bueno encapsular los tipos primitivos y del sistema. Es mejor cosas como CatalogoDeProductos, UltimosMovimientos o Empleados a Collection<Producto>, List<Movimiento> o Set<Empleado>.
Veo que algunos no parais de repetir lo mismo: «es que extends hay que saber usarlo», «es que extends es muy útil en algunas circunstancias», «es que quieres usar la misma herramienta para todos los casos», etc… De momento no he visto ningún caso concreto en el que la herencia de implementación sea la mejor técnica a aplicar. Si alguno tiene un ejemplo concreto que lo publique en su blog, y que me lo diga, para poder enlazarlo desde el mio. Al fin y al cabo el «goto» también era muy «útil», e incluso se podía usar «bien».
P.S. No, no he leido recientemente «Favor composition over inheritance», estas ideas empecé a verlas en el año 2000 cuando me leí el GoF, y unos pocos años después leí algunos posts confirmándolo
«Quizás debieras echar un vistazo al Collection Framework de Java y luego nos cuentas las líneas de código que habrías acabado tirando sin extends…»
Plenamente de acuerdo Albert J. Reily, imagina Swing y en general tu razonamiento es aplicable a cualquier árbol de derivación hecho con sentido común, ya que hablamos en términos de «mierda», me imagino lo que sale al convertir a composición una herencia de implementación de tres clases (tres niveles): seguramente una mierda.
Varias cosas:
No me creo que James Gosling dijera exactamente eso, lo que si me creo es que dijera algo así como que hay un excesivo número de clases públicas en el core de Java en vez de ser interfaces y en eso estoy totalmente de acuerdo, el framework de colecciones a base de interface/implementación es un ejemplo de una API bien hecha en ese sentido y podría ser mejor aún con algún tipo de sistema de factoría.
El concepto de extends es MUY anterior a Java y es NUCLEAR de la orientación a objetos, sin herencia de implementación la OOP se reduce prácticamente a NADA.
«la herencia de implementación, que es más peligrosa que una caja de bombas»
Cierto, pero la solución NO debería ser «como es peligrosa es mierda»
Es como decir
«Como pusimos al niño a los controles de la central nuclear y la lió la energía nuclear es mala, mejor generar energía pedaleando»
«Como le dimos al niño un bisturí la cirugía es mala y la lió, mejor con hierbas»
La solución es decirle al junior, niño no toques si no sabes lo que estás haciendo, por muy buena y encapsulada que sea tu API jamás podrás impedir que un uso indebido de la misma.
Hechad un vistazo al código fuente de vuestro framework/herramienta favorita, Tomcat, Hibernate, Spring, GlassFish, las propias tripas del JDK… etc y verás herencias de implementación por un tubo… afortunadamente.
Lo siento en mi opinión esta moda de que la herencia es mala lo que está llevando es que una generación de programadores estén haciendo de nuevo código C por mucho Java, Scala o el lenguaje «moderno» de turno que usen.
No se porqué os lo tomais como un ataque a JAVA. La herencia de implementación es una mierda y no importa si es con JAVA o con Ruby o con JavaScript o con C++. De hecho el problema se detectó en los tiempos del C++, donde se había usado intensivamente la herencia de implementación como mecanismo de reutilización en las MFC de Windows. No se si alguien se acuerda de esos tiempos, pero a mi me pilló de estudiante y aun tengo pesadillas….
La herencia de implementación no es nuclear a la OO, es nuclear a la orientación a clases, que son dos cosas diferentes. Y da igual, aunque fuera nuclear a la OO original, ¿y qué? No veo como ayuda eso a la herencia de implementación como técnica.
Lo que yo tengo entendido que dijo James Gosling es que si tuviera que volver a «inventar» JAVA, no le pondría «clases», tras las caras de estupefacción de los asistentes, aclaró que el verdadero problema era el «extends». De todas formas esto es apocrifo, no he conseguido encontrar ninguna cita directa de él sobre esto (pero sí de asistentes que dijeron que el había dicho…). No es importante, da igual lo que dijera James Gosling, Martin Fowler o el tio Bob, no usaré el argumento de autoridad. Lo importante es que «extends» es realmente peligroso, y prefiero no usar nitroglicerina si puedo usar explosivo plástico, que es más seguro y más potente.
A mi personalmente me da bastante igual lo que diga el guru de turno, después de cientos y cientos y cientos de herencias de implementación en C++ y Java, mi opinión es la contraria, prescindir de la herencia de implementación ESO SI que es para mi una verdadera pesadilla, y no, no estoy pensando en el típico ejemplo de un nivel de herencia de una clase con un par de métodos, en donde las cosas no cambian prácticamente nada si se usa composición, estoy pensando en cosas como esta:
http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/JCheckBox.html
Sólo imaginar ese código como composición me pondría los pelos de punta.
(aunque con esto no quiero decir que AWT/Swing sea la mejor API diseñada del mundo ni mucho menos).
Aunque no se que pasa pero la gente que hace software complejo de narices es que no aprende de los «gurus» y se empeña una y otra vez en usar herencia de implementación para resolver problemas reales:
http://docs.oracle.com/javafx/2.0/api/javafx/scene/control/RadioButton.html
Aunque puestos a elegir gurus prefiero a Cedric Beust
«We all know that inheritance can be abused, but isn’t it so for every feature of every language on the planet? Instead of giving in to simple sensationalism, how about studying the pros and cons of inheritance and trying to educate your readers objectively?»
http://beust.com/weblog2/archives/000004.html
Pero en fin que cada uno piense lo que quiera pero en mi opinión hay que tener mucho cuidado cuando uno mismo se pone piedras en el camino.
Yo lo que cuento es en base a mi experiencia, y en mi experiencia «extends» no me lleva a un buen diseño y he terminado por dejar de usarlo. Lo curioso que ahora que no lo uso, no lo echo de menos.
Entiendo que tu tengas otro punto de vista, pero no termino de entender cuales son tus argumentos. Bueno, a ver si en el próximo me explico algo mejor, que me parece que han quedado algunos puntos en el aire, y hacemos el debate más productivo.
[…] Comentarios « ¡Extends es una mierda! ¿No te habías enterado? […]
[…] o el “Decorator” entre otros. Antes de nada voy a resumir lo contado hasta ahora. En el primer post vimos cómo usar la herencia de implementación de forma “ingenua” nos podía meter en […]
Como soy bastante friki, he probado ese mismo ejemplo en Smalltalk, en concreto en Pharo, http://www.pharo-project.org , y «funciona bien»…
ListaAuditable Contenidos: pepe, Juan, Luis,
Notificación Auditores: pepe, Juan, Luis,
Es decir, al llamar al insertar de la clase padre no «rebota» al insertar de la clase auditada…
Supongo que como Smalltalk sólo existe desde los 70 no han podido «copiarlo bien»…
Saludos
Me cuesta entender el argumento, para dos o tres clases perfecto, pero para inyección de dependencias de clases heredadas que no sean de primer nivel que implementen una interfaz, cómo lo harías, ¿haciendo clases paralelas?, no concibo POO sin extends sinceramente. ¿Ejemplos prácticos?, tienes cientos, tengo unos cuántos en mi equipo si quieres te los envío por mail sin problema, que se puede hacer de otra forma, si claro, también puedes crear un programa en una o dos clases eso no significa que esté bien.
Y si, «extends» hay que utilizarlo justificadamente, y no es fácil siempre verlo, mucha gente tira de extends, sin ningún sentido, y eso confunde, pero tan malo es una cosa como decir que no es necesario, me parece una barbaridad…
Un saludo.
Sí, para inyección de dependencias en JAVA, tendría una interface que actúa como «role» interfaz para la dependencia. Después le paso una implementación de la interfaz.
Si alguna vez has tenido que hacer «extends» entre implementaciones de una misma «role» interfaz, eso no tiene nada que ver con inyección de dependencias, sino es más bien una elección de diseño de las implementaciones. Yo por ejemplo, casi nunca he tenido que hacerlo, y todas las implementaciones de una misma interfaz son «paralelas» como dices.
Los conceptos esenciales de la POO son objetos, encapsulación, y mensajes entre objetos. La herencia de implementación es un añadido posterior, y realmente no la necesitas! Ojo, en algunos lenguajes, como JAVA, «extends» está muy incrustado en el diseño, de ahi que a veces confundamos lo que se hace en JAVA con «verdadera» OO, cuando JAVA no es realmente el mejor ejemplo de OO.