Naming and abstracting functions

Most abstractions you end up crafting in JavaScript will be manifested within functions. Even within grand architectures, it is individual functions and methods that do the work, and it is in their conception that a good abstraction begins to reveal itself. It is, therefore, worth thinking quite deeply about how we should name our functions and what factors we should take into consideration when doing so.

A function's name should typically use what, in grammar, is called the imperative form. The imperative form is what we employ when we are giving instructions, such as walk to the shop, buy bread, stop there!.

Although we usually use the imperative form when naming functions, there are exceptions. For example, it is also conventional to prefix functions that return Boolean values with is or has; for example, isValid(...). When creating constructors (which are functions), we name them according to the instance they'll produce; for example, Route or SpecialComponent.

The direct nature of the imperative form is the most understandable and readable in the context of programming. To find the correct imperative form for your specific problem, it's best to imagine the act of giving a military order, that is, don't mince your words and say exactly what it is that you want to occur:

  • If you want a prompt to be displayed, use displayPrompt()
  • If you want elements to be removed, use removeElements()
  • If you want a random number between x and y, use generateRandomNumber(x, y)

Often, we wish to qualify our instructions. If you were to issue an instruction to a person, such as find my bicycle, you would likely further qualify that instruction with information such as it's blue and it's missing its front wheel. It is important, however, not to let a function's name get bogged down with these qualifications. The following function would be an example of this:

findBlueBicycleWithAMissingFrontWheel();

As we mentioned earlier, a needlessly long name is a sign of a bad abstraction. When we see this type of over-qualification, we should take a step back and reconsider. Here, it's important to draw a line between what is sensible in spoken language and what is sensible when programming. In programming, functions are ways of abstracting common behavior that can be adjusted or configured, as needed, via arguments.

So, it is via arguments that we should be expressing the qualifications of blue and missing front wheel. And we could, for example, express these as a single object argument like so:

findBicycle({
color: 'blue',
frontWheel: 'missing'
});

By moving the qualifying parts of a function's name into its arguments, we are producing a cleaner and more comprehensible abstraction. This has the added benefit of increasing the configurability of the abstraction, thereby providing the user with more possibilities.

In our case, we may wish to give users the ability to find objects other than bicycles. To cater to this, we would make the name of the function more generic (for example, findObject) and shift the qualifier to the arguments by adding a new option property (for example, type), like so: 

findObject({
type: 'bicycle',
color: 'blue',
frontWheel: 'missing'
});

Something curious happens at this stage of the process. We have, correctly, moved our various qualifiers to the arguments of our function, expanding the usefulness and configuration of our abstraction. But now what we have is an abstraction that is doing many things, so at some point, it may be prudent to take a step back and build higher-level abstractions to encapsulate these different behaviors. In our case, we could achieve this via functional composition, like so:

const findBicycle    = config => findObject({ ...config, type: 'bicycle' });
const findSkateboard = config => findObject({ ...config, type: 'skateboard' });
const findScooter = config => findObject({ ...config, type: 'scooter' });

Above all, a function is a unit of behavior. As the SRP tells us, it's important to ensure that they are only doing one discernible thing. When considering these things or units of behavior, it's important to think about what a function does from the perspective of those who'll use it. Technically, it's highly likely that our composed findScooter function does all manner of things beneath the surface. It may be incredibly complex. But at the layer of abstraction where it will be used, it can be said to only do one thing, and that is what's important.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.251.70