Recientemente en mi empresa hemos tenido un pequeño debate sobre abstracción versus simplicidad. Una escuela de pensamiento que caracterizaría como "SECO y la abstracción no puede hacer daño", y lleva a un código como este:
def make_foo_binary(binaryName, objFiles, fooLibsToLinkAgainst)
make_exe_task(binaryName, objFiles.ext('.o'), fooLibsToLinkAgainst)
end
y esto:
class String
def escape_space
return self.gsub(' ', '\ ')
end
end
Mi punto de vista es que crear una abstracción como esta, que solo se usa en un lugar, hace que el código sea menos legible, ya que está reemplazando una llamada de función con la que el lector está familiarizado (gsub) por otra que nunca ha visto antes (escape_space), que deberán leer si quieren entender cómo funciona realmente el código. El reemplazo se describe esencialmente en inglés ("espacio de escape") y el inglés es notoriamente vago. Por ejemplo, sin mirar la definición, no sabes si se escapa todo el espacio en blanco, o solo el carácter de espacio.
Hay muchos escritos que cantan alabanzas a DRY y abstracción. ¿Alguien está al tanto de las fuentes que describen los límites de la abstracción? ¿Que cantan alabanzas y discuten la pragmática de mantener el código simple?
Editar: Puedo encontrar textos que fomenten la simplicidad en la vida o en la escritura (en inglés), por ejemplo, "Simplifica, simplifica" de Thoreau. o "La escritura vigorosa de Strunk y White es concisa". ¿Dónde está el equivalente para programar?
fuente
Respuestas:
Claro: Joel Spolsky (puede que hayas oído hablar de él) dice :
También, vea KISS y YAGNI que Jeff Atwood discute :
(ver el enlace para la cotización exacta)
Mi interpretación de estas citas:
Es realmente difícil crear buenas abstracciones: es mucho más fácil crear cosas malas.
Si agrega una abstracción para la que no hay necesidad, o que es incorrecta, ahora tiene un código adicional que necesita ser probado, depurado y mantenido. Y si tiene que regresar y refactorizar, ahora tiene más peso muerto que lo arrastra hacia abajo.
fuente
No estoy familiarizado con el lenguaje o el tiempo de ejecución de sus ejemplos, pero
gsub
no significa nada para mí. Sin embargo,escape_space
es mucho más significativo. Estoy completamente en desacuerdo con su argumento de que las llamadas a funciones familiares no deberían reemplazarse por llamadas a funciones que nunca antes habían visto . Si tuviera que llevar este argumento a los extremos, nunca se debería agregar una nueva función a un programa: siempre abstrae las llamadas de funciones familiares y siempre las reemplaza con esta nueva llamada desconocida definida por el usuario.Un desarrollador nunca debería tener que leer el código para comprender cómo funciona. En primer lugar, él o ella no debería tener que preocuparse por cómo funciona, sino que debería ser capaz de comprender cómo usarlo y qué efectos tiene. Si este no es el caso, entonces hay un problema con el nombre y la firma de la función, o su documentación.
Para mí, el objetivo del resumen es eliminar los detalles internos y proporcionar una interfaz más limpia para algunas funciones. En el futuro, el funcionamiento interno de la
escape_space
función podría modificarse o anularse. No me importa que alguna funcióngsub
se llame con dos argumentos. Me importa que mis espacios se escapen. Eso hace que la abstracción sea útil.En mi lenguaje favorito, C #, siempre agrego documentación a todas mis funciones (incluso las privadas) que describen cosas como la función, el uso, las unidades usadas, las excepciones lanzadas, el contrato de entrada y los tipos. Luego, con IntelliSense de Visual Studio, cualquier desarrollador puede ver más claramente lo que hace la función sin leer el código . Sin una herramienta como IntelliSense, los desarrolladores tendrán que ser más específicos en sus nombres de funciones, o mantener documentación adicional en algún lugar fácilmente accesible.
No creo que las abstracciones deban ser limitadas, y no conozco esa fuente.
fuente
sed
y vim:s
), 'g' es utilizado como "todos"gsub
,atoi
ywcstombs
son mucho peores de lo nombradoescape_space
."something".gsub(' ', '\ ')
que sabe exactamente qué salida está obteniendo. Escapar de espacios no es algo que veo muy a menudo. ¿Está usando el carácter `\` o algún otro carácter de escape? ¿Se escapa de las nuevas líneas? Sin mencionar que es un parche de la clase String predeterminada, que algunos desarrolladores consideran una mala práctica.Cuando hablamos de abstracciones, creo que es importante definir dos términos: complejidad inherente e incidental. La complejidad inherente es la complejidad del problema que la abstracción está resolviendo, la complejidad incidental es la complejidad que la abstracción está ocultando.
Las buenas abstracciones son aquellas que ocultan mucha complejidad incidental y tratan de resolver los problemas correctos, disminuyendo su complejidad inherente. Creo que con lo que te estás frustrando aquí son abstracciones demasiado superficiales; no ocultan mucha complejidad incidental y es tan difícil (si no más) comprender las abstracciones como comprender sus implementaciones.
fuente
Mi respuesta va a ir más allá de la experiencia personal, pero una búsqueda rápida encontró este blog , que parece que entra en bastante detalles sobre cuándo la abstracción va demasiado lejos (no solo la entrada vinculada, sino que hay varias relacionadas).
Básicamente, la abstracción puede ser algo bueno. Sin embargo, como cualquier otra cosa, uno puede dejarse llevar. Sus ejemplos son buenos donde la abstracción ha ido demasiado lejos. Estás creando "pasadas" que básicamente no hacen más que cambiar el nombre de la función en cuestión.
Ahora, en el caso de las funciones personalizadas, esto podría ser necesario, si está buscando desaprobar un antiguo esquema de nomenclatura en favor de uno nuevo (como cambiar escape_space a escapeSpace o escWhitespace, o lo que sea). ¿Pero para las funciones de biblioteca? Exceso Sin mencionar contraproducente. ¿Qué has ganado haciendo esa abstracción? Si eso es todo lo que está haciendo, entonces todo lo que ha ganado es el dolor de cabeza de tener que recordar esa función y transmitir ese conocimiento a otra persona (mientras que, con las funciones de biblioteca, los desarrolladores ya las conocen o deberían poder escribir
ruby gsub
en Google y descubre lo que significa).Recomiendo ser más inteligente para determinar cuándo abstraer algo. Por ejemplo, las cosas que son más de 2-3 líneas, y se usan al menos dos veces (incluso si existen pequeñas diferencias, muchas veces, esas diferencias pueden ser parametrizadas), generalmente son buenos candidatos para la abstracción. A veces, consideraré grandes bloques de código que hacen una cosa en particular dentro de una función más grande, en aras de la legibilidad y el principio de "un solo trabajo", y los resumiré en funciones protegidas dentro de la misma clase (a menudo en la inmediata proximidad de la función que lo utiliza).
Además, no olvides YAGNI (no lo vas a necesitar) y la optimización prematura. ¿Se está usando actualmente más de una vez? ¿No? Entonces no se preocupe por resumirlo ahora (a menos que hacerlo cree una ganancia de legibilidad sustancial). "¡Pero podríamos necesitarlo más tarde!" Luego, resúmalo cuando realmente lo necesites en otro lugar. Hasta entonces, terminas con punteros a punteros a punteros sin ningún beneficio real.
En el otro lado de esta moneda, cuando abstraes algo, hazlo tan pronto como la necesidad se haga evidente. Es más fácil mover y ajustar dos o tres bloques de código que 15. En términos generales, tan pronto como me encuentro copiando bloques de código de otro lugar, o escribiendo algo que me resulta muy familiar, empiezo a pensar en cómo abstraerlo.
fuente
Primero, eche un vistazo a este ejemplo de Robert C. Martin:
http://blog.objectmentor.com/articles/2009/09/11/one-thing-extract-till-you-drop
La razón de este tipo de refactorización es, en consecuencia, construir abstracciones para crear funciones muy pequeñas, de modo que cada función haga una sola cosa . Eso lleva a funciones como las que nos mostró anteriormente. Creo que esto es bueno: le ayuda a crear código donde cada función llamada dentro de otra función está en el mismo nivel de abstracción.
Si cree que usar nombres como
make_foo_binary
oescape_space
hace que el código sea menos legible que como consecuencia, debería intentar elegir mejores nombres (por ejemplo:) enescape_space_chars_with_backslash
lugar de abandonar la abstracción.fuente