Чрезмерно доступный и невероятно ресурс голодные отношения между бизнес-объектами. Как я могу зафиксировать это?

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

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

Если Вы смотрите на классы, я не думаю, что модификаторы доступа являются правильными. Я не думаю его структурированный очень хорошо. Большинство отношений смоделировано с общественными собственностями. 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;
     }
  }

}
5
задан David Hall 2 April 2010 в 03:18
поделиться

3 ответа

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

Единая ответственность

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

Первым примером из вышеперечисленного является ваш класс 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,чтобы вы заставляли себя работать с объектом учетной записи, а не рассматривать его как собственность пользователя.

3
ответ дан 14 December 2019 в 19:08
поделиться

Хорошо, просто прокомментируйте код в том виде, в каком он у вас есть на данный момент.

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 .

0
ответ дан 14 December 2019 в 19:08
поделиться

Ленивая загрузка - не создавайте объекты AccountCollection внутри пользователей, если это не так. доступ. В качестве альтернативы вы можете настроить получение коллекций учетных записей одновременно с пользователями и избежать 1 + 9000 обращений к базе данных.

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

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

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

2
ответ дан 14 December 2019 в 19:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: