Na maioria dos códigos Java, vejo pessoas declarando objetos Java como este:
Map<String, String> hashMap = new HashMap<>();
List<String> list = new ArrayList<>();
ao invés de:
HashMap<String, String> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
Por que existe uma preferência para definir o objeto Java usando a interface, em vez da implementação que realmente será usada?
java
object-oriented
class-design
Suman
fonte
fonte
Respostas:
O motivo é que a implementação dessas interfaces geralmente não é relevante ao manipulá-las; portanto, se você obriga o chamador a passar a
HashMap
para um método, então você está essencialmente obrigando qual implementação usar. Portanto, como regra geral, você deve lidar com a interface em vez da implementação real e evitar a dor e o sofrimento que podem resultar na alteração de todas as assinaturas de métodoHashMap
quando você decidir usarLinkedHashMap
.Deve-se dizer que há exceções quando a implementação é relevante. Se você precisar de um mapa quando a ordem for importante, poderá exigir a aprovação de um
TreeMap
ouLinkedHashMap
a, ou melhor ainda,SortedMap
que não especifique uma implementação específica. Isso obriga o chamador a necessariamente passar um certo tipo de implementação do Mapa e sugere fortemente que a ordem é importante. Dito isto, você pode substituirSortedMap
e passar por um não classificado? Sim, é claro, no entanto, esperamos que coisas ruins aconteçam como resultado.No entanto, as práticas recomendadas ainda determinam que, se não for importante, você não deve usar implementações específicas. Isso é verdade em geral. Se você está lidando com
Dog
e doCat
qual derivaAnimal
, para fazer o melhor uso da herança, geralmente evite ter métodos específicos paraDog
ouCat
. Em vez disso todos os métodos emDog
ouCat
deve substituir métodosAnimal
e ele irá salvar-lhe problemas no longo prazo.fonte
SortedMap
, nãoTreeMap
.SortedMap
é uma das várias implementações que lidam com pedidos. Isso está além do ponto.TreeMap
também solicita itens de acordo com a implementação da chaveComparable
ou com umaComparator
interface.LinkedHashMap
não implementaSortedMap
. As únicas subclasses deSortedMap
sãoConcurrentSkipListMap
eTreeMap
.Nas palavras de Layman:
A mesma razão pela qual os fabricantes de aparelhos elétricos construíram seus produtos com plugues elétricos, em vez de simplesmente descascarem cabos, e as casas vêm com soquetes de parede, em vez de descascarem cabos saindo da parede.
Ao usar plugues padrão, eles permitem conectar os mesmos aparelhos em qualquer plugue compatível da casa.
Do ponto de vista da tomada, não importa se você conecta um aparelho de TV ou um aparelho de som.
Isso torna o aparelho e o soquete mais úteis.
Tomemos, por exemplo, um método que aceite um mapa como argumento.
O método funcionará independentemente de você passar um HashMap ou um LinkedHashMap para ele, desde que seja uma subclasse de Map.
Esse é o princípio de substituição de Liskov .
No código de amostra que você forneceu, isso significa que você poderá, posteriormente, por algum motivo, alterar a implementação concreta do Hash e não precisará alterar o restante do código.
O problema com o software é que, como é relativamente fácil mudar as coisas posteriormente sem desperdício de tijolos ou argamassa, as pessoas assumem que esse tipo de pensamento antecipado não vale a pena. Mas a realidade nos mostrou que a manutenção de software é muito cara.
fonte
É seguir o princípio de segregação da interface (o 'I' no SOLID ). Impede que o código que usa esses objetos dependa dos métodos daqueles que não são necessários, o que torna o código menos acoplado e, portanto, mais fácil de alterar.
Por exemplo, se você descobrir mais tarde que realmente precisa de um
LinkedHashMap
, poderá fazer essa alteração com segurança sem afetar nenhum outro código.No entanto, há uma troca, porque você limita artificialmente o código que pode levar seu objeto como parâmetro. Digamos que exista uma função em algum lugar que exija um
HashMap
por algum motivo. Se você retornar aMap
, não poderá passar seu objeto para essa função. Você precisa equilibrar a probabilidade de, no futuro, precisar da funcionalidade extra da classe mais concreta, com o desejo de limitar o acoplamento e manter sua interface pública o menor possível.fonte
A restrição da variável a uma interface garante que nenhum dos usos dessa variável esteja usando
HashMap
funcionalidades específicas que podem não existir na interface; portanto, a instância poderá ser alterada sem preocupação posteriormente para uma implementação diferente, desde que a nova instância também implemente o interface.Por esse motivo, sempre que você desejar usar uma interface de objetos, é sempre uma boa prática declarar suas variáveis como a interface e não a implementação específica; isso vale para todos os tipos de objetos que você pode usar e que possuem uma interface. A razão pela qual você vê isso frequentemente é que muitas pessoas construíram isso como um hábito.
Dito isto, às vezes não é prejudicial deixar de usar interfaces, e a maioria de nós nem sempre segue essa regra sem nenhum dano real. É apenas uma boa prática para manter quando você sentir que o código pode ser alterado e precisar de manutenção / crescimento no futuro. É menos preocupante quando você está invadindo um código que você não suspeita que terá uma vida longa ou muita importância. Também quebrar essa regra geralmente tem uma pequena conseqüência de que mudar a implementação para outra pode exigir um pouco de refatoração; portanto, se você nem sempre a seguir, não se machucará muito, embora não haja nenhum dano real em segui-la. .
fonte