6

I am trying to figure out the best design for instantiating an object which requires two separate calls to the data layer through a DAO. The object is not usable until these calls have been made (because it requires the data retrieved from the database). I have come up with three possible solutions:

  1. Create a method in the DAO itself that gets the necessary data from the database, and then pass the data to the object's constructor. The object is passed back as the return value.
  2. Create a method in the class that is using the object. This method would make both calls to public methods in the DAO, then instantiate and return the object as in step 1.
  3. Pass the DAO as a parameter to the constructor of the object. The constructor would then make both necessary calls to the DAO, setting its own member variables without the need for Setters. The DAO is not used other than in the constructor.

Examples, if SomeObject is the object I want to create, and Foo is the class that will make use of it:

1 - DAO Contains the logic:

public SomeObject getSomeObject(){ // this DAO method called by Foo class
     data1 = getData1();
     data2 = getData2();
     return new SomeObject(data1,data2);
}

2 - Class that uses object contains the logic:

public class Foo {
    ...
         public void useSomeObject() {
              SomeObject obj = createSomeObject();
              obj.doStuff();
         }

         private SomeObject createSomeObject() {
              data1 = dao.getData1();
              data2 = dao.getData2();
              SomeObject someObject = new SomeObject(data1,data2);
              return someObject;
         } 
}

3 - SomeObject takes DAO as parameter and uses it in constructor:

public class SomeObject {
...
      public SomeObject(DAO dao) {
           data1 = dao.getData1();
           data2 = dao.getData2();
      }
...
 }

Hopefully these examples are sufficient to convey my point. Are any of these solution considered better design than the others? If not, what solution is considered best practice?

Thanks.

  • I guess I was under the assumption that having a constructor block on database I/O was considered a bad practice. – Wallace Brown Aug 6 '14 at 20:37
  • 1
    I have mixed feelings about it. Your DAO object would have to have the same "shape" as the object you're instantiating. That's a lot of coupling, and objects aren't supposed to know anything about the way they are being persisted. On the other hand, you can't completely decouple anyway; data always has a specific shape. Normally I use Repository objects or a Service Layer for this sort of thing. – Robert Harvey Aug 6 '14 at 20:38
  • @RobertHarvey Isn't DAO an abstraction ? – Tulains Córdova Aug 6 '14 at 20:41
  • Your DAO object is going to be different for each object you're trying to instantiate, unless you have something like a DataContext, but that's going to give you hydrated objects already anyway. See also msdn.microsoft.com/en-us/magazine/dd882510.aspx – Robert Harvey Aug 6 '14 at 20:42
  • 2
    @RobertHarvey Could you elaborate your comment into an answer ? I would benefit from it since I usually put the DAO inside the object, knowing that I'm doing it wrong. For example elaborate about DataContext, Repository objects, Service layer and hydrated objects. – Tulains Córdova Aug 6 '14 at 20:43
4

In classical OOP, the notion of encapsulation embodies the idea of combining data with code; that is, providing methods that act specifically on the data they represent. So, for example, a Customer object contains only those methods which pertain specifically to customer "behavior."

Persisting objects is an orthogonal concern.

The Customer object should not know anything about how it is being saved or retrieved from a database, XML file, or whatever (the concept is called Persistence Ignorance). This is why the preferred approach, especially in a large system, is to utilize some generalized persistence mechanism like an Object Relational Mapper to store and retrieve objects, and then to draw that functionality out through Repository methods that return objects or perform some business action on the data.

This relieves you of the responsibility of having to code a persistence implementation for each and every class you create.

Further Reading
The Unit Of Work Pattern And Persistence Ignorance

  • I thought the object already followed Persistence Ignorance by using a DAO since the DAO itself has the logic of how persistence works. – Wallace Brown Aug 6 '14 at 21:05
  • But it still refers to a DAO object with persistence knowledge. An ORM can handle all those business objects without the BO ever knowing anything about the ORM or its underlying persistence mechanism. And because you get persistence for free, you don't have to code up all of those constructor overloads. – Robert Harvey Aug 6 '14 at 21:08
  • Ok, so it sounds to me like the BO (assuming you mean something like Basic Object) doesn't know about the DAO at all, it just accepts the needed data as its arguemnts. The ORM has a method that returns the BO. The logic in that method makes use of the appropriate DAO to get the data needed to pass in to the BO's constructor. Does that sound right? – Wallace Brown Aug 6 '14 at 21:31
  • Exactly right. BO == Business Object; your Customer class or something similar. – Robert Harvey Aug 6 '14 at 21:33
  • @Wallace Brown I actually thought the BO needs to know about the DAO. In an ORM model, the BO would need to configure itself to conform to some standard used by the ORM. For example, one can annotate a BO object to indicate persistence like Java JPA, or one can use xml to specify persistence. The BO doesn't know about the implementation of the DAO, but needs to know about the specification of the DAO is what I have in mind. – InformedA Aug 7 '14 at 5:48
3

Your approach in example 1, make the DAO call the constructor, gets very close to the Repository Pattern.

However, it makes more sense to have an actual repository do the coordination.

class SomeObjectRepository {
  SomeObjectDAO sod;
  ...
  SomeObject getSomeObject() {
    return new SomeObject(sod.getData1(), sod.getData2());
  }
}

The DAO gets bound to the repository (a singleton) when it is created. Foo then calls the repository method to get the object.

This way, the object becomes decoupled from the DAO, the DAO performs only basic operations and they are more easily tested with respect to each other.

  • This would be my choice as well. – Robert Harvey Aug 6 '14 at 20:47
  • Just to make sure I understand what you're saying here: It looks like the Repository object is used to hold some number of DAOs. These DAOs are there to interact with specific classes, such as my SomeObject class. The Repository has the logic needed to tell which data is needed from which DAOs. Does that seem about right? – Wallace Brown Aug 6 '14 at 20:59
  • My understanding is that you may well have a repository object responsible for one or more classes. The repository groups the DAOs of interest. Hence, you might have a SomeObject repository with several DAOs, a Permissions repository that is responsible for users/groups/roles/access rights, etc. – BobDalgleish Aug 6 '14 at 21:36
2

I would go with your third option. It allows you to pass whatever DAO you want, and that could be very useful for testing. It also makes your code a bit more flexible: in case the details of data retrieval change, you only have modify the DAO class, and SomeObject should work more or less the same.

The first option isn't that bad. The data retrieval methods are within SomeObject so now your class is a business object and a DAO.

The second option could lead to a situation where createSomeObject gets copied and pasted around, as the need to create a SomeObject increases and spreads throughout your application (I know, it might not happen, but it might). And when that happens, you'll wish you had the data retrieval code in a central place, such as the constructor for SomeObject, which is where you started.

  • 1
    I like the point about being able to pass a mock DAO for unit tests. Right now I'm planning on going with the third option. My concern was that I'd have an object blocking on the database in its constructor, which might be considered bad practice. – Wallace Brown Aug 6 '14 at 20:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.