Felix
Felix

Reputation: 620

OOP Best Practice for Objects containing other objects

I keep struggling with the best way to implement this. Let's say I have two objects, one is a user object the other is a note object. The Note object will hold notes for a patient and it will store who created the note and who it was last updated by. What is the better option:

Option A - Two objects, one with just the Note specific info and the other an extension of this object with the User objects for patient/author/updater:

public class Note {
    private int noteId;
    private int patientId;
    private int authorId;
    private int updatedById;
    private String noteText;
    private Date noteDate;
}

public class NoteExt extends Note {
    private User patient;
    private User author;
    private User updater;
}

Option B - One Note object with User classes for patients, author, updater

public class Note {
    private int noteId;
    private User patient;
    private User author;
    private User updater;
    private String noteText;
    private Date noteDate;
}

Option A would bloat the application with a ton of files for the additional objects/db calls but would speed things up when getting lists of items. (Also is it correct to use extend in this case?)

Option B would mean not worrying about remembering which object to use when and only having one database function, but would repeat data if, for example, I were getting a list of all notes a user has created.

Is there an option C that's better??

Upvotes: 2

Views: 313

Answers (6)

Nathan Hughes
Nathan Hughes

Reputation: 96394

B is the usual way to do this, JPA has anticipated the problem:

  • The EntityManager acts as an identity map and makes sure you don't have more than one copy of an object at a time, so that if you retrieve a list of notes by the same user, those will all be references to the same User object.

  • the references to the other objects are lazy by default, you are getting back only a proxy that contains the ID, making the overhead equivalent to A.

Upvotes: 0

Marcin Szymczak
Marcin Szymczak

Reputation: 11433

Option B is correct. You should not think how data will be persisted when you model objects.

Upvotes: 2

Zak
Zak

Reputation: 25205

It seems like you are confusing the object design with the database design. It's understandable since you are creating objects that need to be represented in a database. However, I found that in many cases, it's easiest to "forget" the database and just design your objects, then come back and create mappers for your objects using the model-mapper design pattern, adding unique id's as needed.

So you use option B, BUT for storing/loading to/from the database, you use separate mapper classes: i.e. UserMapper, NoteMapper, etc... The secret then is that the mapper uses an IdentityMap, so that when things are getting loaded by ID, a locally cached copy of an object is used rather than going to the database over and over to get the same user object for a userID (for instance).

public class Note {
    private int noteId;
    private User patient;
    private User author;
    private User updater;
    private String noteText;
    private Date noteDate;
}

public class UserMapper {
    public User getUserById(int userId){
        User user = UserIdentityMap.getUser(userId);
        if(user == null) {
            user = fetchUserFromDb(userId);
            UserIdentifyMap.cache(userId, user);
        }
        return user;
    }
}

Upvotes: 2

sgbj
sgbj

Reputation: 2274

Creating a relationship like that based on information you're retrieving from a database, especially maintaining such a relationship, and also ensuring referential integrity so that changes to one item propagate to the rest of them (if that's something you want, otherwise you run the risk of recreating numerous copies of the same record if you don't enforce it) can get pretty complicated.

It's probably just me, but the NoteExt class seems like it's kind of abusing inheritance here. The first class seems perfectly fine and simple to me, and I like simple. You'll probably have some database context class that the application can use to find a user or some other record based on its ID (e.g., Database.userForId(int)). To me it makes sense to keep things separated and simple by offering just the essentials, but still provide a way to easily request more information about a record.

As others have mentioned, there's several preexisting alternatives (e.g., Hibernate and JPA) which can map database objects for you and create the kind of relationships you desire.

To answer your question below, I think it's important to note that the ORMs we create aren't necessarily going to be the models we provide to our views--don't get me wrong, it'd be great if it were always that easy, but as you said, sometimes they require a bit more information (that might not even be related to the records from the database, but to the views themselves). In C#'s MVC framework we face a similar problem which a lot of us overcome by creating separate models specifically for our views, much like a DefaultListModel maintains a vector of data to be displayed in a JList (but the model itself doesn't extend Vector or any other collection). The new view model you create can then contain all of the necessary bits of information you'd like to display in the view, and also provide other methods for interacting with it and modifying it.

Upvotes: 1

Bart
Bart

Reputation: 17361

Plan C could be something like this. Creating all objects as separate entities with one composite. Making the note more reusable for other applications.

public class Note {
    private int noteId;
    private String noteText;
    private Date noteDate;
}

public class PatientNote {
    private Note note;
    private User patient;
    private User author;
    private User updater;
}

Upvotes: 2

Peter Lawrey
Peter Lawrey

Reputation: 533530

I wouldn't base the DB on your data structures directly. How your arrange the data in Java and how you arrange it in the DB don't have to be the same and what makes sense for one may not make sense for the other.

Upvotes: 3

Related Questions