tags:

views:

62

answers:

4

Say I have the following classes:

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

and several more levels of objects with children. In my domain, this structure could be quite large, and generating it all (by querying the BackendObject) would take too long, leaving the ui hanging for quite a while. (From the ui, i ask the BackendObject for all the ObjectA's available, the UI knows how to represent each of these objects)

I could potentially add a BackendObject member to each of my objects and call it when i need to access the children of that object, but i don't like that because BackendObject populates these objects in the first place. Is there a better design that allows each object to get its children information only when needed instead of upfront?

A: 

I recommend looking up Lazy Loading / Initialization (and here). It's basically the way to do what you require. The wikipedia article does give a decent example.

Kyle Rozendo
A: 

If a reference to BackEndObject is not contained in each object, from where to they access it? Is there a global BackendObject ?

James Curran
only the UI knows about the BackendObject, the rest of the objects are purely holding the data that they were populated with by the BackendObject.
David A.
+1  A: 

In your design, the backend and the objects it holds are all aspects of the same representation, so I don't see anything wrong with allowing them to hold a reference to the backend object.

If you want to change your design, instead of creating an object hierarchy, you could have methods on the backend object are responsible for finding children of each object:

class Backend
{
  List<ObjectC> GetObjectCInstances() { /* ... */ }
  List<ObjectB> GetObjectBInstances(ObjectC parent) { /* ... */ }
}

This would make it obvious to the caller that you are performing another query each time you try to get the set of children. This is useful for anyone trying to understand your code at a later date, and is also very useful to allow you to switch out the underlying storage mechanism (either at a later date, or if you create an interface IBackend, at runtime - for test purposes).

Another design you could consider is separating the query generator (BackendObject) from the object that actually fetches the queries. Let BackendObject populate query objects, but not invoke the queries. BackendObject then passes the query objects to ObjectC. ObjectC can invoke the query when it needs to return its list of children.

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;
    }
  }
}

One more option: Use delegates (regular, anonymous, closures, etc) to do your data hiding, and avoid passing references. This way, you can still do the queries in Backend, but you can avoid letting C/B/A know that Backend exists.

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;
}
Merlyn Morgan-Graham
I really like your second design, unfortunately the BackendObject is not talking to the database directly, it's getting the data from a data model, completely abstracted from the database.
David A.
@mvdavid7: You may want to consider ditching the backend class entirely, then, and let each object query the data model directly. You can make static methods to perform the `ObjectC` queries, and besides that treat it the same way.
Merlyn Morgan-Graham
@mvdavid7: Posted one more option for you.
Merlyn Morgan-Graham
@mvdavid7: And if you don't want your queries to be executed over and over again, add the method that Stuart suggested to cache the results of the queries.
Merlyn Morgan-Graham
@Merlyn Morgan-Graham Thanks for all your suggestions. This made me realize that, like you originally said they "are all aspects of the same representation" and that trying to hide the BackentObject will bring a lot of complexity for little gain.
David A.
A: 

I think that having each object "remember" the BackendObject that created it is fine, as long as you are clear (in code comments, or whatever) that conceptually the BackendObject "owns" the objects that reference it, rather than the other way around.

I'd suggest making the "Children" properties populate lazily, eg:

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

If you really don't want to have the objects keep track of their associated BackendObject, the only alternative I can see is to make every caller pass around the backend object every time, eg:

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

The downside of that approach, apart from making life harder on your callers, is that you call the backend every time the children are accessed, rather than remembering them after the first time they are accessed on each individual object.

Stuart