Friday, September 28, 2018

BUENAS PRÁCTICAS: Programar una interfaz, no una implementacion y Favorecer la composición de objetos sobre la herencia de clase


Programar una interfaz, no una implementacion.


Es posible que hayas escuchado la frase "program to an interface, not an implementation", y te hayan dicho que es esencial seguir este principio si se quiere ser un buen programador orientado a objetos. ¿Pero qué significa exactamente? En esta publicación de blog se tratará de explicar este concepto e ilustrarlo con un ejemplo.

En esencia, el propósito de la programación de una interfaz es fomentar el acoplamiento flexible entre las clases, reduciendo la dependencia de una clase sobre otra. Esto hace que sea más fácil modificar el código en una clase sin afectar a las demás, y crea una mayor flexibilidad en el sistema.

Programando una interfaz


Imagina que eres el gerente de una empresa de software muy exitosa. Los gerentes de oficina a menudo tienen sed y necesitan una taza de café. Sin embargo, como gerente, obviamente estás demasiado ocupado como para hacer uno tú mismo, así que consigues que tu secretaria te haga uno. En el código, esto podría expresarse como:

class OfficeManager{
        public function getCoffee(){
               //instanciando una secretaria
               $secretary = new Secretary();
               //Dile a la secretaria como hacer una taza de café.
               $coffee = $secretary->getMug()
                                    ->fillWithHotWater()
                                    ->addCoffee();
               //bebe el café 
               $this->drink($coffee);
        }
}
 
Ahora, se puede obtener el café así:
 
$you = new OfficeManager();
$you->getCoffee();
 
Observando el código de arriba ¿Puede haber algo malo en él? ¿Qué tan flexible es este enfoque? Veamos.

Imagine que necesita su café, y llama a la secretaria como lo hace normalmente, pero espera, ¡la secretaria ha llamado y ha dicho que está enferma! ¿Qué se puede hacer? Podrías pedirle al siguiente empleado (llamémosle Eric) que se encuentra frente a tu oficina y decirle que te traiga un café, pero ¿cómo se podría hacer? El método getCoffee no tiene forma de obtener un café sin usar una secretaria. No importa cuánto quiere Eric hacerte un café, no puede, porque el método anterior no es flexible en absoluto. Este es un excelente ejemplo de programación para una implementación - está confiando en una clase "concreta" para que el programa funcione e introduce dependencias no deseadas entre los objetos.
 
Vamos a refactorizar el método getCoffee para aceptar un objeto empleado como argumento:
 
class OfficeManager{
        public function getCoffee($employee){
               //Dile al empleado como hacer una taza de café.
               $coffee = $employee->getMug()
                                   ->fillWithHotWater()
                                   ->addCoffee();
               //bebe café.
               $this->drink($coffee);
        }
}
 
Esta es una solución más flexible. Ahora podemos decirle a Eric que prepare un café:
 
$you = new OfficeManager();
$eric = new Eric();
$you->getCoffee($eric);
 
De hecho, si se siente particularmente sediento, podría crear una gran variedad de empleados para que le traigan café:

$you = new OfficeManager();
$employees = array(
        new Eric(),
        new Jane(),
        new George()
);
foreach($employees as $employee){
        $you->getCoffee($employee);
}
 
Hasta ahora bien, pero hay otro aspecto de nuestro método getCoffee que es menos que ideal. ¿Qué pasa si en el ejemplo anterior, Eric, Jane y George hicieron café de diferentes maneras? ¿Qué pasa si Jane prefiere agregar café antes del agua caliente? ¿Qué pasa si George siempre pone el café en su taza azul favorita? En la actualidad, nuestro método getCoffee limita a los empleados a una cierta forma de hacer café. Pero mientras obtenga el café, no debería importar cómo esté hecho. Tampoco hay garantía de que todos los empleados contengan los mismos métodos, por lo que el código anterior podría romperse fácilmente.
 
Una cosa más: ¿qué pasa si compra una nueva máquina de café para su oficina? Entonces, una clase completamente no relacionada, CoffeeMachine, podría implementar esta funcionalidad.
Hay que hacer que este método sea aún más flexible utilizando una interfaz. Una interfaz define un conjunto de métodos públicos, un contrato, que todas las clases que implementen esa interfaz deben contener.
 
Se comienza creando una interfaz llamada CoffeeMaker. Esta interfaz contendrá un método público llamado makeCoffee. Se garantiza que cualquier clase que implemente una interfaz contenga los métodos definidos en esa interfaz. Cualquier clase que implemente la interfaz de CoffeeMaker DEBE contener un método público llamado makeCoffee.
 
interface CoffeeMaker{
        public function makeCoffee();
}
 
Tenga en cuenta que la interfaz no define cómo debe implementarse el método makeCoffee, solo que debería haber uno. Ahora definamos algunos empleados que implementan esta interfaz. Hagamos que Eric implemente esta interfaz y, para asegurarnos de que tenemos más empleados que nos preparen un café, creemos otro empleado llamado Jane, que también implementa la interfaz de CoffeeMaker.

class Eric implements CoffeeMaker{
        public function makeCoffee(){
               $this->getMug();
               $this->addCoffee();
               $this->addHotWater();
               $this->addMilk();
               return $this->coffee;
        }
}
 
class Jane implements CoffeeMaker{
        public function makeCoffee(){
               $this->getMug();
               $this->addHotWater();
               $this->addMilk();
               $this->addCoffee();
               return $this->coffee;
        }
}

Como puede ver, Eric y Jane tienen una función pública llamada makeCoffee. Si no lo hicieran, se produciría un error antes de ejecutar cualquier código, porque DEBEN ajustarse a la interfaz. Ahora habrá notado que en los ejemplos anteriores, Eric y Jane implementan el método makeCoffee de forma diferente: Eric agrega el café primero, luego agrega agua caliente y luego agrega leche. Jane, por otro lado, agrega primero el agua caliente, luego la leche y luego el café. No importa cómo Eric o Jane implementen el método makeCoffee, siempre y cuando lo implementen.

Ahora, hay que refactorizar el método getCoffee de OfficeManager para lograr que los nuevos empleados hagan el café. En lugar de tomar cualquier objeto como argumento, cambiaremos el método getCoffee para aceptar un objeto de cualquier clase que implemente la interfaz de CoffeeMaker. Podemos suponer con seguridad que cualquier empleado que implemente esta interfaz tendrá un método público llamado makeCoffee.

class OfficeManager{
        public function getCoffee(CoffeeMaker $employee){
               $coffee = $employee->makeCoffee();
               $this->drinkCoffee();
        }
}
 
Entonces, ¿cómo se compara nuestra solución actual con el principio del "programa una interfaz, no la implementación", y por qué es mejor de lo que teníamos al principio?

Bueno, aquí hay una lista de ventajas que acabamos de incorporar a nuestro código:

  • Eliminada la dependencia de la clase "concreta" secretaria
  • Mayor flexibilidad: ahora se puede usar cualquier clase que implemente nuestra interfaz
  • Sin dependencia de cómo se implementa el método getCoffee: podemos cambiar la forma en que los empleados preparan el café sin afectar otros aspectos del programa
Ahora esta solución es mucho más flexible de lo que teníamos que empezar.

Favorecer la composición de objetos sobre la herencia de clase


Empecemos con la definición: Herencia es cuando un objeto o clase se basa en otro objeto o clase, usando la misma implementación o comportamiento. Esto es un mecanismo para la reutilización de código para permitirnos extensiones independientes del software original mediante clases públicas e interfaces.

Por otra parte la composición quiere decir que tenemos una instancia de una clase que contiene instancias de otras clases que implementan las funciones deseadas. Es decir, estamos delegando las tareas que nos mandan a hacer a aquella pieza de código que sabe hacerlas. El código que ejecuta esa tarea concreta está sólo en esa pieza y todos delegan el ella para ejecutar dicha tarea. Por lo tanto estamos reutilizando código de nuevo.

La composición de objetos y la herencia son dos técnicas para reutilizar la funcionalidad en sistemas orientados a objetos. En general, la composición de objetos debería ser favorecida por sobre la herencia. Promueve clases más pequeñas y enfocadas y jerarquías de herencia más pequeñas.

Troels Knak-Nielsen escribió : La herencia de clase es una mezcla de dos conceptos. La clase que se extiende hereda la implementación de los padres (funciones / métodos) y hereda el tipo de los padres. En un lenguaje estático, este último es bastante importante, ya que no se pueden mezclar libremente tipos. Entonces, si algún método espera un argumento de un tipo dado, puede usar la herencia para satisfacer esto. En un lenguaje de tipado dinámico que no es un problema, simplemente puede implementar el comportamiento esperado y eso es todo. Si necesita un contrato más explícito, puede documentarlo o, dado que PHP tiene una especie de punto medio en este asunto, puede usar una interfaz estáticamente estátizada (por ejemplo, implementa Person, en lugar de extends Person) para hacer lo mismo. Dado que PHP está tipado dinámicamente, esta solución (un poco más detallada y restrictiva) es puramente opcional.

El otro uso de la herencia de clase es reutilizar la implementación. Si su Persona de la clase abstracta es extendida por una subclase Empleador, usted tendría acceso al mismo código en el Empleador como lo hace en Persona. También puede lograr la reutilización de código con la composición, pero requiere un poco más de trabajo. El empleador tendría que implementar un contenedor que delegue el control a una instancia de Persona en este caso. P.ej.:

clase Persona {
  function sayHello () {
    echo "¡Hola, mundo!";
  }
}
Empleador de clase {
  protegido $ persona;
  function __construct () {
    $ this-> person = new Person ();
  }
  function sayHello () {
    $ this-> person-> sayHello ();
  }
}

más bien que:

clase Persona {
  function sayHello () {
    echo "¡Hola, mundo!";
  }
}
clase Empleador extiende Persona {}

Como puede ver, hay un poco más de trabajo por hacer, y es por eso que las personas a menudo usan la herencia en estos casos. El costo, sin embargo, es que la relación Persona-Empleador ahora es inamovible; No se puede cambiar ni interceptar en tiempo de ejecución. También está la cuestión de la claridad. Si bien el código de composición es más detallado, también es muy claro sobre lo que hace. Puedes mirar el código y saber lo que hace. Con la versión de herencia, necesita ver la superclase para descubrir qué hace. Algunas veces hay múltiples niveles de herencia, lo que hace que rastree arriba y abajo de la cadena para averiguar exactamente qué código está disponible en la clase concreta. Finalmente, está el problema de la herencia múltiple. En PHP, no puedes. Solo tiene una oportunidad para heredar, por lo que si quiere reutilizar el código de dos lugares, bueno, no tiene suerte.

5 razones para usar composición sobre herencia en Java y OOP


En la composición, una clase que desea utilizar la funcionalidad de una clase existente no heredan, sino que contiene una referencia de esa clase en una variable miembro, por eso la composición del nombre. Las relaciones de herencia y composición también se conocen como relaciones IS-A y HAS-A. Debido a la relación IS-A, se puede pasar una instancia de subclase a un método, que acepta una instancia de superclase. Este es un tipo de Polimorfismo, que se logra usando Herencia.

Una variable de referencia de superclase puede referirse a una instancia de subclase. Al usar la composición, no obtienes este comportamiento, pero aún ofrece mucho más para modificar el equilibrio de su lado.

1) Una razón para favorecer la composición sobre la herencia en Java es que Java no admite herencia múltiple. Dado que solo puede extender una clase en Java, pero si necesita múltiples funciones como, por ejemplo, para leer y escribir datos de caracteres en archivos, necesita la funcionalidad Lector y Escritor y tenerlos como miembros privados facilita su trabajo. Eso se llama composición. Si sigue la programación para la interfaz más que el principio de implementación, y usa el tipo de clase base como variable miembro, puede usar una implementación diferente de Reader y Writer en diferentes situaciones. No obtendrá esta flexibilidad mediante el uso de herencia, en caso de extender una clase, solo obtendrá instalaciones disponibles en tiempo de compilación.

2) La composición ofrece una mejor capacidad de prueba de una clase que la herencia. Si una clase está compuesta por otra clase, puede crear fácilmente un objeto simulado representando la clase compuesta por el bien de la prueba. La herencia no proporciona este lujo. Para probar la clase derivada, debes necesitar su súper clase. Dado que las pruebas unitarias son una de las cosas más importantes a considerar durante el desarrollo del software, especialmente en el desarrollo impulsado por pruebas, la composición gana sobre la herencia.

3) Muchos patrones de diseño orientados a objetos mencionados por Gang of Four en patrones de diseño clásicos intemporales: elementos de software reutilizable orientado a objetos, favorece la composición sobre la herencia. Ejemplos clásicos de esto es el patrón de diseño de estrategia , donde la composición y la delegación se utilizan para cambiar el comportamiento del contexto, sin tocar el código de contexto. Desde el contexto utiliza la composición para mantener la estrategia, en lugar de obtenerla a través de la herencia, es fácil proporcionar una nueva implementación de la Estrategia en tiempo de ejecución. Otro buen ejemplo del uso de la composición sobre la herencia es el patrón de diseño Decorador. En el patrón Decorator , no se amplía ninguna clase para agregar funcionalidad adicional, sino que conservamos una instancia de la clase que estamos decorando y delegamos la tarea original a esa clase después de hacer la decoración. Esta es una de las mayores pruebas de elegir la composición sobre la herencia, ya que estos patrones de diseño están bien probados y probados en diferentes escenarios y resisten la prueba del tiempo, manteniéndose a la altura.

4) Aunque tanto la Composición como la Herencia le permiten reutilizar el código, una de las desventajas de la herencia es que rompe la encapsulación. Si la subclase depende del comportamiento de la superclase para su funcionamiento, de repente se vuelve frágil. Cuando el comportamiento de la superclase cambia, la funcionalidad en la subclase puede romperse, sin ningún cambio en su parte. Un ejemplo de herencia que hace que el código sea frágil es el método add () y addAll () de HashSet . Supongamos, si addAll () de HashSet se implementa llamando a add ()método y escribe una subclase de HashSet, que cifra el contenido antes de insertarlo en HashSet. Dado que hay solo un método add (), que puede insertar objetos en HashSet, usted anula estos métodos y llama a su método encrypt () anulando add () . Esto también cubre automáticamente addAll () , porque addAll () se implementa mediante add () , se ve muy atractivo. Si miras de cerca verás que esta implementación es frágil, porque se basa en el comportamiento de la clase superior. Si la clase base quiere mejorar el rendimiento e implementa addAll () sin llamar al método add () , el siguiente ejemplo se romperá.

la  clase  pública EncryptedHashSet  extiende HashSet {
    .....
    public  boolean  add (Object o) {
        return  super . agregar (cifrar (o));
    }
}

Si has usado Composición a favor de Herencia, no enfrentarás este problema y tu clase habría sido más robusta, porque ya no dependes del comportamiento de la clase superior. En su lugar, está utilizando el método de superclase para la parte de adición y se beneficiará con cualquier mejora en addAll () como se muestra en el siguiente ejemplo:

clase  pública EncryptedHashSet  implementa Set {
contenedor     privado de HashSet;
    public  boolean  add (Object o) {
        contenedor de devolución . agregar (cifrar (o));
    }
    public  boolean  addAll (Collection c) {
        return conatainer. agregar (cifrar (c));
    }
    .......
}
5. Otra razón para favorecer la composición sobre la herencia es la flexibilidad. Si usa composición, es lo suficientemente flexible como para reemplazar la implementación de la clase compuesta con una versión mejor y mejorada. Un ejemplo es el uso de la clase Comparator, que proporciona una funcionalidad de comparación. Si su objeto Container contiene un Comparador en lugar de extender un Comparador particular para comparar, es más fácil cambiar la forma en que se realiza la comparación estableciendo diferentes tipos de Comparador en instancia compuesta, mientras que en caso de herencia solo puede tener un comportamiento de comparación en tiempo de ejecución. No puedes cambiarlo en tiempo de ejecución. 

Hay muchas más razones para favorecer la composición sobre la herencia, que comenzará a descubrir una vez que comience a usar patrones de diseño. En pocas palabras, favorecer la Composición da como resultado un código más flexible y robusto que el uso de Herencia. Aunque hay algunos casos en los que el uso de herencia tiene mucho sentido, como cuando existe una verdadera relación padre-hijo, pero la mayoría de las veces tiene sentido favorecer la composición sobre la herencia para la reutilización del código. 

Grupo Python: Roberto Vilchez
                        Darwin Gonzalez

19 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Un aspecto fundamental de las interfaces es separar la especificación de una clase (qué hace) de la implementación (cómo lo hace). Usar una u otra implementación puede dar lugar a diferentes rendimientos de un programa.
    Muchos lenguajes de programación modernos no tienen una palabra clave llamada "interfaz". JavaScript no tiene una palabra clave de interfaz; Ruby no tiene una palabra clave de interfaz; Scala no tiene una palabra clave de interfaz; y así sucesivamente... Pero aún es posible programar una interfaz en todos esos idiomas.
    La interfaz de la palabra clave de idioma se puede encontrar en los idiomas escritos como Java o C #. Permite al programador crear un tipo abstracto con múltiples implementaciones concretas.
    Si se desea alcanzar el principio de diseño de la programación en una interfaz, es muy probable que use una construcción de lenguaje llamada interfaz si su lenguaje de programación lo tiene (por ejemplo, Java). Sin embargo, el uso de una palabra clave de interfaz no significa que también esté programando en una interfaz.
    Cuando tenemos clases que deben implementar un comportamiento idéntico, el método que implementa en el comportamiento se puede llevar a una clase abstracta .Si una clase va a tener un mismo método pero con distintas implementaciones, estos métodos se pueden definir en una interfaz. En una interfaz todos los métodos son públicos y no se define nunca la implementación de los métodos. En una clase abstracta, se pueden tanto definir la implemetación de métodos como sólo el método a implementar, dejando la implementación a las clases que hereden de la clase abstracta.

    ReplyDelete
  3. Las interfaces nos ayudan a desencajar módulos entre sí, ya que si tenemos una interfaz que explica el comportamiento que el módulo espera para comunicarse con otros módulos, podremos crear una clase que lo implemente de modo que cumpla las condiciones. El módulo que describe la interfaz no tiene que saber nada sobre nuestro código y, sin embargo, nosotros podemos trabajar con él sin problemas. Un uso común de las interfaces es para separar una funcionalidad de su aplicación ya que una interfaz define una funcionalidad, que debería ser notable. Cada clase que implementa esta interfaz define las posibles ejecuciones. Teniendo siempre en mente que los métodos abstractos definen los mensajes que se pueden pasar a un objeto. Se puede usar las clases abstractas para reutilizar el código a través de la herencia y también para enviar mensajes que deben implementarse.

    ReplyDelete
  4. Además de lo ya mencionado se debe tener en cuenta que para los desarrolladores les será mucho más ventajoso escribir código que sea reutilizable en vez de invertir mas tiempo haciendo mantenimiento al código anterior, programar en una interfaz hace que escribir códigos que sean reutilizables resulte mucho más fácil. Dicho eso como su nombre lo indica programar una interfaz, no una implementación; la interfaz viene siendo la especificación de cómo se usa algo, por ejemplo un contrato, entonces si se hace a un lado eso se tienen la implementación que seria como se cumple ese contrato. No obstante es oportuno llegar a pensar que cual es el objeto de la herencia el las interfaces, pero su uso aquí se debe entender como un medio que permite clasificar comportamientos o rasgos que han sido mostrados por muchas clases no relacionadas de objetos, también podría decirse que el uso de la herencia en la programación de interfaces es como una forma de construcción del lenguaje. Para culminar las interfaces hoy en día también son aplicadas en los patrones de diseño y en las metodologías de desarrollo.

    ReplyDelete
  5. Programar una interface en lugar que una implementación logra que nuestra aplicación sea más fácil de extender y nuestro código funcione con todas las implementaciones de esa interface, hasta con las que no han sido creadas. Una de las grandes ventajas que tienen las interfaces es que definen el prototipo de ciertos métodos, pero no se implementan, sino que sólo se especifican, como un contrato. Posteriormente, las clases que quieran hacer uso de ese interfaz, tendrán que implementarlo exactamente como está definido en el "contrato".
    Es como programar interfaces en lugar de clases, por ejemplo en java haces que el código sea dependiente entre sí, pues para que corra una clase es necesario de otras más, lo que vuelve el código difícil de sostener. En cambio, sí se programan interfaces lo que se hace es separar la especificación de una clase (qué hace) de la implementación (cómo lo hace). Esto se ha comprobado que da lugar a programas más robustos y con menos errores.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. De acuerdo con lo que se ha mencionado anteriormente, considero que la frase *programar una interfaz, no una implementación*, como lo indica su nombre intenta demostrarnos de cierta forma, que en algunos casos resulta más cómodo programar una interfaz, ya que ésta permite que se realicen modificaciones sin afectar el código, establece conexiones entre los módulos, creando cierta flexibilidad sin que se haya creado una dependencia entre los mismos. Programar una interfaz en vez de una implementación te proporciona una aplicación más flexible, permitiendo al código funcionar con las implementaciones de todos sus miembros. Mientras que la implementación que está ligada fuertemente a la herencia (generando extensiones que heredan los métodos o funciones de las clases padre). Cabe destacar que éstas a diferencia de las interfaces se encargan de mostrar los detalles de lo que hace cada método.

    ReplyDelete
  8. A cualquiera persona luego de leer esta información posteada se puede preguntar ¿Qué interés tiene implementar una interface del API si no nos proporciona código ninguno? Lo cierto es que una interface puede verse en relación a la programación como una norma urbanística en una ciudad por ejemplo. Si lees la documentación de la interfaz, aunque no proporciona código, sí proporciona instrucciones respecto a características comunes para las clases que la implementen y define qué métodos han de incluirse para cumplir con la interfaz y para qué servirán esos métodos. Si implementamos la interface, lo que hacemos es ajustarnos a la norma. Y si todos los programadores se ajustan a la misma norma, cuando un programador tiene que continuar un programa iniciado por otro no tiene que preguntarse: ¿qué método podré usar para comparar varios objetos de este tipo y ponerlos en orden? Comúnmente por ejemplo los programadores se ciñen a lo establecido por el API de Java: para comparar varios objetos y ponerlos en orden (“orden natural”) se implementa la interfaz Comparable y su método compareTo(). Y además con esto se puede saber qué tipo ha de devolver ese método y cómo ha de funcionar, porque así lo indica la documentación de la interface.
    Se podría decir que si una interfaz define un tipo (al igual que una clase define un tipo) pero ese tipo no provee de ningún método podemos preguntarnos: ¿qué se gana con las interfaces? La implementación (herencia) de una interfaz no podemos decir que evite la duplicidad de código o que favorezca la reutilización de código puesto que realmente no provee código. En cambio sí podemos decir que reúne las otras dos ventajas de la herencia: favorecer el mantenimiento y la extensión de las aplicaciones. ¿Por qué? Porque al definir interfaces permitimos la existencia de variables polimórficas y la invocación polimórfica de métodos.

    ReplyDelete
  9. Como ya lo han dado entender ustedes por medio de este blog programar una interface en lugar que una implementación, por medio de esta se logra que la aplicación que estemos desarrollando sea más fácil de extender y además de esto que nuestro código funcione con todas las implementaciones de esa interface. Como hemos ido aprendiendo durante el tiempo que se lleva programando sabemos que interfaces proporcionan un mecanismo para abstraer los métodos a un nivel superior, lo que permite simular la herencia múltiple de otros lenguajes, ya que hay lenguajes que no soportan la herencia múltiple como es el caso de JAVA, que una forma de lograr esto es por medio de la interfaces. Mediante el ejemplo que dieron a conocer hubo una parte que dio cierta curiosidad, un pequeño fragmento “No importa cómo Eric o Jane implementen el método makeCoffee, siempre y cuando lo implementen” el cual me dio a entender que no importa cómo se hagan las cosas, lo importante es que se realicen y lleven al mismo resultado, en otras palabras, si se programa las interfaces lo que hace es establecer contratos entre los módulos por así decirlo, no nos interesa el cómo se hacen las cosas, sino nada más nos interesa saber el qué de las cosas. Desde mi punto de vista es mejor programar Interfaces porque en cualquier momento podemos cambiar la implementación sin afectar nuestro código.

    ReplyDelete
  10. La pregunta que la mayoría al empezar a leer de este tema se podría hacer es que "Si una interfaz define un tipo (al igual que una clase define un tipo) pero ese tipo no provee de ningún método podemos preguntarnos: ¿qué se gana con las interfaces en Java?
    La implementación (herencia) de una interfaz no podemos decir que evite la duplicidad de código o que favorezca la reutilización de código puesto que realmente no provee código.
    En cambio sí podemos decir que reúne las otras dos ventajas de la herencia: favorecer el mantenimiento y la extensión de las aplicaciones. ¿Por qué? Porque al definir interfaces permitimos la existencia de variables polimórficas y la invocación polimórfica de métodos. Por ejemplo los arboles como, vehículos y personas son de tipo Actor, de modo que podemos generar código que haga un tratamiento en común de todo lo que son actores. En otras palabras, podemos necesitar una lista de Actores.
    Podemos declarar una variable como de tipo Actor (aunque no puedan existir instancias de Actor) que permita referenciar alternativamente a objetos de las distintas subclases de la interfaz.
    Un aspecto fundamental de las interfaces en Java es hacer lo que ya hemos dicho que hace una interfaz de forma genérica: separar la especificación de una clase (qué hace) de la implementación (cómo lo hace).
    Esto se ha comprobado que da lugar a programas más robustos y con menos errores

    ReplyDelete
  11. Las interfaces se crean en situaciones en las que sabemos que debe hacer alguna tarea, pero como debe hacerse puede variar. En otras palabras, podemos decir que implementamos interfaces para que nuestra clase comience a comportarse de manera particular. Por otro lado podemos decir que Hay dos formas de reutilizar el código, mediante la composición y mediante la herencia. La composición significa utilizar objetos dentro de otros objetos. Por ejemplo, un applet es un objeto que contiene en su interior otros objetos como botones, etiquetas, etc. Cada uno de los controles está descrito por una clase. Y no me queda mucho más que añadir una conclusión que no deja de ser una opinión personal La herencia, no es mala de hecho puede ser la solución que necesitas en un momento concreto.
    El problema es que es muy malo es aplicar mal herencia. Y como en la mayor parte de los sistemas que desarrollamos por su naturaleza de cambio, es muy fácil caer en aplicarla mal. Es muy probable que se vuelva contra ti y los beneficios no sean nada comparados con sus problemas a futuro.

    ReplyDelete
  12. En lenguajes que no soportan herencia múltiple, puede resultar ventajoso la composición.
    La composición genera un diseño más desacoplado, que puede ayudar a hacer el testing (o TDD) mas fácil. Se podría decir que conviene favorecer la composición por sobre la herencia, sin embargo esta respuesta no es definitiva, cada uno tiene que identificar la necesidades y el dominio en el cual está trabajando para identificar en casos conviene usar cada metodología.

    ReplyDelete
  13. El diseño de interfaces se hace bastante vistoso cuando nos permite modular mas nuestra aplicacion o desarrollo, haciendo interfaces se separa lo que es el codigo del diseño de la aplicacion, permitiendo asi, tener un programa mas entendible(modularidad) y mas facil de mantener(manejo de errores), simplemente cuando se tiene un error, se busca el modulo correspondiente a ese error y se soluciona o cuando se quiera modificar un segmento del programa se trabaja en el sin afectar a los demas, no es lo mismo tener varios pedazos de codigo que tener uno solo muy grande que sea muy dificil de manipular.
    Agregando a lo que comento pedro, teniendo una clase padre(implementacion) o diseño, se puede heredar y obtener funciones cuando se necesiten, pero opino que la herencia no es tan dificil de manejar ya que si se tiene un diseño de clases, se podria entender facilmente que es lo que se tiene que heredar o hacer en nuestro desarrollo, es por ello que se debe diseñar antes de desarrollar para que no ocurran estos problemas de no saber a donde vamos dirigidos en la aplicación que estemos realizando, modelar el sistema otorga una vista bastante jugosa y hace que no nos perdamos en lo que debemos hacer, como lo debemos hacer y cuando lo debemos hacer.
    Apoyo la metodología de diseñar primero todo el sistema y aplicar las buenas practicas de programación para que nuestros desarrollos sean bastante robustos, seamos reconocidos por como lo hacemos.

    ReplyDelete
  14. La composición es bastante fácil de entender y podemos ver la composición en la vida cotidiana, una mesa tiene patas, una pared está compuesta de bloques, etc., es decir, hacer un total de las partes. La composición tiene ventaja al momento de descomponer un problema complejo en soluciones por módulo, debido a que se maneja en términos de partes y componente, algo que resulta un poco más complicado con la herencia por la abstracción que esta presenta que va más allá de simplemente “reutilizar código” y que se vuelve problemático cuando pasa de ser simple a múltiple. Con esto no quiero decir que la herencia no debería utilizarse ya que en el desarrollo de software hay compensaciones y la preferencia por la composición no es una cuestión de si es mejor o no, sino de lo que es más apropiado para las necesidades que se tengan en un contexto específico.

    ReplyDelete
  15. La interfaz de principio de diseño se refiere a perder acoplamiento entre módulos o sistemas. Cada vez que construyas una pieza más grande de software, tendrás dependencias con otros sistemas. La interfaz de la palabra clave de idioma se puede encontrar en los idiomas escritos como Java o C #. Permite al programador crear un tipo abstracto con múltiples implementaciones concretas., le permite el acoplamiento correcto entre las clases del sistema que esta desarrollando. Uno de los objetivos que buscamos cuando programamos es reutilizar métodos y funcionalidades, para lograr una mayor mantenibilidad. Dentro de los lenguajes convencionales orientados a objetos existen varias formas de hacer esto, las dos más conocidas son: herencia de clases y composición. Podría decir que conviene favorecer la composición por sobre la herencia, sin embargo esta respuesta no es definitiva, cada uno tiene que identificar la necesidades y el dominio en el cual está trabajando para identificar en casos conviene usar cada metodología.

    ReplyDelete
  16. Las interfaz en términos sencillos sería entonces la distinción de cómo se emplea algo. Estas en la actualidad son bastante usadas tanto en las metodologías de desarrollo como en los patrones de diseño. Al buscar un propósito esencial con respecto a la programación de una interfaz es importante decir que este hace referencia a promover el ensamblaje flexible entre las distintas clases, con esto se tiene como resultado la reducción de lo que se conoce dependencia de una clase hacia otra. Entonces hace referencia a que se torna más cómoda la codificación en dicha clase, lo que no permite que se vean perturbadas las demás, obteniendo una gran flexibilidad con respecto al sistema. Por otro lado con respecto a la vistosidad del diseño de interfaz esta se hace considerablemente atractiva cuando nos deja modular nuestro desarrollo; aplicando interfaces se aísla lo que es el código con el diseño de dicho desarrollo lo cual nos genera lo que denominamos modularidad que no es más que el tener un programa más entendible y facil de mantener en cuanto a los errores se refiere.
    No se puede dejar de mencionar la ventaja de tener un código reutilizable para los desarrolladores lo cual les permite administrar y optimizar su tiempo lo que es genial.

    ReplyDelete
  17. Como muchos comentan, la frase “program to an interface, not an implementation” es muy favorable porque al escribir código que sea reutilizable en vez de invertir mas tiempo haciendo mantenimiento es un beneficio grande. Aun asi, El término "programación en una interfaz" está abierto a mucha interpretación. La interfaz en el desarrollo de software es una palabra muy común. Hay desarrolladores que plantean que la Programación a una interfaz significa cuando sea posible, uno debe referirse a un nivel más abstracto de una clase (una interfaz, clase abstracta, o a veces una clase de superclase), en lugar de referirse a una implementación concreta. Sin embargo, esto no es correcto, bueno, o al menos, no es del todo correcto. El punto más importante proviene de una perspectiva de diseño de programa. Aquí, "programar en una interfaz" significa enfocar su diseño en lo que está haciendo el código, no cómo lo hace. Esta es una distinción vital que impulsa su diseño hacia la corrección y la flexibilidad. Ejemplo, supongamos que tengo una clase (clase A) que usa la funcionalidad de la clase abstracta B. Las clases C y D heredan la clase B: proporcionan una implementación concreta para lo que clase se dice que hacer. Si la clase A usa directamente la clase C o D, se llama 'programación para una implementación', que no es una solución muy flexible, y es lo que trata el tema de este blog. Pero si la clase A usa una referencia a la clase B, que luego puede configurarse para la implementación C o D, hace que las cosas sean más flexibles y mantenibles.

    ReplyDelete
  18. Cuando somos nuevos programadores siempre nuestro enfoque esta basado hacia ciertas partes del programa que son dificiles de desarrollar por su implementacion, puesto que nos enfocamos solamente en lo que es el fin del programa mas no en su funcionamiento practico. El uso de interfaces permita que el desarrollo modular sea mas efectivo, en cuanto a que al usar interfaces que no agregan codigo a nuestro programa pero que si le da funcionalidad. Tambien se denota la importancia en el manejo de las clases, esta es una parte que pudiera convertirse en algo engorroso cuando no sabemos hacerlo, y es lo que comunmente pasa cuando somos inexpertos. El manejo de errores resulta mas efectivo al utilizar este tipo de enfoque, puesto que los programas se hacen mas entendibles y manejables en ese aspecto. Aunado a todo lo anterior se destaca que el tiempo que se ahorra con estos modelos de desarrollo permite que haya un espacio de tiempo ideal para enfoque hacia el diseño de vistas, haciendo del producto final mas atractivo a los usuarios finales.

    ReplyDelete
  19. Una interface es similar a una clase o a una estructura, pero sus miembros son abstractos, es decir, no están definidos, no tienen código. Declara modos de comportamiento, pero deja su definición para las clases que la implementen.
    Cuando una clase implementa una interface, debe implementar todos los métodos de la interface. Hay una gran diferencia entre heredar de una clase abstracta e implementar una interface. Una interface puede heredar cero o más interfaces esto es herencia múltiple de interfaces. A tales interfaces se les llama “interfaces base explícitas”. Además de que una interface no sólo hereda las interface que explícitamente indica, sino también aquéllas heredadas implícitamente, es decir, aquéllas que han heredado las interface de las que hereda.

    ReplyDelete