En la mayoría de los códigos Java, veo que la gente declara objetos Java como este:
Map<String, String> hashMap = new HashMap<>();
List<String> list = new ArrayList<>();
en lugar de:
HashMap<String, String> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
¿Por qué existe una preferencia para definir el objeto Java utilizando la interfaz en lugar de la implementación que realmente se va a utilizar?
Respuestas:
La razón es que la implementación de estas interfaces generalmente no es relevante al manejarlas, por lo tanto, si obliga a la persona que llama a pasar
HashMap
un método, entonces esencialmente está obligando a qué implementación usar. Por lo tanto, como regla general, se supone que debe manejar su interfaz en lugar de la implementación real y evitar el dolor y el sufrimiento que podrían tener que cambiar todas las firmas de métodosHashMap
cuando decida que necesita usarlasLinkedHashMap
.Debe decirse que hay excepciones a esto cuando la implementación es relevante. Si necesita un mapa cuando el orden es importante, entonces se puede requerir una
TreeMap
o unaLinkedHashMap
que se pasa, o mejor aún,SortedMap
que no especifica una implementación específica. Esto obliga a la persona que llama a pasar necesariamente un cierto tipo de implementación de Map y sugiere que ese orden es importante. Dicho esto, ¿podrías anularSortedMap
y pasar uno sin clasificar? Sí, por supuesto, sin embargo, espera que sucedan cosas malas como resultado.Sin embargo, la mejor práctica aún dicta que si no es importante, no debe usar implementaciones específicas. Esto es cierto en general. Si está lidiando
Dog
yCat
cuáles se derivanAnimal
, para hacer un mejor uso de la herencia, generalmente debe evitar tener métodos específicos paraDog
oCat
. En su lugar, todos los métodos debenDog
oCat
deben anular los métodosAnimal
y esto le ahorrará problemas a largo plazo.fuente
SortedMap
, noTreeMap
.SortedMap
es una de varias implementaciones que se ocupan de ordenar. Eso está fuera del punto.TreeMap
también ordena artículos de acuerdo con la implementación de la claveComparable
o con unaComparator
interfaz dada .LinkedHashMap
no se implementaSortedMap
. Las únicas subclases deSortedMap
sonConcurrentSkipListMap
yTreeMap
.En palabras de Layman:
La misma razón por la que los fabricantes de electrodomésticos construyeron sus productos con enchufes eléctricos en lugar de simplemente cables pelados, y las casas vienen con enchufes de pared en lugar de cables desprendibles que sobresalen de la pared.
Al utilizar enchufes estándar en su lugar, permiten enchufar los mismos aparatos en cualquier enchufe compatible de la casa.
Desde el punto de vista de la toma de corriente, no importa si conecta un televisor o un estéreo.
Eso hace que tanto el dispositivo como el enchufe sean más útiles.
Tomemos, por ejemplo, un método que acepte un Mapa como argumento.
El método funcionará independientemente de que le pase un HashMap o un LinkedHashMap, siempre que sea una subclase de Map.
Ese es el principio de sustitución de Liskov .
En el código de muestra que proporcionó, significa que más tarde, por alguna razón, puede cambiar la implementación concreta de Hash y no necesitará cambiar el resto del código.
El problema con el software es que, dado que es relativamente fácil cambiar las cosas más tarde sin desperdicio de ladrillos o mortero, la gente asume que ese tipo de previsión no vale la pena. Pero la realidad nos ha demostrado que el mantenimiento del software es muy costoso.
fuente
Es seguir el principio de segregación de interfaz (la 'I' en SÓLIDO ). Evita que el código que usa esos objetos dependa de los métodos de esos objetos que no necesita, lo que hace que el código esté menos acoplado y, por lo tanto, sea más fácil de cambiar.
Por ejemplo, si luego descubre que realmente necesita un
LinkedHashMap
, puede hacer ese cambio de manera segura sin afectar ningún otro código.Sin embargo, hay una compensación, porque está limitando artificialmente el código que puede tomar su objeto como parámetro. Digamos que hay una función en algún lugar que requiere un
HashMap
por alguna razón. Si devuelve aMap
, no puede pasar su objeto a esa función. Debe equilibrar la probabilidad de que en el futuro necesite la funcionalidad adicional que se encuentra en la clase más concreta con el deseo de limitar el acoplamiento y mantener su interfaz pública lo más pequeña posible.fuente
Tener la variable restringida a una interfaz garantiza que ninguno de los usos de esa variable utilizará
HashMap
una funcionalidad específica que puede no existir en la interfaz, por lo que la instancia puede cambiarse sin preocupación más adelante a una implementación diferente siempre que la nueva instancia también implemente interfaz.Por esta razón, cada vez que desee usar una interfaz de objetos, siempre es una buena práctica declarar sus variables como la interfaz y no la implementación particular, esto se aplica a todos los tipos de objetos que puede usar que tienen una interfaz. La razón por la que lo ve a menudo es porque muchas personas lo han incorporado como un hábito.
Dicho esto, a veces no es dañino omitir el uso de interfaces, y la mayoría de nosotros descuidadamente no siempre seguimos esta regla, sin daño real. Es una buena práctica seguir cuando tiene la sensación de que el código puede cambiar y necesita mantenimiento / crecimiento en el futuro. Es menos preocupante cuando pirateas código que no sospechas que tendrá una larga vida o tiene mucha importancia. Además, romper esta regla generalmente tiene una pequeña consecuencia: cambiar la implementación a otra puede requerir un poco de refactorización, por lo que si no siempre la sigue, no se lastimará mucho, aunque tampoco hay ningún daño real en seguirla. .
fuente