El código está vivo y muerto. Segunda parte Acciones y propiedades

La última vez escribí que los nombres de los objetos son de gran importancia y que deben seleccionarse cuidadosamente y con atención a los detalles. El mal nombre asusta y no permite comprender la esencia de lo que está sucediendo. Pero, ¿cuál es la esencia de esto?


Es difícil evaluar a un héroe sin comprender sus "estadísticas" y "habilidades" . Lo que puede y de lo que es capaz es el siguiente nivel de dificultad en el que tenemos que sumergirnos. No es suficiente reflejar el santuario interno del objeto con la ayuda de un nombre exacto, también debe asegurarse de que este sea el mismo santuario y no los establos de los captadores.


Sobre esto - en el artículo.


Tabla de contenidos del ciclo


  1. Los objetos
  2. Acciones y propiedades
  3. Código como texto

Acciones


El personaje ataca, defiende, esquiva, dispara desde un arco, usa hechizos y agita una espada. El nombre refleja el objeto, pero el objeto mismo está en movimiento, en reacción, en acción. De lo contrario, hablaríamos de tablas en Excel.


En C #, las acciones son métodos y funciones. Y para nosotros: verbos, átomos de movimiento verbal. Los verbos mueven el tiempo, debido a que los objetos existen e interactúan. Donde hay un cambio, debe haber un verbo.


Setters


De todos los cambios, la asignación es menos móvil. Describe estricta y matemáticamente qué cantidades son y a qué son iguales, pero nunca comunica vida y vigor a un texto, como lo hacen los verbos.


Por ejemplo, hay IPullRequest con la propiedad Status , que se puede IPullRequest , IPullRequest o Merged . Puede escribir pullRequest.Status = Status.Declined , pero esto es lo mismo que decir "Establecer la solicitud de grupo al estado cancelado", es imprescindible. Mucho más fuerte es pullRequest.Decline() y, en consecuencia, pullRequest.Approve() , pullRequest.Merge() .


Un verbo activo es preferible a un setter, pero no todos los verbos lo son.


Voz pasiva


PerformPurchase , DoDelete , MakeCall .


Al igual que en HeroManager un sustantivo importante está oculto por un Manager sin sentido, así que en PerformMigration - Perform . Después de todo, más vivo, ¡solo Migrate !


Los verbos activos actualizan el texto: no "hit" , sino "hit" ; no "hizo un columpio" , sino "agitó" ; no "tomó una decisión" , sino "decidió" . Entonces, en el código: PerformApplicationApply ; DoDeleteDelete ; PerformPurchasePurchase , Buy . (Pero DealDamage establecido, aunque en casos excepcionales se puede decir Attack ).


Evitando la voz pasiva, desarrollamos la historia, movemos a los personajes, pero también debemos asegurarnos de que la película no se vea en blanco y negro.


Verbos fuertes


Algunas palabras transmiten matices de significado mejor que otras. Si escribe "bebió un vaso de agua" , será simple y claro. Pero "drenó un vaso de agua" - en sentido figurado, más fuerte.


Por lo tanto, el cambio de salud del jugador se puede expresar a través de player.Health = X o player.SetHealth , pero más pintoresco es player.RestoreHealth .


O, por ejemplo, sabemos Stack no por Add/Remove , sino por Push/Pop .


Los verbos fuertes y activos saturan el objeto con comportamiento, si no son demasiado específicos.


Partes redundantes


Al igual que con ManualResetEvent , cuanto más nos acercamos a los aspectos técnicos internos de .NET, que son complejos y sería bueno simplemente expresarlos, más ricos serán los detalles y los excesos de la API.


Sucede que necesita hacer algún trabajo en otro hilo, pero para no molestarse en crearlo y detenerlo. En C # hay ThreadPool para esto. Solo aquí hay un simple "hacer el trabajo" aquí - QueueUserWorkItem ! Qué tipo de elemento de trabajo ( WorkItem ) es y qué puede ser, si no es usuario ( User ), no está claro. Mucho más fácil sería: ThreadPool.Run o ThreadPool.Execute .


Otro ejemplo. Recordar y saber que hay una instrucción atómica de comparar e intercambiar (CAS) es bueno, pero portarlo limpio al código no es la mejor solución. Interlocked.CompareExchange(ref x, newX, oldX) es inferior a Atomically.Change(ref x, from: oldX, to: newX) (usando parámetros con nombre).


El código no es un doctorado en el trabajo con una computadora cuántica, no es una aplicación para cálculos matemáticos, pero el lector a veces es completamente indiferente a lo que se llaman las instrucciones de bajo nivel. El uso diario es importante.


Repeticiones


UsersRepository.AddUser , Benchmark.ExecuteBenchmark , AppInitializer.Initialize , UniversalMarshaller.Marshal , Logger.LogError .


Como dije en la última parte, la repetición erosiona el significado, comprime el espacio.


No UsersRepository.AddUser , sino UsersRepository.Add ; no Directory.CreateDirectory , sino Directory.Create ; no HttpWebResponse.GetResponseStream , sino HttpWebResponse.Stream ; no Logger.LogError , sino Log.Error .


Arena fina


Check es una palabra de muchos lados. CheckHasLongName puede devolver un bool o lanzar una excepción si el usuario tiene un nombre demasiado largo. Mejor es bool HasLongName o void EnsureHasShortName . Incluso conocí CheckRebootCounter , que ... En algún lugar dentro, ¡reinicié IIS!


Enumerate : de la misma serie. En .NET hay un método Directory.EnumerateDirectories(path) : por alguna razón, se especifica que las carpetas se enumerarán, aunque Directories.Of(path) o path.Directories() .


Calc : Calculate se reduce a menudo, aunque se parece más a los depósitos de calcio.


Proc es otra abreviatura elegante para Process .


Base , Impl , Internal , Raw : palabras parásitas que indican la complejidad de los objetos.


Total


Una vez más, un lector atento se dará cuenta, todo se reduce a la simplificación, a comparar el habla natural, y los consejos en sí mismos se relacionan en gran medida no solo con el código, sino con la escritura en general. Utilizándolos, el desarrollador pule tanto el código como el texto y el texto mismo, buscando una presentación transparente y fluida, para simplificar.


Ahora que hemos descubierto el movimiento y los "efectos especiales", veamos cómo se describen las relaciones entre los objetos.


Las propiedades


El personaje tiene salud y maná; los artículos están en la cesta de la compra; El sistema solar está formado por planetas. Los objetos no solo actúan desinteresadamente, sino que también se relacionan: jerárquicamente (antepasado-heredero), composicional (parte completa), espacialmente (elemento de almacenamiento), etc.


En C #, las propiedades y las relaciones son métodos (generalmente comenzando con Get ), getters (propiedades con un cuerpo get específico) y campos. Pero para nosotros es: adiciones de palabras que expresan la pertenencia de un objeto a otro. Por ejemplo, un jugador tiene salud - Player.Health , que corresponde casi exactamente a la "salud del jugador" en inglés .


Lo que más se confunde hoy son los métodos de acción y los métodos de propiedad.


Verbo en lugar de un sustantivo


GetDiscount , CalculateDamage , FetchResult , ComputeFov , CreateMap .


Los asentamientos se escuchan en todas partes: los métodos deben comenzar con verbos. Raramente ves a alguien dudando: ¿es realmente así? Después de todo, no puede haber una diferencia significativa entre Player.Health y Player.Health() . Deje que los registros sean sintácticamente diferentes, significan lo mismo.


Supongamos que en IUsersRepository se espera fácilmente un GetUser(int id) . ¿Por qué, para representar al usuario, piensa en algún tipo de recibo ( Get )? Será más preciso: ¡ User(int id) !


Y realmente: no FetchResult() , sino Result() ; no GetResponse() , sino Response() ; no CalculateDamage() , pero Damage() .


Una charla DDD da un ejemplo de un código "bueno": DiscountCalculator con el método CalculateDiscountBy(int customerId) . No solo hay una repetición simétrica en la cara: DiscountCalculator.CalculateDiscount , también especificaron que se calcula el descuento. ¿Y qué más, uno pregunta, que hacer con ella?


Sería más fuerte pasar de la entidad misma: el Discount con el método static decimal Of(Customer customer, Order order) para llamar a Discount.Of(customer, order) es más simple que _discountCalculator.CalculateDiscountBy(customerId) y corresponde a un solo idioma.


A veces, al omitir el verbo, perdemos algo, como, por ejemplo, en CreateMap() : un reemplazo directo con Map() puede no ser suficiente. Entonces la mejor solución es NewMap() : nuevamente, el objeto está en la cabeza, no la acción.


El uso de verbos vacíos vacíos es característico de una cultura obsoleta e imperativa, donde el algoritmo es primario y está por delante del concepto. Allí a menudo encontrará una "cuchilla que ha sido templada" que una "cuchilla endurecida" . Pero el estilo de los libros sobre James Bond no es adecuado para describir el paisaje. Donde no hay movimiento, no hay lugar para el verbo.


Otros


Las propiedades y métodos que expresan las relaciones entre los objetos también son objetos, por lo tanto, lo mencionado anteriormente en muchos aspectos se aplica a ellos.


Por ejemplo, repeticiones en propiedades: no Thread.CurrentThread , sino Thread.Current ; no Inventory.InventoryItems , sino Inventory.Items , etc.


Total


Las palabras simples y comprensibles no se confunden y, por lo tanto, el código que las contiene tampoco se confunde. En la escritura, es igualmente importante escribir fácilmente: evitar preposiciones pasivas, una gran cantidad de adverbios y adjetivos, repeticiones, porque las acciones prefieren un verbo a un sustantivo. Un ejemplo bien conocido: "Asintió con la cabeza, de acuerdo", en lugar de "Asintió", provoca una sonrisa, y recuerdo QueueUserWorkItem .


El texto del código también es diferente en que en el primer caso se le pagará si la casa está de pie, ahogada por los rayos del sol poniente ; en el segundo, si la casa está en pie ; pero vale la pena recordar: una casa debe estar de pie, y no palos de los ayudantes.


En los primeros dos artículos de la serie, quería mostrar lo importante que es trabajar no solo en el algoritmo, sino también en la palabra; cómo los nombres determinan el contenido de lo que se llama; cómo el código redundante y demasiado complicado aleja al lector.


Junto con esto, los buenos nombres son solo notas. Para jugar , deben estar escritos y encarnados en la música. Te contaré más en el próximo artículo final.

Source: https://habr.com/ru/post/447674/


All Articles