Бизнес-объекты и коллекции C #

Мне трудно разобраться в бизнес-объектах или, точнее, в коллекциях бизнес-объектов.

Вот краткий пример того, что я пытаюсь сделать.

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

Если у меня есть Public List ‹Note> notes = new List ‹Note> (), тогда такие методы, как ADD и REMOVE, становятся доступными для человека в инциденте. Я предполагаю, что если бы я вызвал эти методы в коллекции Notes, он просто удалил бы его из списка, но не выполнил бы какой-либо код для фактического добавления / обновления / удаления сотрудника из источника данных. Это заставляет меня думать, что я должен использовать не List, а что-то еще?

Это также подводит меня к другому вопросу. Где должны находиться фактические операции с базой данных CRUD. Должен ли объект Note иметь собственный CRUD или объект Person должен отвечать за него, поскольку он не может существовать без него?

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


person Mathew    schedule 20.01.2010    source источник
comment
Отличный вопрос, связанный с ООП, определенно поможет другим +1!   -  person JonH    schedule 20.01.2010


Ответы (3)


Была дана отличная информация, но вы упомянули одну вещь, которая может вас сбить с толку:

«Если у меня есть Public List notes = new List (), тогда такие методы, как ДОБАВИТЬ, УДАЛИТЬ, станут доступны человеку в инциденте».

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

Это звучит примерно так:

  • В одном инциденте может участвовать много людей
  • Один человек может создавать много заметок
  • Заметка - это самый низкий уровень, и она существует из-за создания инцидента и ответственных лиц, работающих над этим инцидентом.

Инцидент 1 - много лиц

Человек 1 - много заметок

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

Например

public class Incident {
//insert incident fields here
//do not add person logic / notes logic
//probably contains only properties
}

public class Person {
//insert person fields
//private members with public properties
//do not embed any other logic
}

public class Comment {
 //insert comment private fields
 //add public properties
 //follow the law of demeter
}

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

public class IncidentPersonnel {
List<Person> p;
//add methods to add a person to an incident
//add methods to remove a person from an incident
....
}

Тогда у вас может быть другой класс, обрабатывающий комментарии персонала.

public class PersonnelNotes {
List<Note> n;
//other methods...
}

Вы можете пойти дальше, но это может усложнить ситуацию, но я просто даю вам еще одно представление о том, как с этим справиться.

Постарайтесь следовать закону Деметры для функций

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

Наконец, вы упомянули, как должны работать операции CRUD. Все это восходит к вашему DAL (уровень доступа к данным). Вместо того, чтобы возвращать строки данных из таблицы, вы могли бы затем вернуть объект, на который имеется ссылка, со всеми его атрибутами. Добавление и удаление работают одинаково (передача или передача объекта). Вы можете использовать ORM или написать свой собственный DAL. Все зависит от того, насколько вы хотите участвовать :).

person JonH    schedule 20.01.2010
comment
Замечу, что это всего лишь один из способов справиться с этим. Тем не менее, это может иметь смысл для других разработчиков. - person JonH; 20.01.2010
comment
Отличная информация, спасибо. У меня есть последний вопрос. Список ‹Note› в PersonnelNotes в настоящее время является приватным. Если бы мне нужен список заметок для этого человека, я бы вернул какой-нибудь король списка только для чтения, и если бы я вызвал DeleteNote на PersonnelNotes, он вызвал бы DAL для его удаления, а затем перестроил бы список только для чтения? - person Mathew; 20.01.2010
comment
Определенно должно быть приватным, если вы хотите удалить заметку из списка, вы можете сделать n.Remove (YourIDRepresentationOfANote). Даже если список не предназначен только для чтения, вы всегда можете сделать его доступным только для чтения в любое время. После того, как вы удалите его из списка, вы можете вызвать свой DAL, передав IDentifier заметки, чтобы удалить его из базы данных. - person JonH; 20.01.2010
comment
Хорошо, я думаю, я понял. Обратите внимание, что для этого необходимо иметь поле частного идентификатора, которое можно прочитать с помощью свойства get (но не установлено для защиты). Для этого потребуется Reflection, чтобы установить поле идентификатора, когда оно загружается DAL? - person Mathew; 20.01.2010
comment
Это был бы один из способов сделать это. Если вам нужен другой вариант, вы можете сделать следующее. Вместо n.remove (yourID) (удаление элемента из списка) вы можете просто вызвать DAL для воссоздания списка заметок. Это означает, что DAL обработает удаление, создаст временный список и перезагрузит его со всеми примечаниями только для этого человека, а затем вернет его обратно в ваше приложение как вновь созданный список. Так много вариантов, что в конечном итоге это зависит от вашего уровня комфорта. - person JonH; 20.01.2010
comment
Извините, я имел в виду, что, поскольку заметки извлекаются из источника данных, необходимо установить свойство уникального идентификатора. Поскольку я не хочу, чтобы идентификатор был редактируемым, для этого потребовалось бы получить свойство, а не установить, но мне все равно понадобится возможность установить его изначально. Затем я просто передал бы всю заметку для удаления / обновления в DAL, который прочитал бы идентификатор и точно знал, какая запись требует удаления / обновления. РЕДАКТИРОВАТЬ: Хорошо, мне не нужно, чтобы идентификатор был виден вообще, он просто внутренний для объекта, но его все равно нужно как-то установить. - person Mathew; 20.01.2010
comment
Мэтью, не путайте себя с тем, что объект списка содержит, скажем, только один столбец. Помните, что список может содержать любой объект, это просто указатель на объект. Итак, если у заметок есть тридцать свойств внутри класса Notes, вы все равно можете добавить один объект заметки внутри списка и затем передать его в свой DAL, который затем может сказать Object.Property. Например, Мэтью m = новый Мэтью (); Список ‹Mathew› l = новый Список ‹Mathew›; l.Add (m) ;. Затем, когда вы хотите удалить, скажите элемент списка Mathew m = l.Find (m = ›m.ID = IDtoFind); Не беспокойтесь о значении идентификатора, просто сделайте его набор закрытым и сделайте его общедоступным. - person JonH; 20.01.2010
comment
Извините, мне пришлось создать еще один комментарий (превышен лимит), поэтому, если у вас есть объект в этом случае: Mathew m = l.Find (m = ›m.ID = IDtoFind); тогда вы можете скажите l.Remove (m); то есть удалить объект m из списка объектов mathew. Свойство ID может иметь общедоступное получение и частный набор, вы устанавливаете идентификатор изначально в конструкторе объекта заметок. Это поможет? - person JonH; 20.01.2010
comment
ДжонХ, я, может быть, путаю здесь проблему. Часть идентификатора поступает из базовой базы данных. Столбец ID - это уникальный идентификатор для этой заметки в таблице заметок. Если на моем уровне доступа к данным я хотел бы удалить конкретную заметку, мне нужно было бы знать ее идентификатор. Итак, я предполагаю, что если бы мой DAL собирался получить список заметок для человека с помощью SELECT * FROM Notes WHERE PersonID = 43, это могло бы получить записи заметки 4,5 и 8. Это дало бы 3 объекта Note, возвращенных в виде списка Примечания. Когда я удаляю примечание 3 (ID 8), DAL выполнит DELETE FROM Notes WHERE NoteID = 8. Это настройка и защита ID. - person Mathew; 20.01.2010
comment
Вы устанавливаете NoteID в конструкторе заметки, свойство set является частным (только для чтения). - person JonH; 20.01.2010
comment
Спасибо, ДжонХ. Думаю, я все еще думал о том, что Note имеет возможность хранить себя в базе данных. Я был обеспокоен тем, что, имея идентификатор в конструкторе, можно было бы создать новый экземпляр заметки с любым старым идентификационным номером и затем сохранить его, но поскольку управление добавлением или удалением заметки будет находиться в разделе «Примечания к персоналу» ( если я правильно понимаю) и, в конечном итоге, это DAL, не должно быть возможности добавления случайной заметки без родителя. Это правильно? - person Mathew; 20.01.2010
comment
Когда вы создаете экземпляр заметки внутри конструктора, вы устанавливаете его идентификатор (возможно, это возвращаемое значение из вашего DAL). Итак, вы установили это там. Ваш объект Note может все еще существовать, как только вы создали объект заметки, который вы связываете с человеком (для этого вы просто добавляете объект заметки в список заметок внутри класса personnotes). Помните, что вам не нужно копировать эту реализацию, я просто предлагаю вам идеи. Не слишком беспокойтесь об установке идентификаторов, вы не открываете исходный код для сообщества stackoverflow, я предполагаю, что это внутренний проект. Не нужно строго его оформлять. - person JonH; 20.01.2010

У вас здесь несколько разных вопросов, я постараюсь ответить на большинство из них.

Что касается проблем с использованием List<T> - в структуре есть ReadOnlyCollection<T>, то есть полезно именно в вашей ситуации. Это коллекция, которая не позволяет добавлять или удалять после создания.

Что касается ответственности за операцию CRUD - она ​​должна принадлежать вашему уровню данных, а не каким-либо из ваших объектов (см. SRP - Принцип единой ответственности).

person Oded    schedule 20.01.2010
comment
Вы также можете преобразовать List ‹T› в режим только для чтения, используя метод AsReadOnly () - msdn.microsoft.com/en-us/library/e78dcd75.aspx - person Dan Diplo; 20.01.2010
comment
Значит, для каждого бизнес-объекта потребуется отдельный объект доступа к данным? В объекте Person все еще будет что-то вроде AddNote, RemoveNote, и при вызове они будут указывать DAL на выполнение работы? - person Mathew; 20.01.2010
comment
Класс Person не должен иметь никаких сведений о заметке. Он знает только о человеке, и это все, что его должно волновать. Смотрите мой пост для получения дополнительной информации. - person JonH; 20.01.2010

Я делаю это так: каждый объект, у которого есть дочерние объекты, содержит их список, а каждый объект с родительским объектом содержит свойство с его типом. Добавление выполняется путем заполнения объекта (или иерархии объектов) и отправки в DAL для сохранения, если это необходимо. Все операции CRUD находятся в DAL, который не зависит от типов объектов, но использует такие типы для определения, к каким таблицам, столбцам и т. Д. Необходимо получить доступ. Удаление - это единственное, с чем можно справиться по-другому, установив свойство Deleted объекта, которое запускает DAL для его удаления.

Теперь что касается бизнес-логики - она ​​не находится в самих объектах (DAO), а выполняется классами, которые получают или собирают такие DAO, когда это необходимо, выполняют свою работу и отправляют DAO обратно в DAL для обновлений.

person Otávio Décio    schedule 20.01.2010