Pregunta

cuando se programa en Java que prácticamente siempre, simplemente por costumbre, escribir algo como esto:

public List<String> foo() {
    return new ArrayList<String>();
}

La mayoría de las veces sin siquiera pensar en ello. Ahora, la pregunta es: ¿debo siempre especifica la interfaz como el tipo de cambio? O es aconsejable el uso de la aplicación real de la interfaz, y si es así, en qué circunstancias?

Es evidente que el uso de la interfaz tiene un montón de ventajas (por eso que está ahí). En la mayoría de los casos que en realidad no importa qué aplicación concreta es utilizado por una función de biblioteca. Pero tal vez hay casos en los que sí importa. Por ejemplo, si sé que voy a acceder en primer lugar los datos en la lista de forma aleatoria, un LinkedList sería malo. Pero si mi función de biblioteca sólo devuelve la interfaz, simplemente no lo sé. Para estar en el lado seguro que incluso podría necesito copiar la lista de forma explícita a una ArrayList:

List bar = foo();
List myList = bar instanceof LinkedList ? new ArrayList(bar) : bar;

, pero que sólo parece horrible y mis compañeros de trabajo lo haría probablemente me Lynch en la cafetería. Y con razón.

¿Qué piensan ustedes? ¿Cuáles son sus directrices, ¿cuándo se tiende hacia la solución abstracta, y cuándo se revelan detalles de su implementación para posibles mejoras de rendimiento?

¿Fue útil?

Solución

  

Por ejemplo, si sé que lo haré   acceder en primer lugar los datos de la lista   al azar, un LinkedList sería malo.   Pero si mi función de biblioteca sólo   devuelve la interfaz, simplemente no hago   saber. Para estar en el lado seguro yo podría   incluso necesidad de copiar la lista de forma explícita   a un ArrayList.

Como todo el mundo ha mencionado, sólo que no debe preocuparse por la forma en que la biblioteca ha implementado la funcionalidad, a reducir el acoplamiento y el aumento de capacidad de mantenimiento de la biblioteca.

Si usted, como cliente de biblioteca, se puede demostrar que la aplicación está funcionando mal para su caso de uso, a continuación, puede ponerse en contacto con la persona a cargo y discutir sobre el mejor camino a seguir (un nuevo método para este caso o simplemente cambiando la puesta en práctica).

Dicho esto, tu ejemplo huele a la optimización prematura.

Si el método es o puede ser crítico, se podría hablar de los detalles de implementación en la documentación.

Otros consejos

Vuelta la interfaz apropiada para ocultar los detalles de implementación. Sus clientes sólo deben preocuparse por lo que ofrece su objeto, no la forma en que lo implementaron. Si usted comienza con un ArrayList privada, y decidir más tarde que otra cosa (por ejemplo, LinkedLisk, lista de saltar, etc.) es más apropiado puede cambiar la aplicación sin afectar a los clientes si devuelve el interfaz. En el momento en que regrese un tipo concreto se pierde la oportunidad.

En la programación OO, queremos encapsular tanto como sea posible los datos. Ocultar lo más posible la implementación real, la abstracción de los tipos lo más alto posible.

En este contexto, me gustaría responder a sólo devuelven lo que es significativo . Hace que tiene sentido en absoluto para el valor de retorno sea la clase concreta? Aka en su ejemplo, preguntarse: ¿alguien usar un método de ListaEnlazada específica sobre el valor de retorno de foo

  • Si no, sólo tiene que utilizar la interfaz de nivel superior. Es mucho más flexible y le permite cambiar el back-end
  • En caso afirmativo, se pregunta: ¿no puedo refactorizar mi código para devolver la interfaz de nivel superior? :)

El más abstracto es su código, los menos cambios de su están obligados a hacerlo cuando se cambia un backend. Es tan simple como eso.

Si, por el contrario, se termina echando los valores de retorno a la clase concreta, así que es una fuerte señal de que probablemente debería regresar lugar la clase concreta. Sus usuarios / compañeros de equipo no deben tener que saber sobre los contratos más o menos implícitas:. Si es necesario utilizar los métodos concretos, simplemente devuelve la clase concreta, para mayor claridad

En pocas palabras: Código Extracto , pero explícitamente )

Sin ser capaz de justificar con resmas de cotizaciones CS (soy autodidacta), lo que siempre hemos ido por el mantra de "Aceptar la menor derivada, devolver la mayoría de los derivados", en el diseño de clases y que ha resistido yo bien en los últimos años.

supongo que eso significa en términos de interfaz de frente retorno concreto es que si usted está tratando de reducir las dependencias y / o desacoplar, volviendo la interfaz es generalmente más útil. Sin embargo, si las implementa la clase de hormigón más que la interfaz, por lo general es más útil para las personas que llaman de su método para obtener la clase concreta de atrás (es decir, la "mayoría de los derivados") en lugar de aribtrarily los restringen a un subconjunto de la funcionalidad del objeto devuelto que - a menos que realmente necesidad para limitarlos. Por otra parte, usted podría también acaba de aumentar la cobertura de la interfaz. restricciones innecesarias como este comparo con el sellado desconsiderada de clases; nunca sabes. Sólo para hablar un poco sobre la primera parte de ese mantra (para otros lectores), aceptando la menor deriva también ofrece la máxima flexibilidad para las llamadas de su método.

-Oisin

En general, para una interfaz orientada al público, tales como API, volviendo la interfaz (como List) sobre la aplicación concreta (como ArrayList) sería mejor.

El uso de un ArrayList o LinkedList es un detalle de implementación de la biblioteca que se debe considerar para el caso de uso más común de esa biblioteca. Y, por supuesto, a nivel interno, teniendo métodos private dar el relevo LinkedLists no sería necesariamente una mala cosa, si se dispone de instalaciones que harían que el procesamiento sea más fácil.

No hay ninguna razón por la que una clase concreta no debe ser utilizado en la ejecución, a menos que haya una buena razón para creer que alguna otra clase List sería utilizado más adelante. Pero, de nuevo, el cambio de los detalles de implementación no debe ser tan doloroso, siempre y cuando la parte del lado público está bien diseñado.

La biblioteca en sí debe ser una caja de negro a sus consumidores, por lo que en realidad no tienen que preocuparse por lo que está pasando internamente. Eso también significa que la biblioteca debe estar diseñado de manera que está diseñado para ser utilizado en la forma en que está destinado.

Por regla general, sólo pasar de nuevo implementaciones internas si estoy en algunas labores privadas, internas de una biblioteca, e incluso por lo que sólo con moderación. Por todo lo que es público y es probable que se llamará desde el exterior de mi módulo de interfaces de uso, y también el patrón de fábrica.

El uso de las interfaces de una manera tal, ha demostrado ser una forma muy fiable para escribir código reutilizable.

La principal cuestión que ya ha sido contestada y siempre se debe utilizar la interfaz. Sin embargo, me gustaría simplemente a comentar

  

Es evidente que el uso de la interfaz tiene un montón de ventajas (por eso que está ahí). En la mayoría de los casos que en realidad no importa qué aplicación concreta es utilizado por una función de biblioteca. Pero tal vez hay casos en los que sí importa. Por ejemplo, si sé que voy a acceder en primer lugar los datos en la lista de forma aleatoria, un LinkedList sería malo. Pero si mi función de biblioteca sólo devuelve la interfaz, simplemente no lo sé. Para estar en el lado seguro, incluso puede ser que necesite para copiar la lista de forma explícita a un ArrayList.

Si va a devolver una estructura de datos que usted conoce tiene un rendimiento deficiente de acceso aleatorio - O (n) y por lo general una gran cantidad de datos - hay otras interfaces que debe especificando lugar de la lista, al igual que Iterable para que cualquier persona usando la biblioteca estará plenamente conscientes de que sólo el acceso secuencial está disponible.

Elegir el tipo correcto de retorno no se trata sólo de la interfaz frente implementación concreta, también se trata de seleccionar la interfaz correcta.

No importa tanto si un método de API devuelve una interfaz o una clase concreta; a pesar de lo que dice todo el mundo aquí, que casi nunca cambia la clase de implementiation una vez que el código está escrito.

Lo que es mucho más importante: siempre usar las interfaces de alcance mínimo para el método parámetros ! De esta manera, los clientes tienen la libertad máxima y pueden utilizar el código de clases ni siquiera conocen.

Cuando un método API devuelve ArrayList, no tengo absolutamente ningún reparo con eso, sino que cuando se exige una ArrayList (o, todo lo que común, Vector) parámetro, considero que la caza del programador y hacerle daño, porque significa que me no se puede utilizar Arrays.asList(), Collections.singletonList() o Collections.EMPTY_LIST.

Lo siento no estar de acuerdo, pero creo que la regla básica es la siguiente:

  • En entrada argumentos utilizan el más genérico .
  • En salida valores, el más específico .

Por lo tanto, en este caso desea declarar la aplicación como:

public ArrayList<String> foo() {
  return new ArrayList<String>();
}

Justificación : El caso de entrada ya es conocido y explicado por todo el mundo: utilizar la interfaz, y punto. Sin embargo, el caso de salida puede parecer contrario a la intuición. Desea volver a la aplicación, ya que desea que el cliente tenga la mayor información sobre lo que está recibiendo. En este caso, más conocimiento es más potencia .

Ejemplo 1: el cliente quiere obtener el quinto elemento:

  • Colección de retorno: se debe repetir hasta el 5 elemento vs Lista de retorno:
  • Lista de retorno: list.get(4)

Ejemplo 2: el cliente quiere quitar el quinto elemento:

  • Lista de retorno:. Debe crear una nueva lista sin el elemento especificado (list.remove() es opcional)
  • retorno ArrayList: arrayList.remove(4)

Así que es una gran verdad que el uso de las interfaces es grande, ya que promueve la reutilización, reduce el acoplamiento, mejora la capacidad de mantenimiento y hace feliz a la gente ... pero sólo cuando se usa como entrada .

Así que, de nuevo, la regla puede establecerse como:

  • Sea flexible de lo que usted ofrece.
  • Sea informativo con lo que usted entrega.

Así que, la próxima vez, por favor, devuelva la aplicación.

Se utiliza la interfaz de abstraer lejos de la implementación real. La interfaz es básicamente un modelo para lo que su aplicación puede hacer.

Las interfaces son un buen diseño, ya que le permiten cambiar los detalles de implementación sin tener que temer que cualquiera de sus consumidores se ven afectados directamente, siempre y cuando la aplicación todavía lo hace lo que su interfaz dice que lo hace.

Para trabajar con interfaces que le instanciarlas como esto:

IParser parser = new Parser();

Ahora IParser sería su interfaz, y Parser sería su aplicación. Ahora bien, cuando se trabaja con el objeto analizador desde arriba, tendrá que trabajar en contra de la interfaz (IParser), que a su vez va a trabajar en contra de su aplicación (Analizador).

Esto significa que puede cambiar el funcionamiento interno del analizador como todo lo que quiere, nunca afectará código que funciona en contra de su interfaz de programa de análisis IParser.

En el uso general de la interfaz en todos los casos si no tienen necesidad de la funcionalidad de la clase concreta. Tenga en cuenta que para las listas, Java ha añadido una acceso aleatorio clase marcador principalmente para distinguir un caso común donde un algoritmo puede necesitar saber si get (i) es la constante de tiempo o no.

Para usos de código, Michael anterior es correcto que ser tan genérico como sea posible en los parámetros del método a menudo es aún más importante. Esto es especialmente cierto cuando se prueba un método de este tipo.

Encontrará (o haya encontrado) que al volver las interfaces, sino que está presente a través de su código. p.ej. regresa una interfaz desde el método A y Tienes a continuación, pasar a una interfaz para el método B.

Lo que está haciendo es la programación por contrato, aunque de forma limitada.

Esto le da enormes posibilidades para cambiar las implementaciones bajo las sábanas (siempre que estos nuevos objetos cumplen con los contratos existentes / comportamientos esperados).

Teniendo en cuenta todo esto, tiene ventajas en cuanto a la elección de su aplicación, y cómo se puede sustituir comportamientos (incluyendo pruebas - con ayuda de burla, por ejemplo). En caso de que no hubiera adivinado, estoy totalmente a favor de esto y tratar de reducir a (o introducir) Interfaces siempre que sea posible.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top