Во-первых, Это могло бы походить на долгий вопрос. Я не думаю, что это... Код является просто обзором того, что я в настоящее время делаю. Это не чувствует себя хорошо, таким образом, я ищу конструктивную критику и предупреждения для ловушек и предложений того, что я могу сделать.
У меня есть база данных с бизнес-объектами.
Я должен получить доступ к свойствам родительских объектов.
Я должен поддержать своего рода состояние через бизнес-объекты.
Если Вы смотрите на классы, я не думаю, что модификаторы доступа являются правильными. Я не думаю его структурированный очень хорошо. Большинство отношений смоделировано с общественными собственностями. SubAccount. Учетная запись. Пользователь. Идентификатор <-все те общедоступен..
Существует ли лучший способ смоделировать отношения между классами, чем это так, это не таким образом "общественность"?
Другая часть этого вопроса о ресурсах:
Если я должен был сделать Пользователя. GetUserList () функция, которая возвращает Список, и у меня было 9 000 пользователей, когда я назову метод GetUsers, это сделает 9 000 Пользовательских объектов и в котором это сделает 9 000 новых объектов AccountCollection. Что я могу сделать для создания этого проекта не так ресурс голодный?
См. ниже код и разорвите его к клочкам.
public class User {
public string ID {get;set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string PhoneNo {get; set;}
public AccountCollection accounts {get; set;}
public User {
accounts = new AccountCollection(this);
}
public static List<Users> GetUsers() {
return Data.GetUsers();
}
}
public AccountCollection : IEnumerable<Account> {
private User user;
public AccountCollection(User user) {
this.user = user;
}
public IEnumerable<Account> GetEnumerator() {
return Data.GetAccounts(user);
}
}
public class Account {
public User User {get; set;} //This is public so that the subaccount can access its Account's User's ID
public int ID;
public string Name;
public Account(User user) {
this.user = user;
}
}
public SubAccountCollection : IEnumerable<SubAccount> {
public Account account {get; set;}
public SubAccountCollection(Account account) {
this.account = account;
}
public IEnumerable<SubAccount> GetEnumerator() {
return Data.GetSubAccounts(account);
}
}
public class SubAccount {
public Account account {get; set;} //this is public so that my Data class can access the account, to get the account's user's ID.
public SubAccount(Account account) {
this.account = account;
}
public Report GenerateReport() {
Data.GetReport(this);
}
}
public static class Data {
public static List<Account> GetSubAccounts(Account account) {
using (var dc = new databaseDataContext()) {
List<SubAccount> query = (from a in dc.Accounts
where a.UserID == account.User.ID //this is getting the account's user's ID
select new SubAccount(account) {
ID = a.ID,
Name = a.Name,
}).ToList();
}
}
public static List<Account> GetAccounts(User user) {
using (var dc = new databaseDataContext()) {
List<Account> query = (from a in dc.Accounts
where a.UserID == User.ID //this is getting the user's ID
select new Account(user) {
ID = a.ID,
Name = a.Name,
}).ToList();
}
}
public static Report GetReport(SubAccount subAccount) {
Report report = new Report();
//database access code here
//need to get the user id of the subaccount's account for data querying.
//i've got the subaccount, but how should i get the user id.
//i would imagine something like this:
int accountID = subAccount.Account.User.ID;
//but this would require the subaccount's Account property to be public.
//i do not want this to be accessible from my other project (UI).
//reading up on internal seems to do the trick, but within my code it still feels
//public. I could restrict the property to read, and only private set.
return report;
}
public static List<User> GetUsers() {
using (var dc = new databaseDataContext()) {
var query = (from u in dc.Users
select new User {
ID = u.ID,
FirstName = u.FirstName,
LastName = u.LastName,
PhoneNo = u.PhoneNo
}).ToList();
return query;
}
}
}
В конечном итоге этот ответ содержит множество заголовков модных слов. Надеюсь, я объясню каждый из них и почему он здесь применим.Я думаю, что каждая концепция, которую я представляю ниже, заслуживает рассмотрения - они не всегда применимы, но я считаю, что это все вещи, которые я лично считаю ценными, когда я думаю о структуре системы.
Начните с размышлений об ответственности каждого объекта - в чем его работа? Как правило, вы найдете лучший дизайн, если выберете одну работу для каждого класса. В настоящее время многие из ваших классов делают слишком много, удерживая логику, которая действительно должна существовать как службы.
Первым примером из вышеперечисленного является ваш класс User:
public class User {
public string ID {get;set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string PhoneNo {get; set;}
public AccountCollection accounts {get; set;}
public User {
accounts = new AccountCollection(this);
}
public static List<Users> GetUsers() {
return Data.GetUsers();
}
}
Почему он предоставляет метод, который извлекает пользователей из источника данных? Эту функциональность следует перенести в сервис для пользователей.
Другим ключевым примером этого является метод GenerateReport в SubAccount - не делайте так, чтобы ваша логика создания отчета была так тесно связана с объектом SubAccount. Разделение этого даст вам больше гибкости и уменьшит количество изменений в вашем субаккаунте, нарушающих логику отчета.
Снова посмотрим на ваш класс User - почему он загружает все учетные записи пользователей при создании экземпляра? Всегда ли эти объекты будут использоваться каждый раз, когда вы работаете с пользователем?
Как правило, было бы лучше ввести ленивую загрузку - извлекать учетную запись только тогда, когда она вам нужна. Конечно, бывают случаи, когда вам нужна активная загрузка (если вы знаете, что объект вам понадобится в ближайшее время, поэтому не хотите сокращать доступ к базе данных), но вы должны иметь возможность спроектировать эти исключения.
Этот вид является следствием как точки ленивой загрузки, так и точки единственной ответственности.У вас есть много жестко закодированных ссылок на такие вещи, как ваш класс Data. Это делает ваш дизайн более жестким - рефакторинг доступа к данным для введения отложенной загрузки или изменение способа получения пользовательских записей намного сложнее сейчас, когда многие классы напрямую обращаются к логике доступа к данным.
Подсказка перед Кейдом Ру - я никогда не слышал термина «Объекты дайджеста», которые обычно называли легковесными DTO.
Как говорит Кейд, нет причин извлекать расширенный список, содержащий полностью функционирующие объекты пользователей, если все, что вы делаете, - это отображение поля со списком имен пользователей, привязанного к уникальным идентификаторам.
Представьте легкий пользовательский объект, который хранит только самую основную пользовательскую информацию.
Это еще одна причина для введения абстракции служб / репозитория и некоторого вида внедрения зависимостей. Изменение типов объектов, извлекаемых из хранилища данных, становится намного проще, если вы инкапсулируете извлечение данных отдельно от ваших реальных объектов и когда вы не привязаны жестко к своей реализации доступа к данным.
Ваши объекты слишком много знают о внутренней структуре друг друга. Разрешение детализации через пользователя к учетным записям, а затем к субаккаунту, сбивает с толку ответственность каждого объекта. Вы можете установить информацию дополнительной учетной записи из объекта пользователя, когда, возможно, этого не следует делать.
Я всегда борюсь с этим принципом, так как изучение иерархии кажется очень удобным. Проблема в том, что это не позволит вам серьезно задуматься об инкапсуляции и роли каждого объекта.
Возможно, не раскрывайте свой объект Account от пользователя - вместо этого попробуйте ввести свойства и методы, которые раскрывают соответствующие элементы объекта Account. Посмотрите на метод GetAccount вместо свойства Account,чтобы вы заставляли себя работать с объектом учетной записи, а не рассматривать его как собственность пользователя.
Хорошо, просто прокомментируйте код в том виде, в каком он у вас есть на данный момент.
public class SubAccount {
public Account account {get; set;} //this is public so that my Data class can access the account, to get the account's user's ID.
public SubAccount(Account account) {
this.account = account;
}
[snip]
}
Вам даже не понадобится сеттер для свойства Account
, если он передается в ctor, а затем назначается для резервного поля.
Установка свойства путем передачи его в ctor может быть очень хорошей практикой, но она не работает, если ваш объект данных будет сериализован, т. Е. Если он будет получен или отправлен в веб-службу - в данном случае В этом случае вам понадобится как минимум внутренний
сеттер для свойства Account
.
Изменить: вы все еще можете использовать это поведение типа DI в конструкторе, но проблема в том, что этот конструктор не будет вызываться, когда объект повторно гидратируется из сериализованной формы (то есть когда он был передан в / из веб-сервис). Поэтому, если вы собираетесь использовать веб-службы, вам дополнительно потребуется конструктор без параметров, а также общедоступный или внутренний сеттер в свойстве Account (если вы собираетесь использовать внутренний сеттер, вам также необходимо указать InternalsVisibleToAttribute
в файле AssemblyInfo.cs .
Ленивая загрузка - не создавайте объекты AccountCollection внутри пользователей, если это не так. доступ. В качестве альтернативы вы можете настроить получение коллекций учетных записей одновременно с пользователями и избежать 1 + 9000 обращений к базе данных.
Используйте более избирательные методы сбора данных (например, что вы собираетесь делать с 9000 пользователей? Если вам нужна вся их информация, это не такая уж большая потеря).
Имеют более мелкие «дайджест» объекты, которые используются для длинных списков, таких как раскрывающиеся списки и поисковые запросы - эти объекты являются простыми бизнес-объектами, доступными только для чтения, обычно содержащие лишь небольшой объем информации и которые могут использоваться для извлечения полностью развернутых объектов.
Если вы выполняете разбиение на страницы, загружается ли список пользователей один раз и сохраняется на веб-сервере, а затем разбивается на страницы из кэшированной копии, или коллекция загружается каждый раз из базы данных и разбивка на страницы выполняется элементом управления?