Дизайн (How-to) классов, содержащих коллекции других классов - PullRequest
5 голосов
/ 25 июня 2010

Как проектировать классы с участием коллекций других классов?

Общий пример:

A Рабочая область содержит количество проектов .
A Проект содержит большое количество ресурсов .
Каждый ресурс может содержать большое количество файлов .

Таким образом, здесь могут быть определены классы Workspace, Project, Resource и File. Рабочая область будет иметь список Project.Project будет иметь список ресурсов, а Resource будет иметь список файлов. Конечно, у каждого класса есть свои настройки.

Теперь основные вопросы:
а) Кто создает и добавляет класс к определенной коллекции? Другой класс или класс, содержащий коллекцию?
б) Как отслеживать конкретную коллекцию и как ее хранить?
c) Кто проверяет изменения в конкретной коллекции?
d) Каковы различные шаблоны проектирования, которые можно применять в таких ситуациях?

По сути, я хочу уменьшить связь между различными классами.

Спасибо всем

Ответы [ 3 ]

5 голосов
/ 26 июня 2010

Есть много видов отношений - рассмотрим

  • Автомобиль и колеса
  • Автомобиль и водитель
  • Автомобиль и зарегистрированный владелец
  • Клиент иСтрока заказа и порядка
  • Школа и класс, а также экземпляр класса

Если вы посмотрите на моделирование UML, вы увидите такие понятия, как мощность и направление, а также различия между объединением и композицией и вопросыотносящиеся к жизненному циклу связанных объектов.

Тогда неудивительно, что нам нужен набор методов и шаблонов для работы с различными видами отношений.

Относительно d).Существует один главный принцип Закон Деметры или принцип наименьшего знания.

Одна из важных техник - Инкапсуляция уменьшает связь, скрывая информацию.Автомобили, вероятно, мало интересуются многими сведениями о людях, поэтому у нас может быть интерфейс IDriver в нашем классе Person, IDriver предлагает особые методы, о которых заботится Automobile.Общий принцип состоит в том, чтобы отдавать предпочтение программированию для интерфейсов.

После этого мы можем подумать о а).Создание.Поскольку мы склонны использовать интерфейсы, часто имеет смысл использовать фабричные шаблоны.Это оставляет вопрос о том, кто называет фабрику.Предпочитаем ли мы:

   IPerson aPerson = myAutomobile.createDriver( /* params */);  

или

  IPerson aPerson = aPersonFactory.create( /* params */);
  myAutomobile.addDriver(aPerson);

Здесь я думаю, что совершенно очевидно, что автомобили мало знают о людях, и поэтому второе - это лучшее разделение обязанностей.Однако, может быть, Orders мог бы разумно создавать OrderLines, а классы создавать ClassInstances?

б).Отслеживание?Вот почему у нас есть богатый набор классов Collection.Какие из них использовать, зависит от характера отношений (один-один, один-много и т. Д.) И от того, как мы их используем.Поэтому мы выбираем Arrays, HashMaps и т. Д. В соответствии с потребностями.Для Автомобиля / Колеса мы могли бы даже использовать атрибуты имен Автомобиля - ведь у Автомобиля ровно шесть колес (frontLeft, frontRight, backLeft, backRight, запчасти и рулевое управление).Если под «хранить» вы имеете в виду постоянство, то мы смотрим на такие методы внешних ключей в реляционной базе данных.Отображение между RDBMS и объектами в памяти все чаще управляется хорошими механизмами сохранения, такими как JPA.

c).Аудит?Я не видел, чтобы аудит применялся конкретно на уровне отношений.Ясно, что метод automotive.addDriver () может быть сколь угодно сложным.Если есть деловые требования для аудита этого действия, то совершенно очевидно, что это достойное место для этого.Это просто стандартный вопрос разработки ОО, вращающийся вокруг того, кто владеет информацией.Общий принцип: «Не повторяйте себя» настолько ясно, что мы не хотим, чтобы каждый объект, который вызывает addDriver (), должен был помнить для аудита, поэтому это работа Auto.

2 голосов
/ 26 июня 2010

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

Рабочая область имеет тип Проект + Проект ^ 2 + Проект ^3 ... (что означает true списка проектов - true WorkSpace)

Аналогично,

AПроект имеет тип Ресурс + Ресурс ^ 2 + Ресурс ^ 3 ...

Ресурс имеет тип Файл + Файл ^ 2 + Файл ^ 3 ...

Таким образом, на языке, подобном C # †, вы можете определить свой класс WorkSpace следующим образом:

public class WorkSpace : IList<Project> //the important thing here is that you're declaring that things that are true for a list of Projects is true for a WorkSpace. The WorkSpace class may in fact do other stuff too...
{

}

и аналогично для других классов.

Затем в своем клиентском коде выиспользовал бы это так:

foreach (var project in WorkSpace)
{
    //do stuff
}

или

Projects.Add(new Resource() { file1, file2, file3, file4, /* etc */});

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

† Из вашей истории публикаций я предполагаю, что вы знакомы с C #, но это относится и к другим языкам.

1 голос
/ 26 июня 2010

Создание хорошего ОО-дизайна - это немного художественная форма, но следует помнить о некоторых принципах.

Подумайте, как ваши пользователи будут использовать ваш интерфейс. Кто ваш пользователь?Это только вы и для определенной цели, или вы разрабатываете что-то для разных клиентов.Дизайн интерфейса сложен, но подумайте о реальных примерах того, как ваши пользователи действительно захотят использовать ваш интерфейс.Хороший способ сделать это - написать тесты, которые заставляют вас использовать свои собственные вещи.Другой способ - посмотреть на существующие библиотеки, которые делают то же самое, и посмотреть, как они используются.

Сохраняйте вещи простыми, глупыми. То есть сохраняйте их простыми с точки зрения ваших посетителей,Это относится к инкапсуляции, когда вы должны раскрывать только внутреннюю часть вашей реализации по мере необходимости.Это относится к созданию последовательного интерфейса, который легко понять.И это означает, что вы должны избегать ловушек создания объекта для каждого мыслимого класса, создания тонны атрибутов и максимально общего характера.Этот последний пункт является особенно важным;Что делает нас хорошими разработчиками, так это то, что у нас есть способность размышлять и абстрагироваться, но из-за этого мы часто попадаем в ловушку создания систем, значительно более сложных, чем они должны быть.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...