В вашей конструкции бэкэнд и объекты, которые он содержит, являются аспектами одного и того же представления, поэтому я не вижу ничего плохого, позволяя им хранить ссылку на объект backend.
Если вы хотите изменить дизайн, вместо того, чтобы создать иерархию объектов, вы можете иметь методы на объекте бэкэнда несут ответственность за нахождение детей каждого объекта:
class Backend
{
List<ObjectC> GetObjectCInstances() { /* ... */ }
List<ObjectB> GetObjectBInstances(ObjectC parent) { /* ... */ }
}
Это сделало бы очевидно для что вы выполняете другой запрос каждый раз, когда пытаетесь получить набор детей. Это полезно для тех, кто пытается понять ваш код на более поздний срок, а также очень полезно, чтобы вы могли отключить базовый механизм хранения (либо позже, либо если вы создаете интерфейс IBackend
, во время выполнения - для тестирования цели).
Другой дизайн, который вы могли бы рассмотреть, - это отделить генератор запросов (BackendObject
) от объекта, который действительно выполняет запросы. Пусть BackendObject
заполняет объекты запроса, но не вызывает запросы. BackendObject
затем передает объекты запроса на ObjectC
. ObjectC
может вызвать запрос, когда ему нужно вернуть список своих детей. более
class Query<T>
{
public Query(string query) { this.query = query; }
public List<T> InvokeQuery() { /* ... */ }
private string query;
}
class Backend
{
List<ObjectC> ObjectCInstances
{
get
{
// Do the query for C, since we have to
var result = new List<ObjectC>();
foreach(var instanceData in GetObjectCFromDb)
{
// But let ObjectC query for ObjectB when it needs to
var objectBQuery = new Query<ObjectB>(
"select * from ..."
);
result.Add(new ObjectC(objectBQuery));
}
return result;
}
}
}
Один из вариантов: Использование делегатов (регулярные, анонимные, закрытие и т.д.), чтобы сделать ваше скрытие данных, и избежать передачи ссылки. Таким образом, вы все равно можете выполнять запросы в Backend, но вы можете избежать того, чтобы C/B/A знал, что существует Backend.
class Backend
{
public List<ObjectC> ObjectCInstances
{
get
{
var result = new List<ObjectC>();
foreach(var cInstance in DoQueryForC())
{
result.Add(new ObjectC(GetObjectBFromDb));
}
return result;
}
}
private List<ObjectB> GetObjectBFromDb(ObjectC parent)
{
var result = new List<ObjectB>();
foreach(var instanceData in DoQueryForB(parent))
{
result.Add(new ObjectB(GetObjectAFromDb));
}
return result;
}
private List<ObjectA> GetObjectAFromDb(ObjectB parent)
{
var result = new List<ObjectA>();
foreach(var instanceData in DoQueryForA(parent))
{
result.Add(new ObjectA());
}
return result;
}
}
class ObjectC
{
internal ObjectC(
Func<ObjectC, List<ObjectB>> queryForBChildren
)
{
this.queryForBChildren = queryForBChildren;
}
public List<ObjectB> Children
{
get
{
return queryForBChildren();
}
}
Func<ObjectC, List<ObjectB>> queryForBChildren;
}
Мне очень нравится ваш второй дизайн, к сожалению, BackendObject напрямую не обращается к базе данных, он получает данные из модели данных, полностью абстрагированные от базы данных. –
@ mvdavid7: Возможно, вы захотите рассмотреть возможность отсечения бэкэнд-класса, и пусть каждый объект напрямую запросит модель данных. Вы можете создавать статические методы для выполнения запросов 'ObjectC', и, кроме того, обрабатывать их одинаково. –
@ mvdavid7: Добавлено еще один вариант для вас. –