Durante mi primera implementación que extendió el marco de la colección Java, me sorprendió ver que la interfaz de la colección contiene métodos declarados como opcionales. Se espera que el implementador arroje UnsupportedOperationExceptions si no es compatible. Esto me pareció inmediatamente una mala elección de diseño de API.
Después de leer gran parte del excelente libro "Eficaz Java" de Joshua Bloch, y luego de enterarse de que él podría ser responsable de estas decisiones, no parecía coincidir con los principios expuestos en el libro. Creo que declarar dos interfaces: Collection y MutableCollection, que amplía Collection con los métodos "opcionales", habría llevado a un código de cliente mucho más fácil de mantener.
Hay un excelente resumen de los problemas aquí .
¿Hubo una buena razón por la cual se eligieron métodos opcionales en lugar de la implementación de dos interfaces?
fuente
Respuestas:
Las preguntas frecuentes proporcionan la respuesta. En resumen, vieron una potencial explosión combinatoria de interfaces necesarias con vista modificable, no modificable, solo eliminación, solo adición, longitud fija, inmutable (para subprocesos), y así sucesivamente para cada conjunto posible de métodos de opciones implementados.
fuente
const
palabra clave como C ++.vector<int>, const vector<int>, vector<const int>, const vector<const int>
. Hasta aquí todo bien, pero luego intenta implementar gráficos, y desea hacer la estructura gráfica constante, pero los atributos del nodo modificable, etc.can
método que probaría si una operación es posible? Mantendría la interfaz simple y rápida.Me parece que
Interface Segregation Principle
no estaba tan bien explorado en aquel entonces como lo está ahora; esa forma de hacer las cosas (es decir, su interfaz incluye todas las operaciones posibles y tiene métodos "degenerados" que arrojan excepciones para los que no necesita) era popular antes de que SOLID e ISP se convirtieran en el estándar de facto para el código de calidad.fuente
Count
una colección sin tener que preocuparse por los tipos de elementos que contiene), pero en marcos basados en borrado de tipos como Java, ese no es un problema.Si bien algunas personas pueden detestar los "métodos opcionales", en muchos casos pueden ofrecer una semántica mejor que las interfaces altamente segregadas. Entre otras cosas, permiten las posibilidades de que un objeto gane habilidades o características durante su vida útil, o que un objeto (especialmente un objeto envolvente) no sepa cuándo se construye qué habilidades exactas debería reportar.
Si bien difícilmente llamaré a las clases de colecciones de Java modelos de buen diseño, sugeriría que un buen marco de colecciones debería incluir en su base una gran cantidad de métodos opcionales junto con formas de preguntarle a una colección sobre sus características y habilidades . Tal diseño permitirá que se use una sola clase de envoltura con una gran variedad de colecciones sin oscurecer accidentalmente las habilidades que la colección subyacente podría poseer. Si los métodos no fueran opcionales, sería necesario tener una clase de contenedor diferente para cada combinación de características que las colecciones podrían admitir, o de lo contrario, algunos contenedores no podrán utilizarse en algunas situaciones.
Por ejemplo, si una colección admite escribir un elemento por índice o agregar elementos al final, pero no admite la inserción de elementos en el medio, entonces el código que desea encapsularlo en un contenedor que registraría todas las acciones realizadas en él necesitaría una versión del contenedor de registro que proporcionó la combinación exacta de habilidades compatibles, o si no hubiera ninguno disponible, tendría que usar un contenedor que admitiera agregar o escribir por índice, pero no ambos. Sin embargo, si una interfaz de colección unificada proporciona los tres métodos como "opcionales", pero luego incluye métodos para indicar cuál de los métodos opcionales sería utilizable, entonces una sola clase de contenedor podría manejar colecciones que implementan cualquier combinación de características. Cuando se le preguntó qué características admite, un contenedor podría simplemente informar lo que sea compatible con la colección encapsulada.
Tenga en cuenta que la existencia de "habilidades opcionales" puede permitir en algunos casos que las colecciones agregadas implementen ciertas funciones de manera mucho más eficiente de lo que sería posible si las habilidades se definieran por la existencia de implementaciones. Por ejemplo, suponga
concatenate
que se usó un método para formar una colección compuesta de otras dos, la primera de las cuales resultó ser una ArrayList con 1,000,000 de elementos y la última de las cuales fue una colección de veinte elementos que solo se pudo repetir desde el principio. Si a la colección compuesta se le pidiera el elemento 1,000,013 (índice 1,000,012), podría preguntarle a la ArrayList cuántos elementos contenía (es decir, 1,000,000), restar eso del índice solicitado (arrojando 12), leer y omitir doce elementos del segundo colección, y luego devuelve el siguiente elemento.En tal situación, a pesar de que la colección compuesta no tendría una forma instantánea de devolver un artículo por índice, pedirle a la colección compuesta el 1,000,00013 ítem aún sería mucho más rápido que leer 1,000,013 artículos de forma individual e ignorar todos menos el último uno.
fuente
AsXXX
método en la interfaz base que devolverá el objeto sobre el que se invoca si implementa esa interfaz, devolverá un objeto contenedor que admita esa interfaz si es posible, o arroje una excepción si no. Por ejemplo, unaImmutableCollection
interfaz puede requerir por contrato ...Lo atribuiría a los desarrolladores originales simplemente porque no sabían mejor en ese entonces. Hemos recorrido un largo camino en el diseño OO desde 1998 más o menos cuando se lanzaron Java 2 y Colecciones por primera vez. Lo que parece un mal diseño obvio ahora no era tan obvio en los primeros días de OOP.
Pero puede haberse hecho para evitar el lanzamiento adicional. Si se tratara de una segunda interfaz, tendría que emitir sus instancias de colecciones para llamar a esos métodos opcionales, que también es algo feo. Tal como está ahora, detectará una UnsupportedOperationException de inmediato y corregirá su código. Pero si hubiera dos interfaces, tendría que usar instanceof y emitir por todas partes. Quizás lo consideraron una compensación válida. También en los primeros días de Java 2, instancia de era muy mal visto debido a su lento rendimiento, podrían haber estado tratando de evitar el uso excesivo de la misma.
Por supuesto, todo esto es una especulación salvaje, dudo que podamos responder esto con seguridad a menos que uno de los arquitectos de las colecciones originales intervenga.
fuente
Collection
y no unMutableCollection
, es una clara señal de que no debe modificarse. No sé por qué alguien necesitaría lanzarlos. Tener interfaces separadas significa que obtendrá ese tipo de errores en tiempo de compilación en lugar de obtener una excepción en tiempo de ejecución. Cuanto antes obtenga el error, mejor.const
objeto e instantáneamente le diría al usuario que el objeto no puede modificarse.