El código está vivo y muerto. Primera parte Los objetos

El código es un pensamiento. Aparece una tarea, y el desarrollador piensa cómo resolverla, cómo expresar los requisitos en funciones y clases, cómo hacerlos amigos, cómo lograr el rigor y la corrección, y cómo hacer que el negocio sea correcto. Prácticas, técnicas, principios, patrones y enfoques: todo debe tenerse en cuenta y todo debe recordarse.


Y junto con esto, vemos una epidemia generalizada de gerentes, ayudantes, servicios, controladores, selectores, adaptadores, captadores, setters y otros espíritus malignos: todo esto es un código muerto. Él encadena y desorden.


Propongo luchar de esta manera: debes presentar los programas como texto en un lenguaje natural y evaluarlos en consecuencia. Cómo esto y qué sucede - en el artículo.


Tabla de contenidos del ciclo


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

Prologo


Mi experiencia es modesta (unos cuatro años), pero cuanto más trabajo, más entiendo: si el programa es ilegible, no tiene sentido. Hace tiempo que se sabe y se supera para recordar: el código no solo resuelve algunos problemas ahora , sino también más tarde : es compatible, ampliado, corregido. Además, él siempre: lee.


Después de todo, esto es texto.


La estética del código como texto es un tema clave en el ciclo. La estética aquí es un vidrio a través del cual miramos las cosas y decimos, sí, es bueno, sí, es hermoso.


En cuestiones de belleza y comprensión, las palabras son de gran importancia. Decir: "Por el momento, mis percepciones están en un estado aburrido debido al alto nivel de etanol en la sangre" no es lo mismo que "me emborraché" .


Somos afortunados, los programas están compuestos casi por completo de palabras.


Digamos que necesita hacer "un personaje que tenga salud y maná, camina, ataca, usa hechizos", y puede ver de inmediato: hay objetos (personaje, salud, maná, hechizo), acciones (caminar, atacar, usar) y propiedades ( el personaje tiene salud, maná, velocidad de hechizos): todos estos serán nombres: clases, funciones, métodos, variables, propiedades y campos, en una palabra, todo en lo que se divide el lenguaje de programación.


Pero no distinguiré clases de estructuras, campos de propiedades y métodos de funciones: un personaje como parte de una narración no depende de detalles técnicos (que puede representarse como una referencia o un tipo significativo). Una cosa esencialmente diferente: que este es un personaje y que lo llamaron Hero (o Character ), y no HeroData o HeroUtils .


Ahora tomaré el cristal de la estética y mostraré cómo se escribe un código hoy y por qué está lejos de ser perfecto.


Los objetos


En C # (y no solo), los objetos, instancias de clases que se colocan en el montón, viven allí durante un tiempo y luego el recolector de basura los elimina. También se pueden crear estructuras en la pila o matrices asociativas, o algo más. Para nosotros son: nombres de clase, sustantivos.


Los nombres en código, como los nombres en general, pueden ser confusos. Sí, y rara vez ves un nombre feo, pero un objeto hermoso. Especialmente si es un Manager .


Administrador en lugar de un objeto


UserService , AccountManager , DamageUtils , MathHelper , GraphicsManager , GameManager , VectorUtil .


No es la precisión y la tangibilidad lo que domina aquí, sino algo vago, que deja un lugar en la niebla. Para tales nombres, mucho está permitido.


Por ejemplo, en cualquier GameManager puedes agregar cualquier cosa relacionada con el juego y la lógica del juego . Seis meses después, llegará una singularidad tecnológica.


O, sucede, necesitas trabajar con Facebook. ¿Por qué no poner todo el código en un solo lugar: FacebookManager o FacebookService ? Suena seductoramente simple, pero una intención tan vaga crea una decisión igualmente vaga. Al mismo tiempo, sabemos que hay usuarios, amigos, mensajes, grupos, música, intereses, etc. en Facebook. Suficientes palabras!


No solo hay suficientes palabras: todavía las usamos. Solo en discurso ordinario, no entre programas.


Y no es GitUtils , sino IRepository , ICommit , IBranch ; no ExcelHelper , sino ExcelDocument , ExcelSheet ; no GoogleDocsService , sino GoogleDocs .


Cada área temática está llena de objetos. “Los objetos estaban marcados por enormes vacíos” , “El corazón latía furiosamente” , “La casa estaba parada” : los objetos actúan, sienten, son fáciles de imaginar; están en algún lugar aquí, tangibles y densos.


Al mismo tiempo, a veces ve esto: en el repositorio de Microsoft/calculator - CalculatorManager con los métodos: SetPrimaryDisplay , MaxDigitsReached , SetParentDisplayText , OnHistoryItemAdded ...


(Aún así, recuerdo que una vez vi UtilsManager ...)


Sucede de esta manera: quiero expandir el tipo List<> con un nuevo comportamiento, y ListUtils o ListHelper . En este caso, es mejor y más preciso usar solo métodos de extensión: ListExtensions : son parte del concepto y no un volcado de los procedimientos.


Una de las pocas excepciones es OfficeManager como publicación.


Por lo demás ... Los programas no deben compilarse si contienen tales palabras.


Acción en lugar de objeto


IProcessor , ILoader , ISelector , IFilter , IProvider , ISetter , ICreator , IOpener , IHandler ; IEnableable , IInitializable , IUpdatable , ICloneable , IDrawable , ILoadable , IOpenable , ISettable , IConvertible .


Aquí, la esencia de la esencia es un procedimiento, no un concepto, y el código vuelve a perder sus imágenes y legibilidad, y la palabra habitual se reemplaza por una artificial.


Mucho más animado es ISequence , no IEnumerable ; IBlueprint , no ICreator ; IButton , no IButtonPainter ; IPredicate , no IFilter ; IGate , no IOpeneable ; IToggle , no IEnableable .


Una buena historia cuenta sobre los personajes y su desarrollo, y no sobre cómo crea el creador, construye el constructor y dibuja el pintor. Una acción no puede representar completamente un objeto. ListSorter no es una SortedList .


Tome DirectoryCleaner , por ejemplo, un objeto que limpia carpetas en el sistema de archivos. ¿Es elegante? Pero nunca decimos: "Pídale al limpiador de carpetas que limpie D: / Test" , siempre: "Limpiar D: / Test" , de modo que el Directory con el método Clean se vea más natural y más cercano.


Un caso más animado es más interesante: FileSystemWatcher de .NET es un observador del sistema de archivos que informa los cambios. Pero, ¿por qué todo el observador, si los cambios en sí mismos pueden informar que sucedieron? Además, deben estar inextricablemente vinculados con el archivo o carpeta, por lo que también deben colocarse en el Directory o File (utilizando la propiedad Changes con la capacidad de llamar a file.Changes.OnNext(action) ).


Tales nombres verbales parecen justificar el patrón de diseño de la Strategy , dando instrucciones para "encapsular la familia de algoritmos" . Pero si en lugar de una "familia de algoritmos" encontramos un objeto auténtico que existe en la historia, veremos que la estrategia es solo una generalización.


Para explicar estos y muchos otros errores, pasamos a la filosofía.


La existencia precede a la esencia.


MethodInfo , ItemData , AttackOutput , CreationStrategy , StringBuilder , SomethingWrapper , LogBehaviour .


Dichos nombres están unidos por una cosa: su ser se basa en detalles.


Sucede que algo interfiere rápidamente con una tarea: falta algo o falta, pero no algo. Entonces piensas: "Una cosa que puede hacer X ahora me ayudaría" , así es como se concibe la existencia . Luego, para "hacer" X, se escribe XImpl : así es como aparece la entidad .


Por lo tanto, en lugar de IArrayItem , IIndexedItem o IItemWithIndex es más común o, por ejemplo, en Reflection API, en lugar del método ( Method ), solo vemos información al respecto ( MethodInfo ).


Una forma más verdadera: frente a la necesidad de existencia, encontrar una entidad que la implemente y, como esta es su naturaleza, otras.


Por ejemplo, querían cambiar el valor de tipo string sin crear instancias intermedias: resultó ser una solución sencilla en forma de StringBuilder , mientras que, en mi opinión, es más apropiado: MutableString .


Recupere el sistema de archivos: DirectoryRenamer no DirectoryRenamer necesario para cambiar el nombre de las carpetas, porque tan pronto como acepta la presencia del objeto Directory , la acción ya está en él, simplemente no ha encontrado el método correspondiente en el código.


Si desea describir cómo tomar un bloqueo , no es necesario aclarar que esto es ILockBehaviour o ILockStrategy , que es mucho más fácil: ILock (con el método Acquire que devuelve IDisposable ) o ICriticalSection (con Enter ).


Esto también incluye todo tipo de Data , Info , Output , Input , Args , Args ( State menos frecuente) - objetos que están completamente desprovistos de comportamiento, porque se consideraron unilateral.


Cuando la existencia es primaria, el cociente se mezcla con el general, y los nombres de los objetos son confusos: hay que leer cada línea y averiguar a dónde fue el personaje y por qué solo hay sus Data .


Taxonomía extraña


CalculatorImpl , AbstractHero , ConcreteThing , CharacterBase .


Todo por las mismas razones descritas anteriormente, a veces vemos objetos para los que se indica con precisión un lugar en la jerarquía. Una vez más, la existencia se precipita hacia adelante, nuevamente vemos cómo la necesidad inmediata se vertió rápidamente en el código, sin considerar las consecuencias.


Después de todo, ¿hay realmente una persona ( Human ), el heredero de la persona base ( HumanBase )? Pero, ¿cómo es cuando Item hereda AbstractItem ?


A veces quieren mostrar que no hay Character , sino algún tipo de imagen "en bruto": CharacterRaw .


Impl , Abstract , Custom , Base , Concrete , Internal , Raw : un signo de inestabilidad, vaguedad de la arquitectura que, como el arma de la primera escena, ciertamente se disparará más tarde.


Repeticiones


Con los tipos anidados, esto sucede: RepositoryItem en Repository , WindowState en Window , HeroBuilder en Hero .


La repetición corta el significado, exacerba las fallas y solo contribuye a la complejidad del texto.


Partes redundantes


Para sincronizar hilos, ManualResetEvent se usa a menudo con la siguiente API:


 public class ManualResetEvent { //   —  `EventWaitHandle`. void Set(); void Reset(); bool WaitOne(); } 

Cada vez, personalmente, tengo que recordar cómo Set difiere de Reset (gramática incómoda) y qué es un "evento de restablecimiento manual" en general en el contexto de trabajar con flujos.


En tales casos, es más fácil usar metáforas lejos de la programación (pero cerca de la vida cotidiana):


 public class ThreadGate { void Open(); void Close(); bool WaitForOpen(); } 

¡Ciertamente no hay nada que confundir!


A veces se trata de lo ridículo: ¡especifique que los artículos no son solo Items , sino necesariamente ItemsList o ItemsDictionary !


Sin embargo, si ItemsList no ItemsList divertido, entonces AbstractInterceptorDrivenBeanDefinitionDecorator de Spring está ItemsList . Las palabras en este nombre son trapos de los que se cose un monstruo gigantesco. Aunque ... Si este es un monstruo, entonces ¿qué es HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor ? Esperanza legado .


Además de los nombres de clase e interfaz, a menudo se encuentra redundancia en variables o campos.


Por ejemplo, un campo de tipo IOrdersRepository se llama _ordersRepository . Pero, ¿qué tan importante es informar que los pedidos son enviados por el repositorio? Después de todo, mucho más fácil: _orders .


También sucede que en las consultas LINQ escriben los nombres de argumentos completos de las expresiones lambda, por ejemplo, Player.Items.Where(item => item.IsWeapon) , aunque ya entendemos este elemento al mirar Player.Items . En tales casos, me gusta usar siempre el mismo carácter: x : Player.Items.Where(x => x.IsWeapon) (con continuación a y , z si estas son funciones dentro de funciones).


Total


Admito que con un comienzo así no será fácil encontrar la verdad objetiva. Alguien, por ejemplo, dirá: escribir Service o no escribir es un punto discutible, insignificante, de buen gusto, y ¿qué diferencia hay si funciona?


¡Pero puedes beber de vasos desechables!


Estoy convencido de que el camino hacia el contenido se encuentra a través de la forma, y ​​si uno no mira el pensamiento, parece que se ha ido. En el texto del programa, todo funciona de la misma manera: el estilo, la atmósfera y el ritmo ayudan a expresarse no confundidos, sino comprensiblemente y con capacidad.


El nombre del objeto no es solo su cara, sino también ser, uno mismo. Determina si será etéreo o saturado, abstracto o real, seco o animado. El nombre está cambiando, el contenido está cambiando.


En el próximo artículo hablaremos sobre este contenido y lo que sucede.

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


All Articles