Подтвердить что ты не робот

Является ли класс ReadOnlyCollection хорошим примером Bad Design?

Посмотрите на спецификацию класса ReadOnlyCollection, он реализует IList, правильно.

В интерфейсе IList есть методы добавления/обновления/чтения, которые мы называем предварительными условиями интерфейса. В любом случае в моем приложении, если у меня есть IList, я должен иметь возможность выполнять все подобные операции.

Но как насчет того, верну ли ReadOnlyCollection где-нибудь в моем коде и попытаюсь вызвать метод .Add(...)? Он генерирует исключение NotSupportedException. Считаете ли вы, что это хороший пример плохого дизайна? Кроме того, этот класс нарушает Принцип замены Лискова?

Почему Microsoft реализовала этот способ? Должно ли быть проще (и лучше), чтобы этот ReadOnlyCollection реализовал только интерфейс IEnumerable (который, кстати, уже только для чтения)?

4b9b3361

Ответ 1

Я думаю, что если происходит плохой дизайн, это привычка добавлять к IList без проверки свойства ReadOnly. Привычка программистов игнорировать части интерфейса не означает, что интерфейс плохой.

По правде говоря, немногие из нас, программисты, никогда не удосужились прочитать спецификации. И, честно говоря, есть много вещей, которые кажутся мне более интересными, чем сидеть и читать весь технический документ. (Например, если вы действительно можете держать глаза открытыми с помощью зубочисток, например.) Кроме того, у меня есть ограничение, что я все равно не помню.

Сказав это, нельзя использовать интерфейс, по крайней мере, не глядя на список свойств и методов. И какова цель, на ваш взгляд, имеет значение логическое свойство "ReadOnly"? Возможно, потому, что список можно читать только по той или иной причине. И если вы берете список, переданный откуда-то вне вашего собственного кода, вы должны проверить, что список не читается, прежде чем вы пытаетесь его добавить.

Ответ 2

Да, это действительно плохой дизайн. Интерфейсов коллекции не хватает в .NET: нет интерфейсов только для чтения.

Знаете ли вы, что string[] реализует IList<string> (и ditto для других типов)? У этой проблемы есть одна и та же проблема: вы можете ожидать, что вы можете называть Add и Remove на интерфейсе, но он будет бросать.

К сожалению, это уже нельзя изменить, не нарушая обратной совместимости, но я согласен с вами в том, что это очень плохой дизайн. Лучший дизайн мог бы видеть отдельные интерфейсы для возможностей только для чтения.

Ответ 3

Хотя интерфейс IList<T> определяет методы Add(T) и Insert(int,T), он также определяет свойство IsReadOnly, и если вы внимательно прочитаете определение IList.Insert(int, T) и методы IList.Add(T) в MSDN, вы можете видеть, что оба они указывают, что методы могут бросать NotSupportedException, если список доступен только для чтения.

Говоря, что это плохой дизайн по этой причине, это похоже на то, что он также плохой дизайн, потому что Insert(int, T) может бросать ArgumentOutOfRangeException, когда индекс отрицательный или больше размера коллекции.

Ответ 4

Это не отличный дизайн, а необходимое зло, на мой взгляд.

К сожалению, Microsoft не включила в структуру что-то вроде IReadableList<> и IWriteableList<> и сама IList<> реализует оба из них (или даже пропускает IList<> вообще и имеет IWriteableList<> реализация IReadableList<>). Проблема решена.

Но слишком поздно менять сейчас, и если у вас есть ситуация, когда вам нужна ваша коллекция, чтобы иметь семантику списка, и вы предпочтете исключить исключение во время выполнения, а не разрешать мутации, то ReadOnlyCollection<>, к сожалению, ваш лучший вариант.

Ответ 5

IList имеет некоторые методы и свойства чтения, такие как Item и IndexOf (..). Если ReadOnlyCollection будет реализовывать IEnumerable только тогда, вы пропустите их.


Какая альтернатива? Имея только что прочитанную версию IList и версию для записи? Это усложнит весь BCL (не говоря о LINQ).


Также я не думаю, что это нарушает принцип замены Лискова, потому что он определен на базовом уровне (IList), что он может вызывать не поддерживаемое исключение.

Ответ 6

Я думаю, что это хороший пример компромисса между абстракцией и специализацией.

Вам нужна гибкость IList, но вы также хотите наложить некоторые ограничения, так что вы делаете? То, как оно было разработано, немного неудобно и, вероятно, технически нарушает некоторые принципы проектирования, но я не уверен, что было бы лучше и все же дало бы вам такую ​​же функциональность и простоту.

В этом случае было бы лучше иметь отдельный интерфейс IListReadOnly. Тем не менее, легко спуститься по пути к сумасшедшему одноразовому пространству распространения интерфейса и сделать вещи очень запутанными.

Ответ 7

Я бы сказал, что это плохой дизайн. Даже если принять концепцию умеренно большого интерфейса с запросами на функции, должны были быть другие интерфейсы, которые унаследовали от них, что гарантировало бы, что определенные поведения будут разрешены. Например:

  • IEnumerable (как и существующий, но без reset, и никаких обещаний о том, что произойдет, если коллекция будет изменена во время перечисления)
  • IMultipassEnumerable (добавляет reset и гарантирует, что повторные перечисления изменяющейся коллекции будут возвращать одни и те же данные или throw и исключение)
  • ICountableEnumerable (многостраничное перечислимое, плюс свойство count и метод для получения счетчика и подсчета одновременно)
  • IModifiableEnumerable (IEnumerator, который не будет бросать, если коллекция будет изменена во время перечисления потоком, выполняющим перечисление. Точное поведение не будет указано, но элементы, которые не изменяются во время перечисления, должны быть возвращены ровно один раз, те, которые изменены во время перечисления должны быть возвращены не более одного раза для каждого добавления или модификации, плюс один, если они существовали при запуске перечисления. Этот интерфейс сам по себе не предоставляет никаких мутаций, но будет использоваться в сочетании с другими, которые делают).
  • ICopyableAsEnumerable (включает свойство count и метод для возврата IEnumerable, который представляет моментальный снимок списка, а не фактически самого IEnumerable, но полезную функцию для IEnumerable для предоставления).
  • IImmutable (нет элементов, но наследуемых для создания гарантированных неизменяемых интерфейсов)
  • IImmutableEnumerable
  • IImmutableCountableEnumerable
  • IList (может быть доступен для чтения, чтения-записи или неизменяемости)
  • IImmutableList (нет новых членов, но наследует IImmutable)
  • IWritableList (нет новых участников, но гарантированно доступен для записи)

Это всего лишь небольшая выборка, но должна передать идею дизайна.

Ответ 8

Плохой дизайн ИМО. Вероятно, это связано с проблемами обратной совместимости, отсутствием коразмерности и противоречивости и т.д. И т.д. Теперь, к счастью, они обратились к нему в .NET 4.5 с помощью:

IReadOnlyList<out T>
IReadOnlyCollection<out T>
IReadOnlyDictionary<TKey, TValue>

Однако мне не хватает интерфейса "только для чтения" с "bool Contains (T)".