Imagine you have such routines
/*just do X. Fail if any precondition is not met*/
doX()
/*take care of preconditions and then do X*/
takeCareOfPreconditionsCheckIfNeededAtAllAndThenDoX()
A little bit more concrete example:
/*create directory. Most probably fail with error if any precondition is not met (folder already exists, parent does not exists)*/
createDirectory(path_name)
/*take care of preconditions (creates full path till folder if needed, checks if not exists yet) and then creates the directory*/
CheckIfNotExistsYet_CreateDirectory_andFullPathIfNeeded(path_name)
How do you name such routines, so it would be clear what does what?
I have come to some my own “convetion” like:
naiveCreateDirectory, ForceDirectoryExists, ...
But I imagine this is very standard situation.
Maybe there already exists some norms/convetions for this?
4
There are many possible conventions – I often have to have routines that do something, and other routines that check things and then, eventually, either do it or not. I invariably call these frobWidgets()
and maybeFrobWidgets()
. There are other prefixes that would fit, e.g. ensure
is often used for code that initializes something lazily, but there isn’t a universal convention. It’s not necessary, though. Far more important than perceived consistency with the “state of the art” is actual consistency within your code base. So go ahead with checkAndFrob()
vs. frob()
if you’ve already started doing that.
1
If I have a function that may fail – such as creating a directory, writing to a file, etc. – I use the following signature:
bool TryDoSomething(string someParam)
So the method will do something with someParam
. If successful, return true; likewise, if unsuccessful, return false.
Method parameters are obviously not always necessary. There is another advantage to this method signature in that you don’t have to throw exceptions above this method unless deemed fatal. Note this method can be used to return out
values as well. Reference the C# dictionary TryGetValue(...)
method.
I also name functions that validate data as SomeParamIsValid(string SomeParam)
. In this way, I can write something like:
if (SomeParamIsValid(someParam) {
if(!TryDoSomething(someParam, out SomeObject someObj) {
// handle failed operation
}
return someObj;
}
This makes for some very readable and easily maintained code, in my opinion.
Usually function names should be longer when the functions’ scope is shorter (ie. private methods) and shorter the name when the functions are public with a longer scope.
In your case, I usually do something like this: createDir($path) and fail with ugly error; or tryCreatingDir($path) and catch the error and continue with an internal decision like throw a nice error message or if the directory already exists suppress all errors and get out as normal.
This does not pollute the naming but does a differentiation. Also the explicit nice user friendly exception message is enough for all the exceptional cases.
Sometimes I invert this and use createDir() to check for stuff and than the second method would be doCreateDir() and I would call it ONLY if all conditions are met to create the directory.
Not prefixes, but case. Consider using TurgidCapitalCaseNames()
for ‘opinionated’ functions and brief_lower()
names for basic ‘raw’ functions.
This presumes you can separate your application into ‘raw’ and ‘opinionated’ parts. Sort of like what Linux calls policy/mechanism. Then, your question of where to put the decision work to satisfy preconditions answers itself: In the opinionated functions.
error_type mkdir_and_parents(string path); Dir MakeAnEmptyOutputDirectory(string subdir);
Mixing identifier case like this disgusts some people, but it might work for you?
By ‘opinionated’, I mean functions that would be:
- cock-sure: deluded with the unquestionable reachability of the goal
- expedient: taking short cuts to success, e.g. succeeding silently if the directory exists already
- precocious: acquiring needed resources without a second thought, allocating memory, making parent directories
- stubborn: e.g. retrying while the system responds with ‘filesystem full’
- robust: gets stuff done; might log warnings when someone else is at fault, but probably has a fallback answer
Your ‘raw’ functions could be quite the opposite:
- subservient: don’t do anything unless asked, and when asked, do only that
- leaky: make no attempt to cover up the horror, should anything go wrong; Keen to hand problem back to someone else
- humble: assume no control over any resource not expressly given
- simple: only knows about a very small range of things
I doubt there is much of a middle ground. It would be very confused ground.
Aside: I think internal subsystem code and system interfaces are usually ‘raw’-like. On the other hand I’ve found most customer-facing ‘event response’ code is usually very opinionated: eg key command handlers, view renderers, report generators, cron tasks.
Such a correlation might be because it is more important that world-facing parts of the system achieve a robust something instead of a fragile nothing. Maybe it reflects the human or business user’s rough needs and expectations, versus the conservative, detail-oriented engineer-artisan’s needs for dealing with complexity and diagnosing faults.
1
I’ld go with:
createDirectory(param)
chkDirectoryExists(param)
If these are ambiguous ie if there were two different types of directories I’d split the object in two which would both implement an interface called something like DirectoryHandler.