Этот ответ содержит много заголовков модных слов. Надеюсь, я объясню каждый и почему это применимо здесь. Я думаю, что каждая концепция, которую я представляю ниже, заслуживает рассмотрения - они не всегда применимы, но я считаю, что это все те вещи, которые лично мне кажутся ценными, когда я думаю о структуре системы.
Одиночная ответственность
Начните с размышлений об ответственности каждого объекта - какова его работа? Как правило, вы найдете лучший дизайн, если выберете одну работу для каждого класса. В настоящее время многие ваши классы делают слишком много, сохраняя логику, которая действительно должна существовать в виде сервисов.
Первый пример приведенного выше - ваш класс пользователя:
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. Это делает ваш проект более жестким - рефакторинг доступа к данным для введения отложенной загрузки или изменение способа получения пользовательских записей теперь намного сложнее, поскольку многие классы имеют прямой доступ к логике доступа к данным.
Дайджест объектов
Шляпа для Cade Roux - я никогда не слышал термин «дайджест-объекты», обычно их называли легковесными DTO.
Как говорит Кейд, нет смысла извлекать расширенный список, содержащий полностью функционирующие пользовательские объекты, если все, что вы делаете, это отображаете поле со списком имен пользователей, которое связано с уникальными идентификаторами.
Представьте легкий пользовательский объект, в котором хранится только самая основная пользовательская информация.
Это снова еще одна причина для введения абстракции сервисов / репозитория и своего рода внедрения зависимостей. Изменение типов объектов, извлекаемых из хранилища данных, становится намного проще, когда вы инкапсулировали извлечение данных от реальных объектов и когда вы не тесно связаны с реализацией доступа к данным.
Закон Деметры
Ваши объекты слишком много знают о внутренней структуре друг друга. Разрешение перехода от пользователя к учетным записям, а затем к вспомогательному счету затрудняет ответственность за каждый объект. Вы можете установить информацию о субсчете из пользовательского объекта, когда, возможно, этого не следует делать.
Я всегда борюсь с этим принципом, так как бурение через иерархию кажется очень удобным. Проблема в том, что это заставит вас не задумываться о инкапсуляции и роли каждого объекта.
Возможно, не выставляйте объект Account у пользователя - вместо этого попробуйте ввести свойства и методы, которые раскрывают соответствующих членов объекта Account. Взгляните на метод GetAccount вместо свойства Account, чтобы заставить себя работать с объектом account, а не рассматривать его как свойство пользователя.