Как разработать классы, включающие наборы других классов?
Общий пример:
Рабочая область содержит количество Проектов.
Проект содержит большое количество Ресурсов.
Каждый Ресурс может содержать большое количество Файлов.
Таким образом, здесь определенными классами может быть Рабочая область, Проект, Ресурс и Файл. Рабочая область будет иметь список Проекта. Проект будет иметь список Ресурсов, и Ресурс будет иметь список Файлов. Конечно, каждый класс имеет свои связанные настройки.
Теперь основные вопросы:
a) Кто создает и добавляет класс к конкретному набору? Другой класс или класс, содержащий набор?
b) Также, как отслеживать конкретный набор и как сохранить то же?
c) Кто контролирует изменения в конкретном наборе?
d) Каковы различные шаблоны разработки, которые могли быть применены в таких ситуациях?
В основном я хочу уменьшить связь между различными классами.
Спасибо все
Существует много видов взаимоотношений - рассмотрим
Если вы посмотрите на моделирование UML, вы увидите такие понятия, как мощность и направление, различия между аггеграцией и композицией, а также вопросы, касающиеся жизненного цикла связанных объектов.
Тогда неудивительно, что нам нужен ряд техник и шаблонов, чтобы иметь дело с разными типами отношений.
Относительно d). Есть один главный принцип Закон Деметры или принцип наименьшего знания.
Одним из важных методов является инкапсуляция , уменьшающая связь, скрывая информацию. Автомобили, вероятно, мало интересуются многими деталями о людях, поэтому у нас может быть интерфейс IDriver в нашем классе Person, IDriver предлагает конкретные методы, о которых заботится Automobile. Общий принцип состоит в том, чтобы отдавать предпочтение программированию, а не интерфейсам.
После этого мы можем подумать о а). Творчество. Поскольку мы склонны использовать интерфейсы, часто имеет смысл использовать фабричные шаблоны. Остается вопрос, кто звонит на завод. Что мы предпочитаем:
IPerson aPerson = myAutomobile.createDriver( /* params */);
или
IPerson aPerson = aPersonFactory.create( /* params */);
myAutomobile.addDriver(aPerson);
Здесь, я думаю, довольно ясно, что автомобили мало что знают о людях, и поэтому второй вариант - лучшее разделение обязанностей. Однако, может быть, заказы могли бы разумно создавать OrderLines, а классы создавать ClassInstances?
б). Отслеживание? Вот почему у нас есть богатые наборы классов Collection.Какие из них использовать, зависит от характера отношений (один-один, один-много и т. Д.) И того, как мы их используем. Поэтому мы выбираем массивы, хэш-карты и т. Д. В соответствии с потребностями. Для автомобиля / колеса мы могли бы даже использовать атрибуты имен автомобилей - в конце концов, у автомобиля ровно шесть колес (frontLeft, frontRight, backLeft, backRight, запасное и рулевое управление). Если под «хранением» вы имеете в виду постоянство, то мы рассматриваем такие методы, как внешние ключи, в реляционной базе данных. Сопоставление между СУБД и объектами в памяти все чаще управляется хорошими механизмами сохранения, такими как JPA.
c). Аудит? Я не видел, чтобы одитинг применялся специально на уровне отношений. Очевидно, что метод automotive.addDriver () может быть сколь угодно сложным. Если есть бизнес-требование о проведении аудита этого действия, то совершенно очевидно, что это подходящее место для этого. Это просто стандартный вопрос объектно-ориентированного проектирования, связанный с тем, кому принадлежит информация. Общий принцип: «Не повторяйся» настолько ясно, что мы не хотим, чтобы каждый объект, вызывающий addDriver (), нуждался в аудите, следовательно, это работа Auto.
Создание хорошего объектно-ориентированного дизайна - это своего рода искусство , но следует помнить о некоторых принципах.
Подумайте, как пользователи будут использовать ваш интерфейс. Кто ваш пользователь? Это только вы и для определенной цели, или вы разрабатываете что-то для разных клиентов. Дизайн интерфейса сложен, но подумайте о реальных примерах того, как ваши пользователи действительно захотят использовать ваш интерфейс. Один из хороших способов сделать это - написать тесты, которые заставят вас использовать свои собственные материалы. Другой способ - посмотреть на существующие библиотеки, которые делают то же самое, и посмотреть, как они используются.
Держите вещи простыми, глупыми. То есть сделайте их простыми с точки зрения звонящих. Это относится к инкапсуляции, при которой вы должны раскрывать внутреннюю часть своей реализации только по мере необходимости. Это применимо к созданию последовательного интерфейса, который легко понять. А это означает, что вам следует избегать ловушки создания объекта для каждого мыслимого класса, создания множества атрибутов и максимально возможного обобщения.Последний пункт особенно важен; Что делает нас хорошими разработчиками, так это то, что мы способны рассуждать и абстрагироваться, но из-за этого мы часто попадаем в ловушку, делая системы намного сложнее, чем они должны быть.
Подумайте о собственности. Каждый объект будет либо принадлежать другому, что означает, что он должен быть создан и уничтожен как часть этого объекта, либо на него будет ссылаться другой объект. Вы должны соответствующим образом разработать свой интерфейс. Собственность - это форма связи, но зачастую это правильный дизайн. Это, безусловно, упрощает ваш интерфейс, а также снижает нагрузку на вызывающих абонентов по поддержанию их собственных объектов.
Хотя ты должен знать и использовать твою коллекцию библиотек. Знайте библиотеки коллекций любого языка / фреймворка, который вы используете, и используйте их. Кроме того, попробуйте сделать свои собственные интерфейсы совместимыми с этими библиотеками с точки зрения именования и поведения.
Относительно аудита. Вы можете выполнять некоторый аудит (ведение журнала, хранение статистики) из ваших классов, но если ваши потребности в аудите сложны (вам нужно знать, когда ваши данные изменяются), может быть лучше использовать шаблон наблюдателя.
При разработке программного обеспечения я считаю полезным взглянуть на вещи с точки зрения теории типов и посмотреть, к чему это меня приведет.
WorkSpace имеет тип Project + Project ^ 2 + Project ^ 3 ... (то есть все, что true из списка проектов, равно true рабочего пространства)
Аналогично,
Проект имеет тип Ресурс + Ресурс ^ 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 #, но это применимо к другим языкам.