Pergunta

ao programar em Java eu ??praticamente sempre, apenas por força do hábito, escrever algo como isto:

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

Na maioria das vezes sem pensar mesmo sobre ele. Agora, a pergunta é: devo sempre especificar a interface como o tipo de retorno? Ou é aconselhável usar a implementação real da interface, e em caso afirmativo, em que circunstâncias?

É óbvio que usando a interface tem uma série de vantagens (é por isso que ele está lá). Na maioria dos casos isso realmente não importa o que a implementação concreta é usado por uma função de biblioteca. Mas talvez existam casos em que não importa. Por exemplo, se eu sei que vou acessar principalmente os dados na lista aleatoriamente, um LinkedList seria ruim. Mas se a minha função de biblioteca só retorna a interface, eu simplesmente não sei. Para estar no lado seguro eu poderia até mesmo necessidade de copiar a lista explicitamente sobre a um ArrayList:

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

mas que apenas parece horrível e meus colegas de trabalho provavelmente iria me linchar no refeitório. E justamente por isso.

O que vocês acham? Quais são as suas diretrizes, quando você tende para a solução abstrato, e quando você revelar detalhes de sua implementação para os potenciais ganhos de desempenho?

Foi útil?

Solução

Por exemplo, se eu sei que eu vou acessar principalmente os dados na lista aleatoriamente, um LinkedList seria ruim. Mas se a minha função de biblioteca única retorna a interface, eu simplesmente não fazer conhecer. Para estar no lado seguro eu poderia mesmo necessidade de copiar a lista explicitamente até um ArrayList.

Como todos os outros mencionados, você só deve se preocupam com a forma como a biblioteca tem implementado a funcionalidade, para reduzir o acoplamento e aumentando a capacidade de manutenção da biblioteca.

Se você, como um cliente de biblioteca, pode demonstrar que a implementação está realizando mal para seu caso de uso, você pode entrar em contato com a pessoa responsável e discutir sobre o melhor caminho a seguir (um novo método para este caso ou apenas mudando a implementação).

Dito isto, o vosso exemplo cheira a otimização prematura.

Se o método é ou pode ser crítico, pode mencionar os detalhes de implementação na documentação.

Outras dicas

Voltar a interface apropriada para detalhes esconder de implementação. Seus clientes só deve se preocupar com o que suas ofertas de objetos, não como você implementou. Se você começar com um ArrayList privada, e decidir mais tarde que outra coisa (por exemplo, LinkedLisk, lista de pular, etc.) é mais apropriado você pode mudar a implementação sem afetar os clientes se você retornar a interface. No momento em que você voltar um concreto digite a oportunidade é perdida.

Na programação OO, queremos encapsular tanto quanto possível os dados. Esconder, tanto quanto possível a implementação real, abstraindo os tipos mais alto possível.

Neste contexto, gostaria de responder a retornar apenas o que é significativo . Será que faz sentido em tudo para o valor de retorno a ser a classe concreta? Aka no seu exemplo, pergunte-se: vai usar qualquer um método específico-LinkedList sobre o valor de retorno de foo

  • Se não, basta usar o nível mais alto Interface. É muito mais flexível e permite-lhe mudar o backend
  • Se sim, pergunte a si mesmo: eu não posso refatorar meu código para retornar à interface de nível superior? :)

O mais abstrato é o seu código, a menos muda o seu são obrigados a fazer quando se muda um backend. É simples assim.

Se, por outro lado, você acaba lançando os valores de retorno para a classe concreta, bem, isso é um forte sinal de que você provavelmente deve retornar em vez da classe concreta. Seus usuários / companheiros de equipe não deve ter de saber contratos sobre mais ou menos implícitas:. Se você precisa usar os métodos concretos, basta retornar a classe concreta, para maior clareza

Em poucas palavras: o código abstract , mas explicitamente :)

Sem ser capaz de justificá-la com resmas de citações CS (eu sou autodidata), eu sempre ido pelo mantra de "Aceite os menos derivados, retorne o mais derivados", ao projetar as classes e tem resistido me bem ao longo dos anos.

Eu acho que isso significa em termos de interface de contra retorno concreto é que se você está tentando reduzir dependências e / ou dissociar, retornando a interface é geralmente mais útil. No entanto, se os implementos classe concreta mais do que interface, geralmente é mais útil para os chamadores de seu método para obter de volta a classe concreta (ou seja, o "mais derivado") ao invés de aribtrarily limitá-los um subconjunto de funcionalidades que o objeto retornado - a menos que você realmente necessidade para restringi-los. Então, novamente, você também pode apenas aumentar a cobertura da interface. restrições desnecessárias como este I comparar com impensado vedação de classes; nunca se sabe. Só para falar um pouco sobre o ex-parte desse mantra (para outros leitores), aceitando o também menos derivado dá a máxima flexibilidade para chamadores de seu método.

-Oisin

Em geral, para um público de frente para interface como APIs, retornando a interface (como List) sobre a implementação concreta (como ArrayList) seria melhor.

O uso de um ArrayList ou LinkedList é um detalhe de implementação da biblioteca que deve ser considerado para o caso de uso comum a maior parte dessa biblioteca. E, claro, internamente, tendo métodos private entrega off LinkedLists não seria necessariamente uma coisa ruim, se ele oferece facilidades que tornam o processamento mais fácil.

Não há nenhuma razão para que uma classe concreta não deve ser utilizado na aplicação, a menos que haja uma boa razão para acreditar que alguma outra classe List seria usado mais tarde. Mas, novamente, mudar os detalhes de implementação não deve ser tão doloroso, enquanto a parte virada para público está bem concebido.

A biblioteca em si deve ser uma caixa preta para seus consumidores, para que eles realmente não precisa se preocupar com o que está acontecendo internamente. Isso também significa que a biblioteca deve ser projetado de forma que ele é projetado para ser usado na forma como ele se destina.

Como regra geral, eu só passar de volta implementações internas se estou em alguns trabalhos privados, internos de uma biblioteca, e mesmo assim somente com moderação. Por tudo o que é público e susceptível de ser chamado a partir do lado de fora do meu módulo I usar interfaces, e também o padrão de fábrica.

Usando interfaces em modo um tem provado ser uma forma muito confiável para escrever código reutilizável.

A principal questão foi respondida já e você deve sempre usar a interface. No entanto eu só gostaria de comentário sobre

É óbvio que usando a interface tem uma série de vantagens (é por isso que ele está lá). Na maioria dos casos isso realmente não importa o que a implementação concreta é usado por uma função de biblioteca. Mas talvez existam casos em que não importa. Por exemplo, se eu sei que eu vou acessar principalmente os dados na lista aleatoriamente, um LinkedList seria mau. Mas se a minha função de biblioteca só retorna a interface, eu simplesmente não sei. Para estar no lado seguro eu poderia até mesmo necessidade de copiar a lista explicitamente sobre a um ArrayList.

Se você estiver retornando uma estrutura de dados que você sabe que tem mau desempenho de acesso aleatório - O (n) e, normalmente, uma grande quantidade de dados - existem outras interfaces que você deve estar especificando em vez de List, como Iterable para que qualquer pessoa usando a biblioteca será plenamente consciente de que apenas acesso sequencial está disponível.

Escolher o tipo certo de retorno não é apenas sobre a interface de comparação implementação concreta, é também sobre como selecionar a interface de direito.

Não importa tudo o que muito se um método de API retorna uma interface ou uma classe concreta; apesar do que todos aqui diz, você quase nunca alterar a classe implementiation uma vez que o código é escrito.

O que é muito mais importante: sempre usar interfaces mínimo de escopo para o seu método parâmetros ! Dessa forma, os clientes têm a liberdade máxima e pode usar classes seu código nem sequer conhecem.

quando um método API retorna ArrayList, tenho absolutamente nenhum escrúpulo com isso, mas quando se exige uma ArrayList (ou, tudo para comum, Vector) parâmetro, considero caçar o programador e machucá-lo, porque isso significa que eu não pode usar Arrays.asList(), Collections.singletonList() ou Collections.EMPTY_LIST.

Desculpe discordar, mas eu acho que a regra básica é a seguinte:

  • Para de entrada argumentos usar mais genérico .
  • Para saída valores, o mais específica .

Assim, neste caso, você quer declarar a implementação como:

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

Justificação : O caso de entrada já é conhecido e explicado por todos: usar a interface, período. No entanto, o caso de saída pode parecer contra-intuitivo. Você quer voltar a implementação, porque você quer que o cliente tenha o máximo de informações sobre o que está recebendo. Neste caso, mais conhecimento é mais poder .

Exemplo 1: o cliente deseja obter o quinto elemento:

  • retornar Coleção: deve iterar até 5º elemento vs Lista de retorno:
  • Lista de retorno: list.get(4)

Exemplo 2: o cliente quer remover o 5º elemento:

  • Lista de retorno:. Necessário criar uma nova lista sem o elemento especificado (list.remove() é opcional)
  • retorno ArrayList: arrayList.remove(4)

Por isso, é uma grande verdade que o uso de interfaces é grande porque promove a reutilização, reduz o acoplamento, melhora a capacidade de manutenção e faz as pessoas felizes ... mas só quando usado como de entrada .

Assim, mais uma vez, a regra pode ser declarado como:

  • Seja flexível para o que você oferece.
  • Seja informativo com o que você entregar.

Então, da próxima vez, por favor, devolva a implementação.

Você usa interface para longe abstrair a implementação real. A interface é basicamente apenas um modelo para o que sua implementação pode fazer.

As interfaces são bom projeto, porque eles permitem que você os detalhes de implementação mudança sem ter que temer que qualquer um de seus consumidores são afetados diretamente, enquanto você implementação ainda faz o que sua interface diz que faz.

Para trabalhar com interfaces que iria instanciar-los como este:

IParser parser = new Parser();

Agora IParser seria sua interface, e Analisador seria a sua implementação. Agora, quando você trabalha com o objeto parser de cima, você vai trabalhar contra a interface (IParser), que por sua vez vai trabalhar contra a sua implementação (Analisador).

Isso significa que você pode mudar o funcionamento interno do Analisador tanto quanto você quer, ele nunca vai afetar código que funciona contra sua interface analisador IParser.

No uso geral a interface em todos os casos, se você não tem necessidade da funcionalidade da classe concreta. Note-se que para as listas, Java acrescentou um RandomAccess classe marcador principalmente para distinguir um caso comum onde um algoritmo pode precisar de saber se get (i) é de tempo constante ou não.

Para utilizações de código, Michael acima é certo que ser o mais genérico possível nos parâmetros do método é muitas vezes ainda mais importante. Isto é especialmente verdadeiro quando testando um tal método.

Você vai encontrar (ou que tenham encontrado) que, como você retornar as interfaces, eles permeiam através de seu código. por exemplo. você retornar uma interface de método A e você Have , em seguida, passar uma interface para o método B.

O que você está fazendo é a programação por contrato, ainda que de forma limitada.

Isto dá-lhe uma margem enorme para implementações de mudança debaixo das cobertas (desde que estes novos objetos cumprir os contratos existentes / comportamentos esperados).

Diante de tudo isso, você tem benefícios em termos de escolha de sua implementação, e como você pode substituir comportamentos (incluindo testes - usando zombeteiro, por exemplo). No caso de você não ter adivinhado, eu sou totalmente a favor disso e tentar reduzir (ou apresentar) as interfaces sempre que possível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top