Según tengo entendido, el diseño de arriba hacia abajo es refinando el concepto abstracto de alto nivel en concreto más pequeño y partes comprensibles, hasta que se defina el bloque de construcción más pequeño. Por otro lado, bottom up define partes de bajo nivel, luego gradualmente construye bloques de mayor nivel hasta que se forma todo el sistema.
En la práctica, se dice que es mejor combinar los dos métodos: comienza con una especificación de alto nivel para especificar completamente el conocimiento del dominio, su relación y restricciones. Una vez que se comprende bien el problema, se crean los bloques de construcción más pequeños para construir el sistema.
El proceso de:
- Crear especificaciones de requisitos
- Crear una especificación de diseño (con diagramas)
- Implementar
- Entregar
- Repetir (en el desarrollo iterativo, en lugar de hacer una parte completa en cada fase, hacemos un poco cada una repetidamente y tenemos una reunión diaria para adaptarnos a los requisitos dinámicos del cliente)
me parece perfectamente normal (con especificaciones como planes). Tiene sus defectos, pero es por eso que obtuvimos un desarrollo iterativo: en lugar de perder tiempo en una fase, dice, el análisis de requisitos para estudiar todo lo posible en el conocimiento del dominio que está sujeto a cambios (posiblemente a diario), hacemos un poco de análisis, un poco de diseño y luego implementarlo.
Otra forma es que cada iteración es una moda de mini-cascada, donde el análisis se realiza en unos pocos días (o una semana). Lo mismo se aplica para el diseño. El resto del tiempo se dedica a la implementación. ¿Hay algo inherentemente incorrecto con el enfoque de arriba hacia abajo en combinación con el desarrollo iterativo?
En su ensayo Programming Bottom Up , Paul Graham parece alentar la construcción desde abajo hacia arriba por completo, o programarla desde abajo hacia arriba, pero no la fase de análisis / diseño de requisitos:
Los programadores experimentados de Lisp dividen sus programas de manera diferente. Además del diseño de arriba hacia abajo, siguen un principio que podría llamarse diseño de abajo hacia arriba: cambiar el idioma para adaptarse al problema.
En lo que a mí respecta, lo que quiso decir es que Lisper todavía realiza el diseño de arriba hacia abajo, pero el programa de abajo hacia arriba, ¿es eso cierto? Otro punto que escribió:
Vale la pena enfatizar que el diseño ascendente no significa simplemente escribir el mismo programa en un orden diferente. Cuando trabajas de abajo hacia arriba, generalmente terminas con un programa diferente. En lugar de un solo programa monolítico, obtendrá un lenguaje más grande con operadores más abstractos y un programa más pequeño escrito en él. En lugar de un dintel, obtendrás un arco.
¿Esto significa que durante el período de escritura de un programa en Lisp, terminas con una herramienta genérica?
Respuestas:
De arriba hacia abajo es una excelente manera de describir cosas que sabes o de reconstruir cosas que ya has construido.
El mayor problema de arriba hacia abajo es que, con frecuencia, simplemente no hay "arriba". Cambiará de opinión sobre lo que debe hacer el sistema al desarrollarlo y al explorar el dominio. ¿Cómo puede ser su punto de partida algo que no sabe (es decir, qué quiere que haga el sistema)?
Un "local" de arriba hacia abajo es algo bueno ... pensar un poco antes de la codificación es claramente bueno. Pero pensar y planificar demasiado no lo es, porque lo que está imaginando no es el escenario real (a menos que ya haya estado allí antes, es decir, si no está construyendo, sino reconstruyendo). De arriba hacia abajo global cuando se construyen cosas nuevas no tiene sentido.
El enfoque de abajo hacia arriba debe ser (globalmente) a menos que conozca el 100% del problema, solo necesita codificar la solución conocida y no le importa buscar posibles soluciones alternativas.
El enfoque de Lisp es el destilado ascendente. No solo construye de abajo hacia arriba, sino que también puede dar forma a los ladrillos de la forma en que los necesita. Nada está arreglado, la libertad es total. Por supuesto, la libertad asume la responsabilidad y puedes hacer cosas horribles haciendo un mal uso de este poder.
Pero un código horrible se puede escribir en cualquier idioma. Incluso en idiomas que tienen forma de jaulas para la mente, diseñados con la esperanza de que con esos idiomas incluso los monos puedan poner en marcha buenos programas (una idea tan errónea en tantos niveles que duele incluso con solo pensarlo).
Su ejemplo es sobre un servidor web. Ahora, en 2012, este es un problema bien definido, tiene especificaciones a seguir. Un servidor web es solo un problema de implementación. Especialmente si su objetivo es escribir un servidor web sustancialmente idéntico a los otros miles de servidores web que existen, entonces nada está realmente claro, excepto algunas minucias. Incluso su comentario sobre RSA sigue hablando de un problema claramente definido, con especificaciones formales.
Con un problema bien definido, con especificaciones formales y soluciones ya conocidas, la codificación solo se conecta en los puntos. De arriba hacia abajo está bien para eso. Este es el cielo del gerente de proyecto.
Sin embargo, en muchos casos no existe un enfoque conocido y probado que se utilice para conectar los puntos. En realidad, muy a menudo es difícil decir incluso cuáles son los puntos.
Supongamos, por ejemplo, que se le pide que instruya a una máquina de corte automática para alinear las piezas que se cortarán con un material impreso que no se ajuste perfectamente al logotipo repetitivo teórico. Se le entregan las partes e imágenes del material tomadas por la máquina.
¿Qué es una regla de alineación? Tú decides. ¿Qué es un patrón, cómo representarlo? Tú decides. ¿Cómo alinear las partes? Tú decides. ¿Se pueden "doblar" las piezas? Depende, algunos no y otros sí, pero, por supuesto, no demasiado. ¿Qué hacer si el material está demasiado deformado para que una parte lo corte aceptablemente? Tú decides. ¿Todos los rollos de material son idénticos? Por supuesto que no, pero no puede molestar al usuario para adaptar las reglas de alineación para cada rollo ... eso sería poco práctico. ¿Qué fotos están viendo las cámaras? El material, lo que sea que eso signifique ... puede ser color, puede ser negro sobre negro donde solo el reflejo de luz hace evidente el patrón. ¿Qué significa reconocer un patrón? Tú decides.
Ahora intente diseñar la estructura general de una solución para este problema y haga una cotización, en dinero y tiempo. Mi apuesta es que incluso la arquitectura de su sistema ... (sí, la arquitectura) estará equivocada. La estimación del costo y el tiempo serán números aleatorios.
Lo implementamos y ahora es un sistema que funciona, pero cambiamos de opinión sobre la forma misma del sistema muchas veces. Agregamos subsistemas completos que ahora ni siquiera se puede acceder desde los menús. Cambiamos los roles maestro / esclavo en los protocolos más de una vez. Probablemente ahora tengamos suficiente conocimiento para intentar reconstruirlo mejor.
Otras compañías, por supuesto, resolvieron el mismo problema ... pero a menos que esté en una de estas compañías, probablemente su proyecto detallado de arriba hacia abajo será una broma. Podemos diseñarlo de arriba hacia abajo. No puedes porque nunca lo hiciste antes.
Probablemente también puedas resolver el mismo problema. Trabajando de abajo hacia arriba sin embargo. Comenzando con lo que sabes, aprendiendo lo que no sabes y sumando.
Se desarrollan nuevos sistemas de software complejos, no diseñados. De vez en cuando, alguien comienza a diseñar un nuevo y complejo sistema de software mal especificado desde cero (tenga en cuenta que con un gran proyecto de software complejo solo hay tres posibilidades: a] la especificación es difusa, b] la especificación es incorrecta y contradictoria o c] ambos ... y más a menudo [c] es el caso).
Estos son los proyectos típicos de grandes empresas con miles y miles de horas arrojados solo a diapositivas de PowerPoint y diagramas UML. Invariablemente fallan por completo después de quemar cantidades embarazosas de recursos ... o, en un caso muy excepcional, finalmente entregan una pieza de software demasiado cara que implementa solo una pequeña parte de las especificaciones iniciales. Y ese software invariablemente es profundamente odiado por los usuarios ... no el tipo de software que compraría, sino el tipo de software que usa porque se ve obligado a hacerlo.
¿Significa esto que creo que deberías pensar solo en el código? Por supuesto no. Pero en mi opinión, la construcción debería comenzar desde abajo (ladrillos, código de concreto) y debería subir ... y su enfoque y atención al detalle deberían, en cierto sentido, "desvanecerse" a medida que se aleja de lo que tiene. A menudo, se presenta de arriba hacia abajo como si tuviera que poner el mismo nivel de detalle en todo el sistema a la vez: solo manténgalo dividiendo cada nodo hasta que todo sea obvio ... en los módulos de realidad, el subsistema "crece" a partir de subrutinas. Si no tiene experiencia previa en el problema específico, su diseño descendente de un subsistema, módulo o biblioteca será horrible. Puede diseñar una buena biblioteca una vez que sepa qué funciones poner, no al revés.
Muchas de las ideas de Lisp se están volviendo más populares (funciones de primera clase, cierres, tipeo dinámico por defecto, recolección de basura, metaprogramación, desarrollo interactivo), pero Lisp sigue siendo hoy (entre los lenguajes que conozco) bastante único en lo fácil que es dar forma al código por lo que necesitas
Los parámetros de palabras clave, por ejemplo, ya están presentes, pero si no estuvieran presentes, podrían agregarse. Lo hice (incluida la verificación de palabras clave en tiempo de compilación) para un compilador de Lisp de juguete con el que estoy experimentando y no requiere mucho código.
En cambio, con C ++, lo máximo que puede obtener es un grupo de expertos en C ++ que le dicen que los parámetros de palabras clave no son tan útiles, o una implementación de plantilla increíblemente compleja, rota y con respaldo medio que de hecho no es tan útil. ¿Son las clases C ++ objetos de primera clase? No, y no hay nada que puedas hacer al respecto. ¿Puedes tener introspección en tiempo de ejecución o en tiempo de compilación? No, y no hay nada que puedas hacer al respecto.
Esta flexibilidad de lenguaje de Lisp es lo que lo hace ideal para la construcción de abajo hacia arriba. Puede construir no solo subrutinas, sino también la sintaxis y la semántica del lenguaje. Y en cierto sentido, Lisp es de abajo hacia arriba.
fuente
No estoy seguro de cómo se aplicaría esta respuesta a Lisp, pero acabo de terminar de leer Principios, patrones y prácticas ágiles , y el autor, tío Bob , aboga firmemente por el enfoque de arriba hacia abajo para C # (también aplicable a C ++) con el que completamente de acuerdo.
Sin embargo, a diferencia de otras respuestas que llegaron a la conclusión de que el enfoque de arriba hacia abajo significa que en la primera interacción solo entrega documentos y diseño general, el libro apunta a otro método: TDD combinado con diseño evolutivo.
La idea es que comience desde arriba y defina sus niveles de abstracción más altos (o el más alto local) y tan pronto como se definan, haga que hagan un trabajo útil para que la función # 1 funcione de inmediato. Luego, a medida que agrega más y más funciones, refactoriza su código y evoluciona el diseño según sea necesario, sin perder de vista los principios SÓLIDOS.. De esta manera, no terminará con demasiadas capas de abstracción y no terminará con un diseño de bajo nivel que no se ajuste a la arquitectura general. Si no está seguro de lo que esto significa, el libro que mencioné anteriormente tiene un capítulo completo con un ejemplo donde los desarrolladores toman conceptos y comienzan con diagramas UML y clases de bajo nivel, solo para darse cuenta de que la mitad de esas clases no son necesarias una vez que la codificación realmente comienza. En esencia, con este enfoque, el código de bajo nivel se presiona naturalmente a medida que se definen más detalles de alto nivel en el proyecto.
Y, por último, si practica SOLID, no debe encontrarse con una situación en la que tenga definidas abstracciones de alto nivel y luego entrar en detalles y de repente descubrir que no hay OOD ni abstracciones. Eso no es realmente la culpa del diseño de arriba hacia abajo, sino de la ingeniería perezosa.
Si está interesado en leer más sobre XP y el diseño evolutivo, aquí hay una buena lectura de Martin Fowler, otro gran autor: "Is Design Dead?"
fuente
Para mí, los comentarios más importantes que Paul Graham hace en su artículo son estos:
O, como se conoce en los círculos de C ++: el diseño de la biblioteca es el diseño del lenguaje (Bjarne Stroustrup)
La idea principal del diseño de arriba hacia abajo es: primero planifica, luego codifica. Beanow tiene razón cuando escribe , que hay problemas cuando el código ejecutable llega tarde en el proceso. En el diseño ascendente siempre tiene código, y ese código puede ser probado.
Además, su código no es plano. Con eso quiero decir, tiende a tener más niveles de abstracciones más pequeñas. En el diseño de arriba hacia abajo, las personas a menudo terminan con grandes abstracciones hasta cierto nivel arbitrario y por debajo de eso no hay abstracciones en absoluto. El código diseñado desde abajo hacia arriba OTOH a menudo contiene menos control de bajo nivel y estructuras de datos, ya que es probable que se abstraigan.
fuente
Idealmente, escribir un programa en cualquier idioma, no solo Lisp, le permite escribir un conjunto completo de herramientas genéricas que pueden acelerarlo en su próximo programa o acelerar las mejoras en su actual.
En la práctica, hacer un seguimiento de estas herramientas puede ser difícil. La documentación es pobre y desorganizada y las personas que los conocen se van. En la práctica, reutilizar el código suele ser más problemático de lo que vale. Pero si el código está documentado y organizado adecuadamente, y los programadores se quedan (o mantienen su propio suministro de código útil), se puede ahorrar una gran cantidad de trabajo agarrando el código del almacén en lugar de reconstruirlo.
Todo el diseño tiene que estar de arriba hacia abajo o no sabría lo que estaba haciendo. ¿Estás construyendo un cobertizo o un automóvil? No puedes entender eso con un diseño ascendente. Pero si va a construir una rueda delantera izquierda, podría pensar que podría necesitar más ruedas más adelante, tanto para este proyecto como para otros. Y si construyes una rueda reutilizable, tendrás cuatro por el precio de uno. (Y 18 para ese tractor-remolque que está construyendo a continuación, todo gratis).
Tenga en cuenta que, a diferencia de los autos reales y las ruedas reales, si ha construido una "rueda" de software, ha construido un número infinito de ellas.
Más sobre el diseño de arriba hacia abajo: si bien debe comenzar con esto, me siento obligado a señalar que si no puede construir una rueda, debe averiguarlo antes de hacer mucho trabajo en su automóvil. Por lo tanto, debe trabajar de abajo hacia arriba casi en paralelo con el trabajo de arriba hacia abajo. Además, saber que puede construir una rueda puede sugerir muchos proyectos en los que no habría pensado antes, como el tractor-remolque. Creo que el enfoque de arriba hacia abajo tiene que dominar, pero con un toque muy ligero.
Entonces, para seguir parafraseando a Paul Graham, idealmente cuando escribes un programa terminas con muchas partes reutilizables que pueden ahorrar mucho tiempo tanto en el programa original como en otros. Para distanciarme un poco de Paul Graham, esto funciona en cualquier idioma (aunque algunos fomentan el proceso más que otros).
El enemigo de este proceso casi mágico son los programadores con perspectivas a muy corto plazo. Esto puede deberse a defectos de personalidad, pero más comúnmente al cambiar de trabajo y responsabilidades demasiado rápido, tanto dentro como entre empresas que lo emplean.
fuente
El uso de un enfoque de arriba hacia abajo con desarrollo iterativo no entrega ningún código de trabajo en las primeras iteraciones. Ofrece documentos de diseño y de tal forma que el cliente tendrá dificultades para dar su opinión. Respuestas como, "sí, supongo (esto es demasiado abstracto para mí)" no lo ayudarán a especificar los detalles del sistema deseado por parte del cliente.
En cambio, solo crea una descripción general de lo que se solicita. (Requisitos) para usar como una guía general para lo que define un producto terminado y lo que merece prioridad para implementar. A partir de ahí, crea productos de trabajo para cada iteración con los que el cliente puede jugar para ver si esto era lo que tenía en mente. De lo contrario, no será un problema, ya que no se han dedicado cientos de horas a diseñar un sistema que funcione de manera diferente a lo que el cliente pide ahora.
No hablaré por otra persona. Tampoco leí su ensayo. La respuesta que te doy proviene de mi educación y de mis experiencias.
El uso de este enfoque significa que terminará con más bloques de construcción abstractos. Simplemente porque tendrá que construir subsistemas independientes que se puedan presentar de inmediato, no puede crear un diseño de arriba hacia abajo que esté muy entrelazado y deba implementarse de una vez. Mejora la reutilización, el mantenimiento, pero sobre todo permite una respuesta más flexible al cambio.
fuente