Должен ли я подкласс List <T> или иметь его как свойство?

Я столкнулся с этой проблемой довольно часто в течение последних нескольких месяцев, в течение которых я строил эту систему. Сценарий таков: у меня есть такой объект, который по существу является списком других объектов, но имеет некоторые другие свойства, специфичные по своей природе. Например:

  • Класс Tests:
    • Содержит много объектов Test
    • Имеет свойства:
      • DefaultTimeouts
      • DefaultNumberOfTries

Должен ли я иметь этот подкласс класса List<Test> или должен ли я наследовать его от Object, просто имея список как свойство рядом с другими полями?

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

Ответы

Ответ 1

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

Обычно у меня есть свойство типа List (или IList), которое возвращает ссылку на список. Обычно вам нужно только получить свойство здесь. Вы можете контролировать доступ к списку, выбирая для возврата версии только для чтения с помощью .AsReadOnly() или просто отображая список как IEnumerable.

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

Ответ 2

Подкласс из списка <T> . Если у вас есть Generic как свойство, он не так инкапсулирован как подкласс.

Если это выглядит как List <T> , и это похоже на List <T> , это, вероятно, List <T> .

Я бы назвал это TestCollection.

Ответ 3

Если это буквально делает все, что делает a List, и все функции List будут действовать на объект Tests интуитивно понятным способом, который дает правильный результат, тогда, на мой взгляд, он должен просто подклассом List<Test>.

В противном случае у вас будет класс с тонны методов, которые просто вызовут метод с тем же именем в переменной List<Test> в экземпляре. Это будет просто несовершенное расширение самого класса List<Test>.

Ответ 4

Наследование обычно сопоставляется с отношением "is-a". Поскольку ваш контейнер представляет собой список, но с некоторыми другими вещами, я бы наследовал от List и добавлял ваши дополнительные свойства в ваш подкласс.

Ответ 5

Я не думаю, что у вас есть простой "список тестов", потому что вам нужно что-то еще. Я предлагаю вам назвать его TestSuite и сделать Список как свойство. Имеет-а намного легче поддерживать по сравнению с наследованием.

В общем, я был бы очень осторожен, чтобы наследовать что-то вроде List.

Ответ 6

При чтении ответов кажется, что ключевой вопрос заключается в том, какой длиной является ваш объект действительно список? Поэтому, возможно, хорошим компромиссом между опасностями наследования и отсутствием прозрачности композиции было бы иметь List<Test> как частный бэкэнд и выставлять только те методы, которые будут использоваться.

Ответ 7

В вашем примере вы сказали: "Тест классов содержит множество объектов теста", а не "Тест классов" - это набор объектов "Тесты". IMO нет необходимости подкласса List в этом сценарии, если вам не нужен интерфейс List-like для этого класса.

Однако ответ действительно зависит от контекста класса Tests. Если он ведет себя как List и будет использоваться в контекстах, где ожидается список объектов, наследуйте от List.

Обратите внимание, что наследование сложнее, чем состав.

Кроме того, я бы переименовал тесты в TestCollection (если вы являетесь списком подкласса) или что-то вроде TestUnit (если он будет содержать список тестовых классов.

Ответ 8

"Предпочтительная композиция над наследованием" всегда является хорошим правилом. Являются ли методы, которые используют этот класс, используя все методы List, а также те, которые вы добавляете, или только те, которые вы добавляете?