Static type systems are all about preventing incorrect uses of data.
There are obvious examples of types doing this:
- you can't get the month of a UUID
- you can't multiply two strings.
There are more subtle examples
- you can't pay for something using the length of desk
- you can't make an HTTP request using someone's name as the URL.
We may be tempted to use
double for both price and length, or use a
string for both a name and a URL. But doing that subverts our awesome type system, and allows these misuses to pass the language's static checks.
Getting pound-seconds confused with Newton-seconds can have poor results at runtime.
This is particularly a problem with strings. They frequently become the "universal data type".
We are used to primarily text interfaces with computers, and we often extend these human interfaces (UIs) to programming interfaces (APIs). We think of 34.25 as the characters 34.25. We think of a date as the characters 05-03-2015. We think of a UUID as the characters 75e945ee-f1e9-11e4-b9b2-1697f925ec7b.
But this mental model harms API abstraction.
The words or the language, as they are written or spoken, do not seem to play any role in my mechanism of thought.
Similarly, textual representations should not play any role in designing types and APIs. Beware the
string! (and other overly generic "primitive" types)
Types communicate "what operations make sense."
For example, I once worked on a client to an HTTP REST API. REST, properly done, use hypermedia entities, which have hyperlinks pointing to related entities. In this client, not only were the entities typed (e.g. User, Account, Subscription), the links to those entities were also typed (UserLink, AccountLink, SubscriptionLink). The links were little more than wrappers around
Uri, but the separate types made it impossible to try to use an AccountLink to fetch a User. Had everything been plain
Uris -- or even worse,
string -- these mistakes would only be found at runtime.
Likewise, in your situation, you have data that is used for only one purpose: to identify an
Operation. It shouldn't be used for anything else, and we shouldn't be trying to identify
Operations with random strings we've made up. Creating a separate class adds readability and safety to your code.
Of course, all good things can be used in excess. Consider
how much clarity it adds to your code
how often it is used
If a "type" of data (in the abstract sense) is used frequently, for distinct purposes, and between code interfaces, it is a very good candidate for being a separate class, at the expense of verbosity.