I am working on a simple project developing a telephone directory application. As such, it requires the implementation of the CRUD actions. Right now I have them as individual classes (e.g. “AddEntry”), which are nested under an “Actions” directory. I was considering creating an “Entry” class and having each action as an instance method, but Entry objects are not actually stored as Entry objects; entries are stored in CSV format. Is it proper design to leave the actions as individual classes?
In layman’s terms:
- Classes should be modeled after entities, things, not verbs.
- Methods should be modeled after verbs.
Then…
The class sould be PhoneDirectoryEntry
(a thing) which has a method called save()
(a verb).
Bottomline:
No, classes should not be modeled after actions/verbs (CRUD or otherwise).
Although classes usually represents entities there can be exceptions. For example if you are implementing a command pattern (https://en.wikipedia.org/wiki/Command_pattern) your classes represent actions. So there is no hard and fast rule saying that you cannot model your actions as classes but it depends on your requirement.
This might be clearer if you think of REST API calls.
Entry is the resource.
Add is modelled as a POST call on the entry resource.
Therefore the java jaxrs would be something like:
@Path("entry")
public class Entry {
@POST
public String addEntry(@FormParam("entry") String name) {
// store csv
}
@GET
public String retrieveEntryFromCSV(@Context UriInfo ui) {
// get entry
}
}
1
There’s (at least) three directions we can approach the question to find reasons that all suggest a single class with multiple methods is more appropriate.
First, one reason is that each of the individual C,R,U,D operations need access to the same underlying storage. If you change the underlying storage object/mechanism, that means likely changing all 4 C/R/U/D classes (and if it doesn’t mean that then the underlying common storage object is already providing a CRUD abstraction to hide its details!). The common underlying storage requirement suggest a single class with methods over multiple classes sharing a common storage object (one class vs. 5 classes).
Second, another reason is we should think about how we might pass around and/or share the CRUD capabilities with other code that wants to use that. As separate classes, we’d have to pass C, R, U, and D objects separately to various other users of the capabilities. As a single class, we merely pass around the one entity.
Good programming is about providing abstractions that can be built upon by you and perhaps someone else. You want to move away from low-level operations and provide useful and relevant abstractions. These abstractions are necessarily groups of capabilities that are related to each other, packaged together. Sometimes this means a single class, other times a class hierarchy, and other times a set of interfaces. A third reason is that as you’re not providing a real class hierarchy (but rather instead a flat set of classes), you choose a single class as the packaging for your abstraction.
When we do this right, we separate providers from consumers, which provide for a measure of independence. This allows for layering where one layer simple doesn’t have to worry about all the layers below it because it has just the right tools from the immediately lower layer to implement a useful abstraction for the next layer up.