Во время моей первой реализации, расширяющей инфраструктуру Java-коллекции, я был очень удивлен, увидев, что интерфейс коллекции содержит методы, объявленные как необязательные. Ожидается, что разработчик выдаст исключение UnsupportedOperationException, если оно не поддерживается. Это сразу показалось мне плохим выбором дизайна API.
Прочитав большую часть превосходной книги Джошуа Блоха «Эффективная Ява», а затем узнав, что он может нести ответственность за эти решения, она, похоже, не согласилась с принципами, изложенными в книге. Я думаю, что объявление двух интерфейсов: Collection и MutableCollection, расширяющих Collection с помощью «необязательных» методов, привело бы к гораздо более поддерживаемому клиентскому коду.
Там отличный обзор вопросов здесь .
Была ли веская причина, почему вместо реализации двух интерфейсов были выбраны дополнительные методы?
источник
Ответы:
FAQ дает ответ. Короче говоря, они увидели потенциальный комбинаторный взрыв необходимых интерфейсов с изменяемым, неизменяемым представлением, только для удаления, только для добавления, фиксированной длины, неизменяемым (для потоков) и так далее для каждого возможного набора реализованных методов параметров.
источник
const
ключевое слово типа C ++.vector<int>, const vector<int>, vector<const int>, const vector<const int>
. Пока все хорошо, но затем вы пытаетесь реализовать графы, и вы хотите сделать структуру графов постоянной, но атрибуты узла можно изменять и т. Д.can
создали метод, который бы проверял, возможна ли операция? Это сделало бы интерфейс простым и быстрым.Для меня это звучит так, будто тогда
Interface Segregation Principle
не было так хорошо изучено, как сейчас; этот способ выполнения действий (т. е. ваш интерфейс включает в себя все возможные операции, и у вас есть «вырожденные» методы, которые генерируют исключения для тех, которые вам не нужны) был популярен до того, как SOLID и ISP стали стандартом де-факто для качественного кода.источник
Count
коллекцию, не беспокоясь о том, какие типы элементов она содержит), но в основанных на стирании типов средах, таких как Java, это не такая проблема.Хотя некоторые люди могут ненавидеть «дополнительные методы», они могут во многих случаях предлагать лучшую семантику, чем сильно разделенные интерфейсы. Среди прочего, они учитывают возможности того, что объект может получить способности или характеристики в течение своего времени жизни или что объект (особенно объект-обертка) может не знать, когда он сконструирован, о каких точных способностях он должен сообщать.
Хотя я вряд ли назову классы коллекций Java образцами хорошего дизайна, я бы предположил, что хорошая структура коллекций должна включать в себя большое количество дополнительных методов, а также способы расспросить коллекцию о ее характеристиках и возможностях . Такой дизайн позволит использовать один класс-обертку с большим разнообразием коллекций без случайного затенения способностей, которыми может обладать базовая коллекция. Если бы методы не были необязательными, то было бы необходимо иметь разные классы-оболочки для каждой комбинации функций, которые могут поддерживать коллекции, иначе в некоторых ситуациях некоторые оболочки могут быть непригодными.
Например, если коллекция поддерживает запись элемента по индексу или добавление элементов в конце, но не поддерживает вставку элементов в середине, тогда для кода, который хочет инкапсулировать его в оболочку, которая будет регистрировать все выполненные над ним действия, потребуется версия Оболочки журналирования, которая обеспечивала точную комбинацию поддерживаемых способностей, или, если ни одна из них не была доступна, пришлось бы использовать оболочку, которая поддерживала либо добавление, либо запись по индексу, но не оба. Если, однако, унифицированный интерфейс коллекции предоставил все три метода как «необязательные», но затем включил методы, чтобы указать, какой из дополнительных методов будет использоваться, то единственный класс-оболочка может обрабатывать коллекции, которые реализуют любую комбинацию функций. Когда его спросят, какие функции он поддерживает, оболочка может просто сообщить, что поддерживает инкапсулированная коллекция.
Обратите внимание, что существование «необязательных способностей» может в некоторых случаях позволять агрегированным коллекциям реализовывать определенные функции способами, которые были бы гораздо более эффективными, чем было бы возможно, если бы способности определялись существованием реализаций. Например, предположим, что
concatenate
метод использовался для формирования составной коллекции из двух других, первым из которых оказался ArrayList с 1 000 000 элементов, а последним был набор из двадцати элементов, который можно было повторять только с самого начала. Если в составной коллекции запрашивается 1 000 013-й элемент (индекс 1 000 012), он может спросить у ArrayList, сколько элементов в нем содержится (т.е. 1 000 000), вычесть это из запрошенного индекса (получая 12), прочитать и пропустить двенадцать элементов из второго коллекция, а затем вернуть следующий элемент.В такой ситуации, даже несмотря на то, что у составной коллекции не было бы мгновенного способа возврата элемента по индексу, запрос составной коллекции для 1 000 013-го элемента все равно был бы намного быстрее, чем чтение 1 000 013 элементов из нее по отдельности и игнорирование всех, кроме последнего один.
источник
AsXXX
в базовый интерфейс можно включить метод, который будет возвращать объект, для которого он вызывается, если он реализует этот интерфейс, возвращать объект-обертку, который поддерживает этот интерфейс, если это возможно, или генерировать исключение, если нет. Например,ImmutableCollection
интерфейс может потребовать по контракту ...Я бы отнес это к первоначальным разработчикам, просто не зная тогда лучшего. Мы прошли долгий путь в ОО-дизайне с 1998 года или около того, когда впервые были выпущены Java 2 и Коллекции. То, что кажется очевидным плохим дизайном, не было таким очевидным в первые дни ООП.
Но это, возможно, было сделано, чтобы предотвратить дополнительное использование. Если бы это был второй интерфейс, вам пришлось бы приводить экземпляры своих коллекций для вызова тех необязательных методов, что также довольно уродливо. Как сейчас, вы сразу же поймаете исключение UnsupportedOperationException и исправите свой код. Но если бы было два интерфейса, вам пришлось бы использовать instanceof и приведение повсеместно. Возможно, они считали это действительным компромиссом. Также в начале двухдневной Java-версии на экземпляр instanceof приходилось неодобрительно из-за его низкой производительности, возможно, они пытались предотвратить его чрезмерное использование.
Конечно, это все дикие предположения, я сомневаюсь, что мы могли бы ответить на этот вопрос наверняка, если не вмешается один из оригинальных архитекторов коллекций.
источник
Collection
а не aMutableCollection
, это явный признак того, что он не предназначен для изменения. Я не знаю, почему кто-то должен был их разыграть. Наличие отдельных интерфейсов означает, что вы получите такие ошибки во время компиляции, а не получите исключение во время выполнения. Чем раньше вы получите ошибку, тем лучше.const
объект, мгновенно сообщая пользователю, что объект нельзя изменить.