Last time I wrote that the names of objects are of great importance, and that you need to select them carefully and with attention to detail. Bad name scares and does not give insight into what is happening. But what is the essence?
It is difficult to evaluate the hero without understanding his "stats" and "abilities . " What he can and can do is the next level of difficulty we have to dive into. It is not enough to reflect the inner sanctuary of the object with the help of the exact name, one should also make sure that this is a sanctuary , and not a stable of getters.
About this - in the article.
The character attacks, defends, dodges, archery, uses spells, waving his blade. The name reflects the object, but the object itself is in motion, in reaction, in actions. Otherwise we would talk about tables in Excel.
In C #, actions are methods and functions. And for us: verbs, atoms of verbal movement. Verbs move time, because of them objects exist and interact. Where there is a change - there must be a verb.
Of all the changes, the least moving is assignment. It strictly and mathematically describes what values ​​are and what they are equal to, but never informs the life and vigor of the text, as verbs do.
For example, there is an IPullRequest
with a Status
property that can be Approved
, Declined
or Merged
. You can write pullRequest.Status = Status.Declined
, but this is the same as saying “Install a pull-request canceled status” is imperative. Much stronger - pullRequest.Decline()
and, respectively, pullRequest.Approve()
, pullRequest.Merge()
.
The active verb is preferable to the setter, but not all verbs are like that.
PerformPurchase
, DoDelete
, MakeCall
.
As in HeroManager
an important noun is obscured by a meaningless Manager
, so in PerformMigration
- Perform
. After all, livelier - just Migrate
!
Active verbs refresh the text: not “struck” , but “hit” ; not “made a swing” , but “swung” ; not “made a decision” , but “decided” . So in the code: PerformApplication
→ Apply
; DoDelete
→ Delete
; PerformPurchase
→ Purchase
, Buy
. (But DealDamage
settled, although in rare cases Attack
may be meant .)
Avoiding a passive voice, we develop the story, move the characters, but still need to make sure that the movie did not work in black and white.
Some words convey a sense of meaning better than others. If you write “he drank a glass of water” , it’s easy and clear. But “drained a glass of water” - more vividly, more strongly.
So a player's health change can be expressed through player.Health = X
or player.SetHealth
, but more picturesquely - player.RestoreHealth
.
Or, for example, Stack
we know not by Add/Remove
, but by Push/Pop
.
Strong and active verbs saturate an object with behavior, if they are not too specific.
As with ManualResetEvent
, the closer we get to the technical .NET internals, which are complex and it would be nice to express them simply, the richer the details and extras get the API.
It happens, you need to do some work on another thread, but so as not to bother to create and stop it. In C #, there is a ThreadPool
for this. Only here is a simple “doing job” here - QueueUserWorkItem
! What kind of work item ( WorkItem
) and how it can be, if not user ( User
), is unclear. Where it would be easier - ThreadPool.Run
or ThreadPool.Execute
.
Another example. To remember and know that there is an atomic instruction; compare-and-swap (CAS) is good, but to transfer it clean to the code is not the best solution. Interlocked.CompareExchange(ref x, newX, oldX)
is inferior to Atomically.Change(ref x, from: oldX, to: newX)
entries Atomically.Change(ref x, from: oldX, to: newX)
(using named parameters).
The code is not a doctorate for working with a quantum computer, not an application to mathematical calculations, but sometimes the reader is completely indifferent to what low-level instructions are called. Daily use is important.
UsersRepository.AddUser
, Benchmark.ExecuteBenchmark
, AppInitializer.Initialize
, UniversalMarshaller.Marshal
, Logger.LogError
.
As mentioned in the last part, repetitions blur the meaning, squeeze space.
Not UsersRepository.AddUser
, but UsersRepository.Add
; Directory.CreateDirectory
, not Directory.Create
; not HttpWebResponse.GetResponseStream
, but HttpWebResponse.Stream
; not Logger.LogError
, but Log.Error
.
Check
is a multifaceted word. CheckHasLongName
can either return a bool
or throw an exception if the user has a very long name. Better - bool HasLongName
or void EnsureHasShortName
. I even met CheckRebootCounter
, which ... IIS restarted somewhere inside IIS!
Enumerate
- from the same series. In .NET there is a method Directory.EnumerateDirectories(path)
: for some reason, it is specified that the folders will be listed, although Directories.Of(path)
or path.Directories()
.
Calc
- so often reduce Calculate
, although it looks more like deposits of calcium.
Proc
is another fancy abbreviation for Process
.
Base
, Impl
, Internal
, Raw
- words-parasites, indicating the over-complexity of objects.
Once again, the attentive reader will notice, it all comes down to simplification, to the likening of natural speech, and the advice itself largely concerns not only the code, but letters in general. Using them, the developer polishes both the code as text and the text itself, striving for a transparent, smooth presentation, for simplicity.
Now, having dealt with the movement and “special effects” , we will look at how relations between objects are described.
The character has health and mana; items in the shopping cart; The solar system consists of planets. Objects not only selflessly act, but also correlate: hierarchically (ancestor-heir), compositionally (whole-part), spatially (repository-element), etc.
In C #, properties and relationships are methods (usually starting with Get
), getters (properties with a particular get
body), and fields. For us, it is: words-additions expressing the belonging of one object to another. For example, a player has health - Player. Health, which almost exactly corresponds to the English “player's health“ .
Most confused now are action methods and property methods.
GetDiscount
, CalculateDamage
, FetchResult
, ComputeFov
, CreateMap
.
You can hear well-established from everywhere: methods must begin with verbs. It is rare to meet someone who doubts: is it really so? After all, there can be no significant difference between Player.Health
and Player.Health()
. Let the notes be syntactically different, they mean the same thing.
Let us assume that some GetUser(int id)
is easily expected in the IUsersRepository
. Why in order to represent the user think out some receipt ( Get
)? Be careful - User(int id)
!
And indeed: not FetchResult()
, but Result()
; not GetResponse()
, but Response()
; not CalculateDamage()
, but Damage()
.
One DDD report provides an example of “good” code: DiscountCalculator
with the CalculateDiscountBy(int customerId)
method CalculateDiscountBy(int customerId)
. Not only is symmetrical repetition on the face - DiscountCalculator.CalculateDiscount
, but also clarified that the discount is calculated . And what else with her, I ask, to do?
It would be stronger to go from the entity itself - Discount
with the static decimal Of(Customer customer, Order order)
method to call Discount.Of(customer, order)
- simpler than _discountCalculator.CalculateDiscountBy(customerId)
, and corresponds to a single language.
Sometimes, having omitted the verb, we lose something, as in, say, CreateMap()
: there may be few direct substitutions for Map()
. Then the best solution is NewMap()
: the object is at the head again, not the action.
The use of empty verbs is characteristic of an obsolete, imperative culture, where the algorithm is primary and stands in front of the concept. There you will often meet the “blade that was hardened” than the “hardened blade” . But the style of the books about James Bond is not suitable for describing the landscape. Where there is no movement, there is no place for the verb.
Properties and methods expressing the relationship between objects are also objects, so what has been said above largely applies to them.
For example, the repetition in the properties: not Thread.CurrentThread
, but Thread.Current
; not Inventory.InventoryItems
, but Inventory.Items
, etc.
Simple, understandable words do not confuse, and therefore the code consisting of them also does not confuse. In writing, it is equally important to write easily: to avoid passive voice, an abundance of adverbs and adjectives, repetitions, and for actions to prefer the verb to the noun. A well-known example: “He nodded with his head, agreeing” instead of “He nodded” causes a smile, and recalls QueueUserWorkItem
.
The text differs from the code by the fact that in the first case you will be paid if the house stands, sinking in the rays of the setting sun ; in the second - if the house is worth ; but it is worth remembering: the house should stand, not the helper sticks.
In the first two articles of the cycle, I wanted to show how important it is to work not only on the algorithm, but also on the word; how the names define the content called; as redundant and overdeveloped code drives the reader away.
At the same time, good names are just notes. To play , they must be written and embodied in music. I'll tell you more in the next, final article .
Source: https://habr.com/ru/post/447674/
All Articles