2010-08-23 2 views
0

Скажем, у меня есть следующие классы:Должны ли классы содержать ссылки на объекты, которые их заполняют?

public class BackendObject {} 
public class ObjectA {} 
public class ObjectB 
{ 
    List<ObjectA> Children; 
} 
public class ObjectC 
{ 
    List<ObjectB> Children; 
} 

и еще несколько уровней объектов с детьми. В моем домене эта структура может быть довольно большой, и ее генерация (путем запроса BackendObject) займет слишком много времени, в результате чего ui висит довольно долго. (С ш, я прошу BackendObject для всех ObjectA-х доступен, пользовательский интерфейс знает, как представить каждый из этих объектов)

Я мог бы потенциально добавить BackendObject элемент к каждому из моих объектов и назвать его, когда мне нужно для доступа к дочерним элементам этого объекта, но мне это не нравится, потому что BackendObject населяет эти объекты в первую очередь. Есть ли лучший дизайн, который позволяет каждому объекту получать информацию о своих детях только тогда, когда это необходимо, а не заранее?

ответ

1

В вашей конструкции бэкэнд и объекты, которые он содержит, являются аспектами одного и того же представления, поэтому я не вижу ничего плохого, позволяя им хранить ссылку на объект 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; 
} 
+0

Мне очень нравится ваш второй дизайн, к сожалению, BackendObject напрямую не обращается к базе данных, он получает данные из модели данных, полностью абстрагированные от базы данных. –

+0

@ mvdavid7: Возможно, вы захотите рассмотреть возможность отсечения бэкэнд-класса, и пусть каждый объект напрямую запросит модель данных. Вы можете создавать статические методы для выполнения запросов 'ObjectC', и, кроме того, обрабатывать их одинаково. –

+0

@ mvdavid7: Добавлено еще один вариант для вас. –

0

Рекомендую посмотреть Lazy Loading/Initializationhere). Это в основном способ сделать то, что вам нужно. Статья в Википедии дает достойный пример.

0

Если ссылка на BackEndObject не указана в каждом объекте, откуда бы они к ней обращались? Существует ли глобальный BackendObject?

+0

только пользовательский интерфейс знает об объекте BackendObject, остальные объекты просто хранят данные, которые они заполняли BackendObject. –

0

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

Я бы предложил сделать «Дети» свойства заселить лениво, например:

private List<ObjectA> children; 
public IEnumerable<ObjectA> Children { 
    get { 
    if (children == null) children = backendObject.GetChildren(this); 
    return children; 
    } 
} 

Если вы действительно не хотите, чтобы объекты следить за связанных с ними BackendObject, единственной альтернативой я могу видеть чтобы сделать каждый абонент проходит вокруг объекта бэкэнда каждый раз, например:

public List<ObjectA> GetChildren(BackendObject backend) { 
    return backend.GetChildren(this); 
} 

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

Смежные вопросы