programming example art language-agnostic oop abstraction

language agnostic - example - ¿Cuánta abstracción es demasiado?



encapsulation oop example (8)

En un programa orientado a objetos: ¿cuánta abstracción es demasiado? ¿Cuánto es justo?

Siempre he sido un tipo loco y perno. Entendí el concepto detrás de altos niveles de encapsulación y abstracción, pero siempre sentí instintivamente que agregar demasiado confundiría el programa.

Siempre traté de capturar una cantidad de abstracción que no dejara clases o capas vacías. Y en caso de duda, en lugar de agregar una nueva capa a la jerarquía, trataría de encajar algo en las capas existentes.

Sin embargo, recientemente he estado encontrando sistemas más altamente abstractos. Sistemas en los que todo lo que podría requerir una representación más adelante en la jerarquía obtiene uno por adelantado. Esto lleva a muchas capas vacías, lo que al principio parece un mal diseño. Sin embargo, pensándolo bien, me he dado cuenta de que dejar esas capas vacías te da más lugares para engancharte en el futuro sin mucha refactorización. Le deja una mayor capacidad para agregar una nueva funcionalidad sobre la anterior sin hacer casi tanto trabajo para ajustar la anterior.

Los dos riesgos de esto parecen ser que podrías obtener las capas que necesitas mal. En este caso, uno terminaría necesitando una refactorización sustancial para extender el código y aún tendría toneladas de capas nunca usadas. Pero dependiendo de cuánto tiempo dediques a las abstracciones iniciales, la posibilidad de arruinarlo y el tiempo que podrías ahorrar más adelante si lo haces bien, puede que valga la pena intentarlo.

El otro riesgo que puedo pensar es el riesgo de hacerlo y nunca necesitar todas las capas adicionales. ¿Pero eso es realmente tan malo? ¿Son las capas de clase extra realmente tan caras que es una gran pérdida si nunca se usan? El mayor gasto y pérdida aquí sería el tiempo que se pierde en la delantera al aparecer las capas. Pero gran parte de ese tiempo todavía podría guardarse más adelante cuando se pueda trabajar con el código abstraído en lugar de con un código de bajo nivel.

Entonces, ¿cuándo es demasiado? ¿En qué punto las capas vacías y las abstracciones adicionales "podrían necesitar" se vuelven excesivas? ¿Qué tan pequeño es muy poco? ¿Dónde está el punto dulce?

¿Hay alguna regla de oro confiable que hayas encontrado en el transcurso de tu carrera que te ayude a juzgar la cantidad de abstracción necesaria?


¿Qué tan pequeño es muy poco?

Cuando trabajas con elementos de "bajo nivel" de forma rutinaria y sientes constantemente que no quieres hacerlo. Abstraerlos de distancia.

Entonces, ¿cuándo es demasiado?

Cuando no puede hacer fragmentos de algunas partes del código de forma regular y tiene que depurarlas hasta la capa anterior. Sientes que esta capa en particular no aporta nada, solo un obstáculo. Déjalo caer.

¿Dónde está el punto dulce?

Me gusta aplicar el enfoque pragmático. Si ves la necesidad de una abstracción y entiendes cómo mejorará tu vida, adelante. Si ha escuchado que debería haber "oficialmente" una capa extra de abstracción pero no está claro por qué, no lo haga, pero investigue primero. Si alguien insiste en abstraer algo pero no puede explicar claramente lo que traerá, dígales que se vayan.


Entonces, ¿cuándo es demasiado? ¿En qué punto las capas vacías y las abstracciones adicionales "podrían necesitar" se vuelven excesivas? ¿Qué tan pequeño es muy poco? ¿Dónde está el punto dulce?

No creo que haya una respuesta definitiva a estas preguntas. Se necesita experiencia para desarrollar una sensación de lo que es "demasiado" y "muy poco". Tal vez el uso de algunas herramientas de control de calidad o métricas puede ayudar, pero es difícil generalizar. Depende principalmente de cada caso.

Aquí hay algunos enlaces que pueden inspirarte en la búsqueda de respuestas:

El desarrollo se trata de encontrar el equilibrio adecuado entre las diversas tensiones que están presentes en cualquier esfuerzo de ingeniería de software.


Cada abstracción que no se utiliza en realidad es demasiado. Cuanto más simple es un sistema, más fácil es cambiarlo. Las capas de abstracción casi siempre hacen que los sistemas sean más complicados.

OTOH, ciertamente es posible programar un desorden complejo e inmanejable sin ningún tipo de abstracción, y algunas veces, las capas de abstracción "estandarizadas" pueden ayudar a estructurar un sistema mejor de lo que la mayoría de la gente podría hacer por sí misma.


El objetivo de las abstracciones es factorizar las propiedades comunes de las específicas, como en la operación matemática:

ab + ac => a(b + c)

Ahora haces lo mismo con dos operaciones en lugar de tres. Este factoring hizo nuestra expresión más simple.

Un ejemplo típico de abstracción es el sistema de archivos. Por ejemplo, desea que su programa pueda escribir en muchos tipos de dispositivos de almacenamiento: pen drives, tarjetas SD, discos duros, etc.

Si no tuviéramos un sistema de archivos, tendríamos que implementar la lógica de escritura del disco directo, la lógica de escritura del pen drive y la lógica de escritura de la tarjeta SD. Pero todas estas lógicas tienen algo en común: crean archivos y directorios, por lo que estas cosas comunes se pueden abstraer, creando una capa de abstracción y proporcionando una interfaz al proveedor de hardware para hacer las cosas específicas.

Cuanto más las cosas comparten una propiedad común. La más beneficiosa puede ser una abstracción:

ab + ac + ad + ae + af

a:

a(b + c + d + e + f)

Esto reduciría las 9 operaciones a 5.

Básicamente, cada buena abstracción aproximadamente reduce a la mitad la complejidad de un sistema.

Siempre necesita al menos dos cosas que compartan una propiedad común para hacer una abstracción útil. Por supuesto, separa una sola cosa para que parezca una abstracción, pero no significa que sea útil:

10 => 5 * 2

No puede definir la palabra "común" si tiene solo una entidad.

Entonces para responder a tu pregunta. Tienes suficientes abstracciones si hacen que tu sistema sea lo más simple posible.

(En mis ejemplos, la adición conecta las partes del sistema, mientras que la multiplicación define una relación abstracta-concreta).


En pocas palabras, hay demasiada abstracción si el código es difícil de entender.

Ahora bien, esto no significa que deba codificar todo, porque es el código más fácil de escribir y leer.

La prueba más fácil es dejarlo de lado por unos días, volver a levantarlo y pregúntese si esto tiene algún sentido. Un mejor enfoque es dárselo a otra persona, y ver si puede entenderlo o no.


En teoría, debería ser una cuestión de matemática simple usando solo tres variables (bastante simples):

  • S = ahorro de uso
  • C = costo de las abstracciones adicionales
  • P = probabilidad de uso

Si S * P> C, entonces el código es bueno. Si S * P <C, entonces es malo.

La razón que es puramente teórica, sin embargo, es que generalmente no puede adivinar la probabilidad de uso o los ahorros que obtendrá al usarla. Peor aún, no puedes adivinar o, incluso, ni siquiera medir el costo de su presencia.

Al menos algunas personas han sacado una conclusión de esto. En TDD, el mantra estándar es "no lo vas a necesitar" (YAGNI). En pocas palabras, cualquier cosa que no contribuya directamente con el código que cumpla con sus requisitos actuales se considera algo malo. En esencia, han llegado a la conclusión de que la probabilidad de uso es tan baja, que incluir ese código adicional nunca se justifica.

Algo de esto regresa al desarrollo de "abajo hacia arriba" versus "de arriba hacia abajo". Tiendo a pensar en el desarrollo de abajo hacia arriba como el "desarrollo de la biblioteca", es decir, en lugar de desarrollar una aplicación específica, realmente está desarrollando bibliotecas para el tipo de cosas que necesitará en la aplicación. El pensamiento es que con una biblioteca lo suficientemente buena, puedes desarrollar casi cualquier aplicación de ese tipo general con relativa facilidad.

Un poco también depende del tamaño del proyecto. Los grandes proyectos que permanecen en uso durante décadas justifican una inversión mucho más a largo plazo que los proyectos más pequeños que se descartan y se reemplazan mucho más rápidamente. Esto tiene análogos obvios en la vida real también. No se preocupe tanto por el ajuste, el acabado o la mano de obra en una afeitadora desechable que va a tirar en menos de una semana como lo hace en un auto nuevo que va a utilizar en los próximos años. .


La realidad es que depende de cuán bien puedas mirar hacia el futuro. Desea planificar los cambios que puede prever sin crear demasiadas capas adicionales. Si tiene un diseño que transfiere datos entre sistemas, proceda y cree una interfaz y use la implementación planificada como la predeterminada. Por ejemplo, usa FTP para mover archivos pero sabe que el estándar estará basado en mensajes (o lo que sea) el próximo año.

En cuanto a las capas dentro del diseño, a veces las capas agregadas hacen que sea más fácil escribir clases más pequeñas. Está bien agregar capas conceptuales si significa que las clases concretas se vuelven sencillas.


Ver el artículo (6a) de RFC 1925 y saber que es verdad. Los únicos problemas que no puede corregir al agregar capas de abstracción son los causados ​​por tener demasiadas capas de abstracción. (En particular, cada pieza de abstracción hace que todo sea más difícil de entender).