reset password

Design Model Classes

In this document we will discuss how to design model classes. We will use an example taken from Homework 2 and 3 from CS520 Fall 2011, and we will mimic Suze Orman's writing style in her book The Road to Wealth by explaining the design process with a series of questions and answers.

Sample Requirements

Suppose we want to design model classes to add rubric support to CSNS.

A rubric, according to Merriam-Webster, is "a guide listing specific criteria for grading or scoring academic papers, projects, or tests". For example, the department  program assessment page lists five rubrics that are used to evaluate the students in several areas.

Create new model classes and/or modify existing ones based on the following requirements:

  • Create a Rubric class that holds the information about a rubric. This class should be in the csns.model.assessment package.
  • A rubric has a name, a rating scale which is either 1-3, or 1-4, or 1-5, and a number of criteria.
  • Each criterion has a name, and for each rating value, a description of the performance quality that warrants the rating.
  • One or more rubrics can be associated with an assignment. For example, we may associate an Oral Communication rubric to the Midterm Presentation assignment in CS520. The instructor grades a student's work for the assignment by giving a rating to each criterion in each rubric associated with the assignment. The ratings for a rubric is stored in a class RubricScore in the csns.model.assessment package.

Design

Q. How do we design the model classes?

Class design usually takes two steps: identify the entity classes and their fields (or in the terminology of the Entity-Relationship Model, the entity sets and their attributes), and then add the associations between classes (or in the ER terminology, the relationships).

Q. So how do we identify the entity classes and their fields?

Usually the nouns in the requirement description are your classes and fields. In our example, they are "rubrics", "rubric name", "rating scale", "criterion", "criterion name", "description", "ratings", "rubric score".

Most of the time which noun should be a class and which one should be a field are pretty clear. For example, rubric, criterion, and rubric score are clearly classes because they contain other things, while name, description, ratings are clearly fields because they are of simple types (i.e string, number, or date). However, sometimes things are not that cut and dry. For example, rating scale in our example could be either a class or a field:

Design A. Create a RatingScale class and add a RatingScale reference to the Rubric class.

Design B. Create a minRating and a maxRating field in the Rubric class. Because minRating is always 1, we could actually drop it and only keep the maxRating field for rating scale.

The advantage of Design A is that it's easier to ensure that a rubric only uses a valid rating scale (1-3, or 1-4, or 1-5) because on the OO side a Rubric object must reference an existing RatingScale object, and on the database side there's a foreign key constraint. For Design B, it's possible that a user sets the value of maxRating to something other than 3, 4, or 5. With that said, I actually prefer Design B for its simplicity. Design A creates one more class, one more table, and requires some additional code when creating a Rubric object. Design B is quite a bit simpler, and can still ensure the validity of the rating scales with a little care to the UI (e.g. let the user select a rating scale from a list instead of entering it in a text field) and a check() constraint in the database.

Q. What's the difference between a component class and an entity class?

A component class is basically some fields from an entity class grouped into their own class. An entity class is mapped to a table in database, while a component class is mapped to some columns in the table of the entity class it is embedded to. A classical example of component class is the Address class we discussed during lecture.

Personally I think component classes bring little benefit so don't use them.

Q. How do we decide the types of the class fields?

There are a couple of decisions to made when you decide the type for a field:

  • Use a class type (e.g. Integer, Boolean) or a primitive type (e.g. int, boolean)
  • Which collection type to use: list, set, map, or array

The main difference between class types and primitive types is that class types allow null values, while primary types always have a default value. So for fields that could be null, use class types, and for fields that cannot be null, use primitive types (and add an annotation @Column(nullable=false) so you have a not-null constraint on the database side). Note that you should always use class type for Id fields because ORM uses whether id is null to determine whether an object is new.

As for collections, the main difference between lists and sets is that lists keep the order of the elements while sets do not. You probably would want to use lists over sets most of the time because order is usually important, even just for display purpose. You may want to use a set if the order is really not important and you want fast element-in-collection check (HashSet lets you do element-in-collection check in O(1)).

Between list and array, use list if the number of elements in the collection may change.

Personally I always feel mapping a map to database is a little weird, so I'd recommend against using maps unless you feel the application demands it.

Q. So how do the classes look like after we complete the first step of the design?

Here they are:

Rubric Criterion RubricScore
Long id
String name
int ratingScale
List<Criterion> criteria
User author
Date timestamp
Long id
List<String> qualityDescriptions
 
Long id
List<Integer> ratings

Note that we have some additional fields that are not in the requirement description. This is not uncommon because most of the time your clients can only give you a rough requirement description and you are supposed to fill in some missing pieces. In our example we added the Id fields because Hibernate/JPA wants them and it's a good practice to have an integer primary key column. The reason we chose Long instead of Integer is because Hibernate creates new ids from the same PosgreSQL sequence, so the id values could get quite large. We added the author field because we'll need the information to implement security measures, and a timestamp field is always useful for things like sorting the rubrics from the latest to the oldest and so on.

Q. How do we add the associations?

The second step of the design process is to add the associations to the entity classes we identified earlier. This step involves several things:

  • Identify the associations between classes
  • Determine the association type, i.e. one-to-many, many-to-many, and so on.
  • Decide whether to use bi-directional association or uni-directional association

Q. What are the associations in our example?

Our example is a little unusual in the sense that not all entity classes are mentioned in the requirement description. Specifically, we need to add User, Assignment, and Submission class into the mix.

As for the associations:

  • Rubric and Criterion are clearly associated.
  • Rubric and User are associated in the authorship relationship.
  • Rubric and Assignment are associated because the requirement description says so.

The associations involving RubricScore are a bit tricky. Note that a RubricScore includes the ratings for a particular student on a particular rubric of a particular assignment, which means that RubricScore should be associated with User, Rubric, and Assignment. Normally this would be the end of the story, but if you take a look at the Submission class, you'll see that Submission includes references to both student and assignment, and in fact it'd be a perfect place to store RubricScores because they are just like submission grades, so instead of associating RubricScore to User and Assignment separately, we can simply associate it with Submission.

To summarize, here are the associations:

  • Rubric and Criterion
  • Rubric and User
  • Rubric and Assignment
  • RubricScore and Rubric
  • RubricScore and Submission

Q. What are the types of the associations?

  • Rubric and Criterion: one-to-many

You could make them many-to-many, which would mean the same criterion can be used in multiple rubrics - although it sounds like a reasonable design, it's actually not, because when a user edits a criterion, he or she may unknowingly change other rubrics that share the same criterion, and that's not good.

  • Rubric and User: many-to-one because a rubric only has one author.
  • Rubric and Assignment: many-to-many
  • RubricScore and Rubric: many-to-one
  • RubricScore and Submission: many-to-one

Q. So bi-directional or uni-directional?

Although bi-directional association is usually more convenient, sometimes uni-directional association just feels more natural from a design perspective.

  • Rubric and Criterion: bi-directional
  • Rubric and User: uni-directional on the Rubric side

The User class is associated with many things. If you always use bi-directional association, the User class will become rather unwieldy.

  • Rubric and Assignment: uni-directional on the Assignment side

When we display an assignment, we should show all the rubrics associated with it, but when we display a rubric, we probably don't need/want to display the assignments it is associated with, so a uni-directional association is enough.

  • RubricScore and Rubric: uni-directional on the RubricScore side, or bi-directional

RubricScore should definitely has a Rubric reference. Whether Rubric should keep a collection of RubricScore depends on whether we'll provide some function to calculate the score statistics for a rubric.

  • RubricScore and Submission: bidirectional

Q. So how do the classes look like?

Rubric Criterion RubricScore Assignment Submission
Long id
String name
int ratingScale
List<Criterion> criteria
User author
Date timestamp
Long id
List<String> qualityDescriptions
Rubric rubric;
 
Long id
List<Integer> ratings
Rubric rubric
Submission submission
...
List<Rubric> rubrics
..
List<RubricScore> rubricScores
This page has been viewed 4025 times.