While developing the improvements for the Generate class of Testing.Commons I faced an API design challenge that I, at least, consider challenging.
Imagine we have to model a TimeOfDay
class, that puts a name to an important time of the day, such as noon or midnight and which members are used from within a delegate, to give a nice lambda-ish flavor to the API:
How could one design such a type?
Enumerations, Good Old Friends
MSDN: ”An enumeration type (also named an enumeration or an enum) provides an efficient way to define a set of named integral constants that may be assigned to a variable.”, which means that you can put a name on a constant value, as long as it is one of the supported types (integral type byte, sbyte, short, ushort, int, uint, long, or ulong).As we want to represent that time of the day as the time elapsed since the day started, we are willing to represent it as a TimeSpan
. Of course, TimeSpan is not one of the supported types, which rules out enumerations.
Static, Not Good Enough
One way to overcome this problem is declare the values either as constants. Since they are not constants as the compilers likes them, they could be public readonly fields, as in:
That is almost fine (we can use static to prevent instantiation), if what we want to use is the class in statements, as in:
Unfortunately, in order to use it within the lambda an instance of TimeOfDay is required as static fields cannot be accesses from object instances .
Instance is the way to go
Instances if what is needed, instances is what will be provided.
That indeed allows the delegate usage, and instantiation is allowed. I do not see a lot of people willing to instantiate such a simple class (we have become so lazy as to consider the new operator as an annoyance) nor I want to instantiate one every time, just to access a “constant” field (serious case of micro-optimization, I know).
Thou Shall Not Instantiate
Preventing instantiation from the outside is as simple as to provide the lamest construction of all time: a singleton. I do not care about lazy initialization or anything, so the simplest construct is used:
Now, we are able to prevent instantiation from outside (and inside the assembly) and happily use the single instance exposed by Instance from within the assembly.
We could satisfy the users that want to consume static counterparts, but it would have to be using another name, as static and instance members cannot have the same name . But as that is not a use case that I foresee (famous last words) I decided not to add them.
Interesting?
Maybe. I, for one, enjoyed designing an API test-first.
Useful? Well, I think the API is intuitive and expressive. What do you think?