Durante minha primeira implementação, estendendo a estrutura de coleção Java, fiquei bastante surpreso ao ver que a interface de coleção contém métodos declarados como opcionais. O implementador deve lançar UnsupportedOperationExceptions se não for suportado. Isso imediatamente me pareceu uma má escolha de design de API.
Depois de ler grande parte do excelente livro "Effective Java" de Joshua Bloch, e depois descobrir que ele pode ser responsável por essas decisões, não pareceu gelar com os princípios adotados no livro. Eu acho que declarar duas interfaces: Collection e MutableCollection, que estende a Collection com os métodos "opcionais", levaria a um código de cliente muito mais sustentável.
Há um excelente resumo dos problemas aqui .
Havia um bom motivo para a escolha de métodos opcionais em vez da implementação das duas interfaces?
fonte
Respostas:
O FAQ fornece a resposta. Em resumo, eles viram uma potencial explosão combinatória de interfaces necessárias com visão modificável e não modificável, somente exclusão, somente adição, comprimento fixo, imutável (para segmentação) e assim por diante para cada conjunto possível de métodos de opção implementados.
fonte
const
palavra - chave como C ++.vector<int>, const vector<int>, vector<const int>, const vector<const int>
. Até aí tudo bem, mas em seguida, tentar gráficos implementação, e que pretende tornar a estrutura gráfico constante, mas atribui o nó modificável, etc.can
método que testaria se uma operação é possível? Manteria a interface simples e rápida.Parece-me que o
Interface Segregation Principle
não era tão bem explorado naquela época como é agora; essa maneira de fazer as coisas (ou seja, sua interface inclui todas as operações possíveis e você tem métodos "degenerados" que lançam exceções para aquelas que você não precisa) era popular antes de o SOLID e o ISP se tornarem o padrão de fato para o código de qualidade.fonte
Count
uma coleção sem ter que se preocupar com os tipos de itens que ela contém), mas em estruturas baseadas em apagamento de tipo como Java, esse não é um problema.Enquanto algumas pessoas podem odiar "métodos opcionais", em muitos casos podem oferecer semântica melhor do que interfaces altamente segregadas. Entre outras coisas, eles permitem as possibilidades de que um objeto possa adquirir habilidades ou características durante sua vida útil, ou que um objeto (especialmente um objeto de invólucro) talvez não saiba quando é construído quais habilidades exatas devem ser relatadas.
Embora eu dificilmente chame as classes de coleções Java de modelos de bom design, sugiro que uma boa estrutura de coleções inclua em sua base um grande número de métodos opcionais, além de maneiras de perguntar a uma coleção sobre suas características e habilidades . Esse design permitirá que uma única classe de wrapper seja usada com uma grande variedade de coleções sem ocultar acidentalmente as habilidades que a coleção subjacente possa possuir. Se os métodos não fossem opcionais, seria necessário ter uma classe de wrapper diferente para cada combinação de recursos que as coleções possam suportar, ou então alguns wrappers podem ser inutilizados em algumas situações.
Por exemplo, se uma coleção suportar escrever um item por índice ou anexar itens no final, mas não suportar a inserção de itens no meio, o código que deseja encapsulá-lo no wrapper que registraria todas as ações executadas nela precisaria de uma versão do wrapper de log que fornecia a combinação exata de habilidades suportadas ou, se não houvesse nenhum disponível, seria necessário usar um wrapper que suporta anexar ou gravar por índice, mas não ambos. Se, no entanto, uma interface de coleção unificada fornecesse todos os três métodos como "opcional", mas incluísse métodos para indicar quais métodos opcionais seriam utilizáveis, uma única classe de wrapper poderia manipular coleções que implementam qualquer combinação de recursos. Quando perguntado sobre quais recursos ele suporta, um wrapper pode simplesmente relatar o que a coleção encapsulada suportar.
Observe que a existência de "habilidades opcionais" pode, em alguns casos, permitir que coleções agregadas implementem certas funções de maneiras muito mais eficientes do que seria possível se as habilidades fossem definidas pela existência de implementações. Por exemplo, suponha que um
concatenate
método tenha sido usado para formar uma coleção composta de duas outras, a primeira sendo uma ArrayList com 1.000.000 elementos e a última uma coleção de vinte elementos, que só poderia ser iterada desde o início. Se a coleção composta fosse solicitada para o 1.000.013 ° elemento (índice 1.000.012), ele poderia perguntar à ArrayList quantos itens continha (ou seja, 1.000.000), subtrair isso do índice solicitado (com 12), ler e pular doze elementos do segundo coleção e, em seguida, retorne o próximo elemento.Em tal situação, mesmo que a coleção composta não tivesse uma maneira instantânea de retornar um item por índice, solicitar à coleção composta pelo 1.000.013º item ainda seria muito mais rápido do que ler 1.000.013 itens individualmente e ignorar todos, exceto o último 1.
fonte
AsXXX
método na interface base que retornará o objeto sobre o qual é invocado se implementar essa interface, retornar um objeto wrapper que suporte essa interface, se possível, ou lançar uma exceção se não. Por exemplo, umaImmutableCollection
interface pode exigir, por contrato ... #Eu atribuiria isso aos desenvolvedores originais, simplesmente não sabendo melhor naquela época. Percorremos um longo caminho no design de OO desde 1998, quando o Java 2 e o Collections foram lançados. O que parece um projeto ruim óbvio agora não era tão óbvio nos primeiros dias da OOP.
Mas isso pode ter sido feito para impedir a transmissão extra. Se fosse uma segunda interface, você teria que converter suas instâncias de coleções para chamar esses métodos opcionais, que também são feios. Agora, você pega uma UnsupportedOperationException imediatamente e corrige seu código. Mas se houvesse duas interfaces, você teria que usar instanceof e casting em todo o lugar. Talvez eles considerassem isso uma troca válida. Também no início do Java 2 dias, a instância de era bastante desaprovada devido ao seu desempenho lento, eles poderiam estar tentando impedir o uso excessivo dele.
É claro que tudo isso é especulação selvagem, duvido que possamos responder isso com certeza, a menos que um dos arquitetos das coleções originais entre em cena.
fonte
Collection
e não umMutableCollection
, é um sinal claro de que ele não deve ser modificado. Não sei por que alguém precisaria lançá-los. Ter interfaces separadas significa que você obterá esse tipo de erro no tempo de compilação, em vez de obter uma exceção no tempo de execução. Quanto mais cedo você receber o erro, melhor.const
objeto, informando instantaneamente ao usuário que o objeto não pode ser modificado.