Arquitectura Limpia Pagenumber Pagenumber
Arquitectura Limpia Pagenumber Pagenumber
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 2 2
EPUB es un formato abierto estándar de la industria para libros electrónicos. Sin embargo, la compatibilidad
con EPUB y sus numerosas funciones varía según los dispositivos de lectura y las aplicaciones. Use la
configuración de su dispositivo o aplicación para personalizar la presentación a su gusto. Las configuraciones
que puede personalizar a menudo incluyen fuente, tamaño de fuente, columna simple o doble, modo
horizontal o vertical, y figuras en las que puede hacer clic o tocar para ampliar. Para obtener información
adicional sobre la configuración y las funciones de su dispositivo o aplicación de lectura, visite el sitio web
del fabricante del dispositivo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 3 3
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 4 4
Arquitectura limpia
UNA GUÍA DEL ARTESANO SOBRE LA ESTRUCTURA DEL SOFTWARE
Y DISEÑO
Roberto C. Martín
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 5 5
Muchas de las designaciones utilizadas por los fabricantes y vendedores para distinguir sus
productos se reclaman como marcas comerciales. Donde esas designaciones aparecen en este libro, y
el editor estaba al tanto de un reclamo de marca registrada, las designaciones se han impreso con
letras mayúsculas iniciales o en mayúsculas.
El autor y el editor se han ocupado de la preparación de este libro, pero no ofrecen garantías expresas
o implícitas de ningún tipo y no asumen ninguna responsabilidad por errores u omisiones. No se asume
ninguna responsabilidad por daños incidentales o consecuentes en relación con o que surjan del uso de la
información o los programas contenidos en este documento.
Para obtener información sobre la compra de este título en grandes cantidades o para
oportunidades de ventas especiales (que pueden incluir versiones electrónicas, diseños de portada
personalizados y contenido específico para su negocio, objetivos de capacitación, enfoque de
marketing o intereses de marca), comuníquese con nuestro departamento de ventas corporativas. en
[email protected] o (800) 382-3419.
Si tiene preguntas sobre las ventas fuera de los EE. UU., comuníquese con [email protected].
Reservados todos los derechos. Impreso en los Estados Unidos de América. Esta publicación está
protegida por derechos de autor y se debe obtener el permiso del editor antes de cualquier reproducción,
almacenamiento en un sistema de recuperación o transmisión prohibida en cualquier forma o por
cualquier medio, ya sea electrónico, mecánico, fotocopiado, grabación o similar.
Para obtener información sobre permisos, formularios de solicitud y los contactos apropiados dentro
del Departamento de Permisos y Derechos Globales de Pearson Education, visite www.pearsoned.com/
permissions/.
ISBN-13: 978-0-13-449416-6
ISBN-10: 0-13-449416-4
1 17
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 6 6
Este libro está dedicado a mi encantadora esposa, mis cuatro hijos espectaculares
y sus familias, incluida mi carcaza llena de cinco nietos, que son el postre de mi
vida.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 7 7
CONTENIDO
Prefacio
Prefacio
Expresiones de gratitud
Sobre el Autor
PARTE I Introducción
Arquitectura
El mayor valor
Matriz de Eisenhower
Lucha por la Arquitectura
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 8 8
Conclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 9 9
Capítulo 12 Componentes
Una breve historia de los componentes
Reubicación
Enlazadores
Conclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 10 10
PARTE V Arquitectura
Capítulo 16 Independencia
Casos de uso
Operación
Desarrollo
Despliegue
Dejar opciones abiertas
Capas de desacoplamiento
Casos de uso de desacoplamiento
Modo de desacoplamiento
Capacidad de desarrollo independiente
Capacidad de implementación independiente
Duplicación
Modos de desacoplamiento (otra vez)
Conclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 11 11
El temido monolito
Componentes de implementación
Hilos
Procesos Locales
Servicios
Conclusión
Asignadores de datos
Oyentes de servicio
Conclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 12 12
La API de prueba
Conclusión
PARTE VI Detalles
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 13 13
Conclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 14 14
Índice
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 15 15
PREFACIO
Como con cualquier metáfora, describir el software a través de la lente de la arquitectura puede
ocultar tanto como revelar. Puede prometer más de lo que puede entregar y entregar más de lo
que promete.
Los edificios tienen una estructura física obvia, ya sea que estén arraigados en piedra u
hormigón, ya sean arcos altos o anchos, grandes o pequeños, magníficos o mundanos. Sus
estructuras no tienen más remedio que respetar la física de la gravedad y sus materiales. Por otro
lado, excepto en su sentido de seriedad, el software tiene poco tiempo para la gravedad. ¿Y de qué
está hecho el software? A diferencia de los edificios, que pueden estar hechos de ladrillos, hormigón,
madera, acero y vidrio, el software está hecho de software. Las construcciones de software grandes
están hechas de componentes de software más pequeños, que a su vez están hechos de
componentes de software aún más pequeños, y así sucesivamente.
Está codificando tortugas hasta el final.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 16 16
de estructuras, pero su variedad eclipsa el rango de estructura física que se encuentra en los edificios.
Incluso puede argumentar de manera bastante convincente que hay más actividad de diseño y enfoque en el
software que en la construcción de arquitectura; en este sentido, ¡no es irrazonable considerar la arquitectura de
software más arquitectónica que la arquitectura de construcción!
Pero la escala física es algo que los humanos entienden y buscan en el mundo.
Aunque atractivos y visualmente obvios, los recuadros en un diagrama de PowerPoint no son la arquitectura de un
sistema de software. No hay duda de que representan una visión particular de una arquitectura, pero confundir las cajas
con el panorama general, con la arquitectura, es perder el panorama general y la arquitectura: la arquitectura de
software no se parece a nada. Una visualización particular es una elección, no un hecho. Es una elección basada en un
conjunto adicional de opciones: qué incluir; qué excluir; qué enfatizar por forma o color; qué restar importancia a través
de la uniformidad o la omisión. No hay nada natural o intrínseco en una visión sobre otra.
Aunque puede que no tenga sentido hablar de física y escala física en la arquitectura de software,
apreciamos y nos preocupamos por ciertas limitaciones físicas.
La velocidad del procesador y el ancho de banda de la red pueden dar un veredicto severo sobre el rendimiento
de un sistema. La memoria y el almacenamiento pueden limitar las ambiciones de cualquier base de código.
El software puede ser material del que están hechos los sueños, pero se ejecuta en el mundo físico.
Esta es la monstruosidad en el amor, señora, que la voluntad es infinita, y la ejecución confinada; que
el deseo es ilimitado, y el acto esclavo del límite.
-William Shakespeare
El mundo físico es donde vivimos nosotros, nuestras empresas y nuestras economías. Esto nos da otra
calibración por la que podemos entender la arquitectura de software, otras fuerzas y cantidades menos físicas a
través de las cuales podemos hablar y razonar.
La arquitectura representa las decisiones de diseño significativas que dan forma a un sistema,
donde la importancia se mide por el costo del cambio.
—Grady Booch
El tiempo, el dinero y el esfuerzo nos dan un sentido de escala para clasificar entre lo grande y lo pequeño, para
distinguir el material arquitectónico del resto. Esta medida también nos dice cómo podemos determinar si una
arquitectura es buena o no: una buena arquitectura no solo satisface las necesidades de sus usuarios, desarrolladores
y propietarios en un momento determinado, sino que también las satisface a lo largo del tiempo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 17 17
Los tipos de cambios que experimenta típicamente el desarrollo de un sistema no deben ser los cambios que son
costosos, que son difíciles de realizar, que requieren proyectos administrados por sí mismos en lugar de integrarse en
el flujo de trabajo diario y semanal.
Ese punto nos lleva a un problema no tan pequeño relacionado con la física: el viaje en el tiempo. ¿Cómo sabemos
cuáles serán esos cambios típicos para que podamos dar forma a esas decisiones importantes en torno a ellos?
¿Cómo reducimos el esfuerzo y el costo de desarrollo futuro sin bolas de cristal ni máquinas del tiempo?
La arquitectura son las decisiones que desearía poder tomar bien al principio de un proyecto, pero
que no es necesariamente más probable que las tome bien que cualquier otra.
—Ralph Johnson
Comprender el pasado es bastante difícil tal como es; nuestra comprensión del presente es, en el mejor de los casos,
resbaladiza; predecir el futuro no es trivial.
Por el camino más oscuro viene la idea de que la arquitectura fuerte y estable proviene de la autoridad y la rigidez. Si
el cambio es costoso, el cambio se elimina: sus causas se someten o se desvían hacia una zanja burocrática. El
mandato del arquitecto es total y totalitario, con la arquitectura convirtiéndose en una distopía para sus desarrolladores
Por otro camino llega un fuerte olor a generalidad especulativa. Una ruta llena de conjeturas codificadas,
innumerables parámetros, tumbas de código muerto y más complejidad accidental de la que puede sacudir un
presupuesto de mantenimiento.
El camino que más nos interesa es el más limpio. Reconoce la suavidad del software y pretende preservarlo
como una propiedad de primera clase del sistema. Reconoce que operamos con un conocimiento incompleto, pero
también entiende que, como humanos, operar con un conocimiento incompleto es algo que hacemos, algo en lo que
somos buenos.
Juega más con nuestras fortalezas que con nuestras debilidades. Creamos cosas y descubrimos cosas.
Hacemos preguntas y hacemos experimentos. Una buena arquitectura surge de entenderla más como un viaje
que como un destino, más como un proceso continuo de investigación que como un artefacto congelado.
Recorrer este camino requiere cuidado y atención, pensamiento y observación, práctica y principios. Al principio, esto
puede sonar lento, pero todo depende de la forma en que caminas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 18 18
Disfruta el viaje.
—Kevlin Henney
mayo 2017
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 19 19
PREFACIO
El título de este libro es Arquitectura Limpia. Ese es un nombre audaz. Algunos incluso lo llamarían arrogante.
Entonces, ¿por qué elegí ese título y por qué escribí este libro?
Escribí mi primera línea de código en 1964, a la edad de 12 años. Ahora es el año 2016, así que he estado
escribiendo código durante más de medio siglo. En ese tiempo, aprendí algunas cosas sobre cómo estructurar
sistemas de software, cosas que creo que otros probablemente encontrarán valiosas.
Aprendí estas cosas construyendo muchos sistemas, tanto grandes como pequeños. He construido pequeños
sistemas integrados y grandes sistemas de procesamiento por lotes. He construido sistemas de tiempo real y
sistemas web. He creado aplicaciones de consola, aplicaciones GUI, aplicaciones de control de procesos, juegos,
sistemas de contabilidad, sistemas de telecomunicaciones, herramientas de diseño, aplicaciones de dibujo y
muchas, muchas otras.
He creado aplicaciones de subproceso único, aplicaciones de subprocesos múltiples, aplicaciones con pocos
procesos pesados, aplicaciones con muchos procesos livianos, aplicaciones multiprocesador, aplicaciones de
bases de datos, aplicaciones matemáticas, aplicaciones de geometría computacional y muchas, muchas otras.
He creado muchas aplicaciones. He construido muchos sistemas. Y de todos ellos, y tomándolos a todos en
consideración, he aprendido algo sorprendente.
Esto es sorprendente porque los sistemas que he construido han sido radicalmente diferentes. ¿Por qué
sistemas tan diferentes deberían compartir reglas de arquitectura similares?
Mi conclusión es que las reglas de la arquitectura del software son independientes de cualquier otra variable.
Esto es aún más sorprendente cuando se considera el cambio que ha tenido lugar en
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 20 20
hardware durante el mismo medio siglo. Empecé a programar en máquinas del tamaño de
refrigeradores de cocina que tenían tiempos de ciclo de medio megahercio, 4K de memoria central,
32K de memoria de disco y una interfaz de teletipo de 10 caracteres por segundo. Estoy escribiendo
este prefacio en un autobús durante una gira por Sudáfrica. Estoy usando una MacBook con cuatro
núcleos i7 que funcionan a 2,8 gigahercios cada uno. Tiene 16 gigabytes de RAM, un terabyte de
SSD y una pantalla retina de 2880 × 1800 capaz de mostrar video de extremadamente alta definición.
La diferencia en el poder computacional es asombrosa. Cualquier análisis razonable mostrará
que este MacBook es al menos 1022 veces más poderoso que las primeras computadoras que
comencé a usar hace medio siglo.
Y con todo ese gran cambio en el poder computacional, ¿cuál ha sido el efecto en el software
que escribo? Seguro que se ha hecho más grande. Solía pensar que 2000 líneas era un gran
programa. Después de todo, era una caja llena de tarjetas que pesaba 10 libras. Ahora, sin embargo,
un programa no es realmente grande hasta que supera las 100.000 líneas.
El software también se ha vuelto mucho más eficaz. Hoy podemos hacer cosas con las que apenas
podíamos soñar en la década de 1960. The Forbin Project, The Moon Is a Harsh Mistress y 2001: A
Space Odyssey intentaron imaginar nuestro futuro actual, pero fallaron significativamente. Todos
imaginaron enormes máquinas que ganaron sensibilidad. En cambio, lo que tenemos son máquinas
increíblemente pequeñas que aún son... solo máquinas.{xx}
Y hay una cosa más sobre el software que tenemos ahora, en comparación con el software
de entonces: está hecho del mismo material. Está hecho de sentencias if , sentencias de
asignación y bucles while .
Oh, podría objetar y decir que tenemos lenguajes mucho mejores y paradigmas superiores.
Después de todo, programamos en Java, C# o Ruby, y usamos el diseño orientado a objetos. Cierto,
y sin embargo, el código sigue siendo solo un ensamblaje de secuencia, selección e iteración, tal
como lo era en las décadas de 1960 y 1950.
Cuando realmente miras de cerca la práctica de programar computadoras, te das cuenta de que
muy poco ha cambiado en 50 años. Los idiomas han mejorado un poco. Las herramientas han
mejorado fantásticamente. Pero los componentes básicos de un programa de computadora no han
cambiado.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 21 21
Si tomo a una programadora de 1966 en el tiempo hasta 2016 y la pongo frente a mi MacBook con
IntelliJ y le muestro Java, es posible que necesite 24 horas para recuperarse del impacto. Pero entonces
sería capaz de escribir el código. Java simplemente no es tan diferente de C, o incluso de Fortran.
Y si te transportara a 1966 y te mostrara cómo escribir y editar el código PDP-8 perforando una cinta de
papel en un teletipo de 10 caracteres por segundo, es posible que necesites 24 horas para recuperarte
de la decepción. Pero entonces serías capaz de escribir el código. El código simplemente no ha cambiado
tanto.
Ese es el secreto: esta inmutabilidad del código es la razón por la que las reglas de la arquitectura
del software son tan consistentes en todos los tipos de sistemas. Las reglas de la arquitectura del
software son las reglas para ordenar y ensamblar los componentes básicos de los programas. Y
dado que esos bloques de construcción son universales y no han cambiado, las reglas para ordenarlos
son igualmente universales e inmutables.
Los programadores más jóvenes podrían pensar que esto no tiene sentido. Podrían insistir
en que todo es nuevo y diferente hoy en día, que las reglas del pasado son cosa del pasado y se
han ido. Si eso es lo que piensan, están tristemente equivocados. Las reglas no han cambiado.
A pesar de todos los nuevos lenguajes, todos los nuevos marcos y todos los paradigmas, las reglas
son las mismas ahora que cuando Alan Turing escribió el primer código de máquina en 1946.
Pero una cosa ha cambiado: en ese entonces, no sabíamos cuáles eran las reglas.
En consecuencia, los rompimos, una y otra vez. Ahora, con medio siglo de experiencia a nuestras
espaldas, tenemos una idea de esas reglas.
Y son esas reglas, esas reglas atemporales e inmutables, de lo que trata este libro.
Registre su copia de Clean Architecture en el sitio de InformIT para acceder cómodamente a las
actualizaciones y/o correcciones a medida que estén disponibles. Para iniciar el proceso de
registro, vaya a informit.com/register e inicia sesión o crea una cuenta.
Introduzca el ISBN del producto (9780134494166) y haga clic en Enviar. Busque en la
pestaña Productos registrados un enlace Acceder a contenido de bonificación junto a este
producto y siga ese enlace para acceder a los materiales de bonificación.
1. Y es muy probable que sea mujer ya que, en aquel entonces, las mujeres constituían una gran fracción de
programadores
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 22 22
EXPRESIONES DE GRATITUD
Las personas que participaron en la creación de este libro, sin ningún orden en particular: {xxiii}
Chris Guzikowski
Chris Diente
matt heuser
jeff overbey
miqueas martin
justin martin
carl hickman
james grenning
Simón Marrón
kevlin henney
Jason Gorman
doug bradbury
colin jones
Grady Booch
Kent Beck
Martín Cazador
Alistair Cockburn
James O. Coplien
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 23 23
tim conrado
ricardo lloyd
Buscador de Ken
jerry fitzpatrick
jim newkirk
ed thelen
jose mabel
Bill Degnan
En mi revisión final de este libro, mientras leía el capítulo sobre la arquitectura que
grita, la sonrisa de ojos brillantes y la risa melódica de Jim Weirich resonaron en mi mente. ¡Buena
suerte, Jim!
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 24 24
SOBRE EL AUTOR
Martin ha escrito y editado muchos libros, incluidos The Clean Coder, Clean Code, UML
para programadores de Java, Desarrollo de software ágil, Programación extrema en la
práctica, Más gemas de C++, Lenguajes de patrones de diseño de programas 3 y Diseño de
aplicaciones de C++ orientadas a objetos utilizando Booch. Método.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 25 25
yo
INTRODUCCIÓN
No se necesita una gran cantidad de conocimientos y habilidades para que un programa funcione. Los
niños en la escuela secundaria lo hacen todo el tiempo. Hombres y mujeres jóvenes en la universidad
inician negocios de miles de millones de dólares basados en recopilar unas pocas líneas de PHP o Ruby.
Montones de programadores junior en granjas de cubos de todo el mundo se esfuerzan a través de
documentos de requisitos masivos que se encuentran en grandes sistemas de seguimiento de problemas
para que sus sistemas "funcionen" por la pura fuerza bruta de la voluntad. El código que producen puede
no ser bonito; pero funciona. Funciona porque hacer que algo funcione, una vez, no es tan difícil.
Hacerlo bien es otra cuestión completamente diferente. Obtener el software correcto es difícil. Se
necesitan conocimientos y habilidades que la mayoría de los programadores jóvenes aún no han adquirido.
Requiere pensamiento y perspicacia que la mayoría de los programadores no se toman el tiempo de
desarrollar. Requiere un nivel de disciplina y dedicación que la mayoría de los programadores nunca
soñaron que necesitarían. Sobre todo, se necesita pasión por el oficio y el deseo de ser un profesional.
Y cuando obtiene el software correcto, sucede algo mágico: no necesita hordas de programadores
para que siga funcionando. No necesita documentos de requisitos masivos ni grandes sistemas de
seguimiento de problemas. No necesita granjas de cubos globales y programación 24/7.
Cuando el software se hace correctamente, requiere una fracción de los recursos humanos para crearlo
y mantenerlo. Los cambios son simples y rápidos. Los defectos son pocos y distantes entre sí. Se minimiza
el esfuerzo y se maximizan la funcionalidad y la flexibilidad.
Sí, esta visión suena un poco utópica. Pero he estado allí; Lo he visto pasar. He trabajado en proyectos
en los que el diseño y la arquitectura del sistema facilitaban la escritura y el mantenimiento. He
experimentado proyectos que requerían una fracción del
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 26 26
Pero no confíes en mi palabra. Mira tu propia experiencia. ¿Has experimentado lo contrario? ¿Ha
trabajado en sistemas que están tan interconectados e intrincadamente acoplados que cada
cambio, por trivial que sea, lleva semanas e implica grandes riesgos? ¿Ha experimentado la
impedancia del código incorrecto y el diseño podrido? ¿Ha tenido el diseño de los sistemas en los
que ha trabajado un gran efecto negativo en la moral del equipo, la confianza de los clientes y la
paciencia de los gerentes? ¿Ha visto equipos, departamentos e incluso empresas que se han
derrumbado por la estructura podrida de su software? ¿Has estado en el infierno de la
programación?
Yo lo he hecho y, hasta cierto punto, la mayoría de nosotros también. Es mucho más común
abrirse camino a través de diseños de software terribles que disfrutar del placer de trabajar con
uno bueno.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 27 27
Ha habido mucha confusión sobre el diseño y la arquitectura a lo largo de los años. ¿Qué
es el diseño? ¿Qué es la arquitectura? ¿Cuáles son las diferencias entre los dos?
Uno de los objetivos de este libro es acabar con toda esa confusión y definir, de una vez
por todas, qué es el diseño y la arquitectura. Para empezar, afirmaré que no hay diferencia
entre ellos. Ninguno en absoluto.
La palabra "arquitectura" se usa a menudo en el contexto de algo de alto nivel que está
divorciado de los detalles de nivel inferior, mientras que "diseño" parece implicar más a
menudo estructuras y decisiones en un nivel inferior. Pero este uso no tiene sentido cuando
miras lo que hace un arquitecto real.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 28 28
¿arquitectura? Claro que lo hace. ¿Y qué es esa arquitectura? Bueno, es la forma de la casa, la apariencia
exterior, las elevaciones y el diseño de los espacios y habitaciones. Pero cuando miro a través de los
diagramas que produjo mi arquitecto, veo una inmensa cantidad de detalles de bajo nivel. Veo dónde se
colocarán cada enchufe, interruptor de luz y luz. Veo qué interruptores controlan qué luces. Veo dónde está
colocado el horno, y el tamaño y la ubicación del calentador de agua y la bomba de sumidero. Veo
representaciones detalladas de cómo se construirán las paredes, los techos y los cimientos.
En resumen, veo todos los pequeños detalles que respaldan todas las decisiones de alto nivel. También
veo que esos detalles de bajo nivel y decisiones de alto nivel son parte de todo el diseño de la casa.
Y lo mismo ocurre con el diseño de software. Los detalles de bajo nivel y la estructura de alto nivel son
todos parte del mismo todo. Forman un tejido continuo que define la forma del sistema. No puedes tener
uno sin el otro; de hecho, ninguna línea divisoria clara los separa. Simplemente hay un continuo de
decisiones desde los niveles más altos hasta los más bajos.
¿LA META?
¿Y el objetivo de esas decisiones? ¿El objetivo de un buen diseño de software? Ese objetivo es nada
menos que mi descripción utópica:
El objetivo de la arquitectura de software es minimizar los recursos humanos necesarios para construir
y mantener el sistema requerido.
La medida de la calidad del diseño es simplemente la medida del esfuerzo requerido para satisfacer las
necesidades del cliente. Si ese esfuerzo es bajo y se mantiene bajo durante la vida útil del sistema, el diseño
es bueno. Si ese esfuerzo crece con cada nuevo lanzamiento, el diseño es malo. Es tan simple como eso.
CASO DE ESTUDIO
Como ejemplo, considere el siguiente caso de estudio. Incluye datos reales de una empresa real que
desea permanecer en el anonimato.
Primero, veamos el crecimiento del personal de ingeniería. Estoy seguro de que estará de acuerdo en que
esta tendencia es muy alentadora. ¡Un crecimiento como el que se muestra en la Figura 1.1 debe ser una
indicación de un éxito significativo!
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 29 29
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 30 30
Claramente algo anda mal aquí. Aunque cada versión cuenta con el apoyo de un número cada vez
mayor de desarrolladores, parece que el crecimiento del código se está acercando a una asíntota.
Ahora aquí está el gráfico realmente aterrador: la figura 1.3 muestra cómo ha cambiado el costo por
línea de código con el tiempo.
Estas tendencias no son sostenibles. No importa qué tan rentable pueda ser la empresa en este
momento: esas curvas drenarán catastróficamente las ganancias del modelo de negocios y conducirán
a la empresa a un estancamiento, si no a un colapso total.
¿Qué causó este notable cambio en la productividad? ¿Por qué fue 40 veces más costoso producir
el código en la versión 8 en comparación con la versión 1?
LA FIRMA DE UN LÍO
Lo que estás mirando es la firma de un desastre. Cuando los sistemas se ensamblan a toda
prisa, cuando la gran cantidad de programadores es el único impulsor de
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 31 31
La figura 1.4 muestra cómo se ve esta curva para los desarrolladores. Comenzaron con
una productividad de casi el 100%, pero con cada lanzamiento su productividad disminuyó.
Para el cuarto lanzamiento, estaba claro que su productividad iba a tocar fondo en un
acercamiento asintótico a cero.
Y, sin embargo, a pesar de todos sus actos heroicos, horas extras y dedicación,
simplemente ya no logran hacer nada. Todo su esfuerzo se ha desviado de las características
y ahora se consume en la gestión del desorden. Su trabajo, tal como es, se ha convertido en
mover el desorden de un lugar a otro, y al siguiente, y al siguiente, para que puedan agregar
una pequeña característica más.
LA VISIÓN EJECUTIVA
Si crees que eso es malo, ¡imagínate cómo se ve esta imagen para los ejecutivos!
Considere la Figura 1.5, que muestra la nómina mensual de desarrollo para el mismo
período.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 32 32
El Release 1 se entregó con una nómina mensual de unos cientos de miles de dólares.
El segundo lanzamiento costó unos cientos de miles más. Para el octavo lanzamiento, la nómina
mensual era de $ 20 millones y seguía aumentando.
Solo este gráfico por sí solo da miedo. Es evidente que algo sorprendente está sucediendo. Uno
espera que los ingresos superen los costos y, por lo tanto, justifiquen el gasto. Pero no importa cómo
mires esta curva, es motivo de preocupación.
Pero ahora compare la curva de la Figura 1.5 con las líneas de código escritas por versión en la Figura
1.2. Esos cientos de miles de dólares iniciales por mes compraron una gran cantidad de funcionalidad,
¡pero los $ 20 millones finales compraron casi nada! Cualquier CFO miraría estos dos gráficos y sabría
que es necesaria una acción inmediata para evitar un desastre.
Pero, ¿qué acción se puede tomar? ¿Qué ha ido mal? ¿Qué ha causado esta increíble
disminución de la productividad? ¿Qué pueden hacer los ejecutivos, además de pisotear y enfadar a
los desarrolladores?
Hace casi 2600 años, Esopo contó la historia de la tortuga y la liebre. La moraleja de esa historia se
ha dicho muchas veces de muchas maneras diferentes:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 33 33
La historia misma ilustra la estupidez del exceso de confianza. La liebre, tan confiada en su velocidad
intrínseca, no se toma la carrera en serio, por lo que duerme la siesta mientras la tortuga cruza la línea
de meta.
Los desarrolladores modernos están en una carrera similar y exhiben un exceso de confianza
similar. Oh, no duermen, ni mucho menos. La mayoría de los desarrolladores modernos trabajan
duro. Pero una parte de su cerebro sí duerme, la parte que sabe que el código bueno, limpio y bien
diseñado es importante.
Estos desarrolladores creen una mentira familiar: “Podemos limpiarlo más tarde; ¡Solo tenemos que
llegar al mercado primero!” Por supuesto, las cosas nunca se arreglan más tarde, porque las presiones
del mercado nunca disminuyen. Llegar primero al mercado simplemente significa que ahora tiene una
horda de competidores detrás de usted, y debe adelantarse a ellos corriendo lo más rápido que pueda.
Y así, los desarrolladores nunca cambian de modo. No pueden regresar y limpiar las cosas porque
tienen que hacer la siguiente característica, y la siguiente, y la siguiente, y la siguiente. Y así se
acumula el desorden y la productividad continúa su enfoque asintótico hacia cero.
Así como la liebre confiaba demasiado en su velocidad, los desarrolladores confían demasiado en su
capacidad para seguir siendo productivos. Pero el desorden progresivo del código que socava su
productividad nunca duerme y nunca cede. Si se le da su camino, reducirá la productividad a cero en
cuestión de meses.
La mentira más grande en la que se creen los desarrolladores es la idea de que escribir código
desordenado los hace ir rápido a corto plazo y solo los ralentiza a largo plazo.
Los desarrolladores que aceptan esta mentira exhiben el exceso de confianza de la liebre en su
capacidad para cambiar de modo de hacer líos a limpiar líos en algún momento en el futuro, pero
también cometen un simple error de hecho. El hecho es que ensuciar siempre es más lento que
mantenerse limpio, sin importar la escala de tiempo que esté usando.
Considere los resultados de un notable experimento realizado por Jason Gorman representado
en la figura 1.6. Jason realizó esta prueba durante un período de seis días. Cada día completó un
programa simple para convertir números enteros en números romanos. Sabía que su trabajo estaba
completo cuando pasó su conjunto predefinido de pruebas de aceptación. Cada día la tarea tomó un
poco menos de 30 minutos. Jason usó una disciplina de limpieza bien conocida llamada desarrollo
basado en pruebas (TDD) en el primer, tercer y quinto día.
Los otros tres días, escribió el código sin esa disciplina.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 34 34
Primero, observe la curva de aprendizaje aparente en la figura 1.6. El trabajo en los últimos
días se completa más rápidamente que en los primeros días. Observe también que el trabajo
en los días TDD avanzó aproximadamente un 10 % más rápido que el trabajo en los días no TDD,
y que incluso el día TDD más lento fue más rápido que el día no TDD más rápido.
Algunas personas podrían mirar ese resultado y pensar que es un resultado notable. Pero
para aquellos que no se han dejado engañar por el exceso de confianza de Hare, el resultado es el
esperado, porque conocen esta simple verdad del desarrollo de software:
Los desarrolladores pueden pensar que la respuesta es empezar de cero y rediseñar todo el
sistema, pero eso es solo la liebre hablando de nuevo. El mismo exceso de confianza que
condujo al desorden ahora les dice que pueden construirlo mejor si tan solo pueden comenzar la
carrera de nuevo. La realidad es menos halagüeña:
CONCLUSIÓN
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 35 35
En todos los casos, la mejor opción es que la organización de desarrollo reconozca y evite
su propio exceso de confianza y comience a tomar en serio la calidad de su arquitectura de
software.
Para tomarse en serio la arquitectura de software, debe saber qué es una buena
arquitectura de software. Para construir un sistema con un diseño y una arquitectura que
minimicen el esfuerzo y maximicen la productividad, necesita saber qué atributos de la
arquitectura del sistema conducen a ese fin.
De eso trata este libro. Describe cómo son las arquitecturas y los diseños buenos y
limpios, para que los desarrolladores de software puedan construir sistemas que tengan una
vida útil larga y rentable.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 36 36
2
UNA HISTORIA DE DOS VALORES
Cada sistema de software proporciona dos valores diferentes a las partes interesadas: comportamiento y
estructura. Los desarrolladores de software son responsables de garantizar que ambos valores se
mantengan altos. Desafortunadamente, a menudo se enfocan en uno excluyendo al otro. Aún más
desafortunadamente, a menudo se enfocan en el menor de los dos valores, dejando el sistema de software
eventualmente sin valor.
COMPORTAMIENTO
El primer valor del software es su comportamiento. Los programadores son contratados para hacer que las
máquinas se comporten de una manera que genere o ahorre dinero para las partes interesadas. Hacemos
esto ayudando a las partes interesadas a desarrollar una especificación funcional o un documento de
requisitos. Luego escribimos el código que hace que las máquinas de las partes interesadas satisfagan
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 37 37
esos requisitos.
Cuando la máquina viola esos requisitos, los programadores sacan sus depuradores y solucionan el problema.
Muchos programadores creen que esa es la totalidad de su trabajo. Creen que su trabajo es hacer que la
máquina implemente los requisitos y corrija cualquier error. Están tristemente equivocados.
ARQUITECTURA
El segundo valor del software tiene que ver con la palabra "software", una palabra compuesta por "soft" y
"ware". La palabra “ware” significa “producto”; la palabra “suave”… Bueno, ahí es donde radica el segundo valor.
El software se inventó para ser "suave". Estaba destinado a ser una forma de cambiar fácilmente el
comportamiento de las máquinas. Si hubiéramos querido que el comportamiento de las máquinas fuera difícil de
cambiar, lo habríamos llamado hardware.
Para cumplir su propósito, el software debe ser suave, es decir, debe ser fácil de cambiar.
Cuando las partes interesadas cambian de opinión sobre una característica, ese cambio debe ser simple y
fácil de realizar. La dificultad para hacer tal cambio debería ser proporcional solo al alcance del cambio, y
no a la forma del cambio.
Es esta diferencia entre el alcance y la forma lo que a menudo impulsa el crecimiento de los costos de desarrollo
de software. Es la razón por la que los costos crecen fuera de proporción con el tamaño de los cambios solicitados.
Es la razón por la que el primer año de desarrollo es mucho más barato que el segundo, y el segundo año es
mucho más barato que el tercero.
Desde el punto de vista de las partes interesadas, simplemente están proporcionando una corriente de cambios
de alcance más o menos similar. Desde el punto de vista de los desarrolladores, las partes interesadas les están
dando un flujo de piezas de rompecabezas que deben encajar en un rompecabezas de complejidad cada vez
mayor. Cada nueva solicitud es más difícil de ajustar que la anterior, porque la forma del sistema no coincide con
la forma de la solicitud.
Estoy usando la palabra "forma" aquí de una manera poco convencional, pero creo que la metáfora es
adecuada. Los desarrolladores de software a menudo sienten que se ven obligados a meter clavijas cuadradas
en agujeros redondos.
El problema, por supuesto, es la arquitectura del sistema. Cuanto más esta arquitectura
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 38 38
prefiere una forma sobre otra, las nuevas características más probables serán cada vez más
difíciles de encajar en esa estructura. Por lo tanto, las arquitecturas deben ser tan agnósticas como
prácticas.
EL MAYOR VALOR
¿Función o arquitectura? ¿Cuál de estos dos proporciona el mayor valor? ¿Es más importante que
el sistema de software funcione o es más importante que el sistema de software sea fácil de cambiar?
Si le pregunta a los gerentes comerciales, a menudo dirán que es más importante que el sistema
de software funcione. Los desarrolladores, a su vez, suelen estar de acuerdo con esta actitud. Pero
es la actitud equivocada. Puedo probar que está mal con la simple herramienta lógica de examinar
los extremos.
Si les pregunta a los gerentes comerciales si quieren poder hacer cambios, dirán que por supuesto
que sí, pero luego pueden calificar su respuesta señalando que la funcionalidad actual es más
importante que cualquier flexibilidad posterior. Por el contrario, si los gerentes comerciales le piden un
cambio y los costos estimados para ese cambio son demasiado altos, es probable que los gerentes
comerciales se enfaden porque permitió que el sistema llegara al punto en que el cambio no era
práctico.
MATRIZ DE EISENHOWER
Considere la matriz de importancia versus urgencia del presidente Dwight D. Eisenhower (Figura
2.1). De esta matriz, Eisenhower dijo:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 39 39
Tengo dos clases de problemas, los urgentes y los importantes. Los urgentes no son importantes,
y los importantes nunca son urgentes.1
Hay una gran cantidad de verdad en este viejo adagio. Las cosas que son urgentes rara vez son de gran
importancia, y las cosas que son importantes rara vez son de gran urgencia.
El segundo valor del software, la arquitectura, es importante pero nunca particularmente urgente.
Por supuesto, algunas cosas son tanto urgentes como importantes. Otras cosas no son urgentes ni
importantes. En última instancia, podemos organizar estos cuatro pareados en prioridades:
1. Urgente e importante 2. No
urgente e importante 3. Urgente y
no importante 4. No urgente y no
importante
Tenga en cuenta que la arquitectura del código, lo importante, se encuentra en las dos primeras
posiciones de esta lista, mientras que el comportamiento del código ocupa la primera y la tercera
posición.
El error que a menudo cometen los gerentes de negocios y los desarrolladores es elevar los elementos en
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 40 40
El dilema para los desarrolladores de software es que los gerentes comerciales no están
equipados para evaluar la importancia de la arquitectura. Para eso se contrató a los desarrolladores
de software. Por lo tanto, es responsabilidad del equipo de desarrollo de software afirmar la
importancia de la arquitectura sobre la urgencia de las funciones.
Solo recuerde: si la arquitectura es lo último, entonces el sistema será cada vez más costoso
de desarrollar y, finalmente, el cambio será prácticamente imposible para una parte o la totalidad
del sistema. Si se permite que eso suceda, significa que el equipo de desarrollo de software no
luchó lo suficiente por lo que sabía que era necesario.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 41 41
II
COMENZANDO CON LOS LADRILLOS:
PARADIGMAS DE PROGRAMACIÓN
La arquitectura de software comienza con el código, por lo que comenzaremos nuestra discusión sobre
la arquitectura observando lo que hemos aprendido sobre el código desde que se escribió por primera
vez.
En 1938, Alan Turing sentó las bases de lo que sería la programación informática. No fue el
primero en concebir una máquina programable, pero fue el primero en comprender que los programas
eran simplemente datos. En 1945, Turing estaba escribiendo programas reales en computadoras
reales en un código que reconoceríamos (si entrecerrábamos los ojos lo suficiente). Esos programas
usaban bucles, ramas, asignaciones, subrutinas, pilas y otras estructuras familiares. El lenguaje de
Turing era binario.
Desde aquellos días, se han producido una serie de revoluciones en la programación. Una
revolución con la que todos estamos muy familiarizados es la revolución de los idiomas. Primero, a fines
de la década de 1940, llegaron los ensambladores. Estos "lenguajes" aliviaron a los programadores de la
monotonía de traducir sus programas a binario. En 1951, Grace Hopper inventó A0, el primer compilador.
De hecho, acuñó el término compilador. Fortran se inventó en 1953 (el año después de mi nacimiento). Lo
que siguió fue una avalancha incesante de nuevos lenguajes de programación: COBOL, PL/1, SNOBOL,
C, Pascal, C++, Java, ad infinitum.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 42 42
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 43 43
3
VISIÓN GENERAL DEL PARADIGMA
Los tres paradigmas incluidos en este capítulo de descripción general son la programación estructurada,
la programación orientada a objetos y la programación funcional.
PROGRAMACIÓN ESTRUCTURADA
El primer paradigma que se adoptó (pero no el primero que se inventó) fue la programación
estructurada, que fue descubierta por Edsger Wybe Dijkstra en 1968. Dijkstra demostró que el uso
de saltos sin restricciones (sentencias goto ) es perjudicial para la estructura del programa. Como
veremos en los capítulos siguientes, reemplazó esos saltos con las construcciones if/then/else y do/
while/until más familiares .
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 44 44
PROGRAMACION FUNCIONAL
El tercer paradigma, que sólo recientemente ha comenzado a adoptarse, fue el primero en inventarse. De
hecho, su invención es anterior a la propia programación informática. La programación funcional es el
resultado directo del trabajo de Alonzo Church, quien en 1936 inventó el l-calculus mientras perseguía el
mismo problema matemático que motivaba a Alan Turing al mismo tiempo. Su cálculo l es la base del
lenguaje LISP, inventado en 1958 por John McCarthy. Una noción fundamental del cálculo de l es la
inmutabilidad, es decir, la noción de que los valores de los símbolos no cambian. Esto significa efectivamente
que un lenguaje funcional no tiene declaración de asignación. La mayoría de los lenguajes funcionales, de
hecho, tienen algún medio para alterar el valor de una variable, pero solo bajo una disciplina muy estricta.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 45 45
programador. Ninguno de ellos añade nuevas capacidades. Cada uno impone algún tipo de disciplina
adicional que es negativa en su intención. Los paradigmas nos dicen qué no hacer, más de lo que
nos dicen qué hacer .
Otra forma de ver este tema es reconocer que cada paradigma nos quita algo. Los tres paradigmas
juntos eliminan las instrucciones goto , los punteros de función y la asignación. ¿Queda algo para
llevar?
Probablemente no. Por lo tanto, es probable que estos tres paradigmas sean los únicos tres que
veremos, al menos los únicos tres que son negativos. Otra evidencia de que ya no existen tales
paradigmas es que todos fueron descubiertos dentro de los diez años entre 1958 y 1968. En las
muchas décadas que siguieron, no se agregaron nuevos paradigmas.
CONCLUSIÓN
¿Qué tiene que ver con la arquitectura esta lección de historia sobre paradigmas?
Todo. Utilizamos el polimorfismo como mecanismo para cruzar los límites arquitectónicos;
usamos programación funcional para imponer disciplina sobre la ubicación y el acceso a los datos;
y utilizamos la programación estructurada como base algorítmica de nuestros módulos.
Observe qué tan bien se alinean esos tres con las tres grandes preocupaciones de la
arquitectura: función, separación de componentes y administración de datos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 46 46
4
PROGRAMACIÓN ESTRUCTURADA
En 1955, después de haber sido programador durante tres años y aún estudiante, Dijkstra
concluyó que el desafío intelectual de la programación era mayor que el desafío intelectual de
la física teórica. Como resultado, eligió la programación como su carrera a largo plazo.
En 1957, Dijkstra se casó con Maria Debets. En ese momento, tenías que indicar tu profesión.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 47 47
como parte de los ritos matrimoniales en los Países Bajos. Las autoridades holandesas no estaban
dispuestas a aceptar "programador" como profesión de Dijkstra; nunca habían oído hablar de tal profesión.
Para satisfacerlos, Dijkstra se conformó con "físico teórico" como título de su trabajo.
Como parte de la decisión de hacer de la programación su carrera, Dijkstra consultó con su jefe,
Adriaan van Wijngaarden. A Dijkstra le preocupaba que nadie hubiera identificado una disciplina o
ciencia de la programación y que, por lo tanto, no lo tomaran en serio. Su jefe respondió que Dijkstra
muy bien podría ser una de las personas que descubriría tales disciplinas, con lo que convertiría el
software en una ciencia.
Dijkstra comenzó su carrera en la era de los tubos de vacío, cuando las computadoras eran enormes,
frágiles, lentas, poco confiables y (según los estándares actuales) extremadamente limitadas. En esos
primeros años, los programas se escribían en binario o en un lenguaje ensamblador muy tosco. La
entrada tomó la forma física de cinta de papel o tarjetas perforadas. El bucle de edición/compilación/
prueba duró horas, si no días, de duración.
Fue en este entorno primitivo donde Dijkstra hizo sus grandes descubrimientos.
PRUEBA
El problema que Dijkstra reconoció desde el principio fue que programar es difícil y que los
programadores no lo hacen muy bien. Un programa de cualquier complejidad contiene demasiados
detalles para que un cerebro humano los maneje sin ayuda. Pasar por alto solo un pequeño detalle da
como resultado programas que pueden parecer funcionar, pero fallan de manera sorprendente.
Por supuesto, para poner esto en marcha, Dijkstra se dio cuenta de que tendría que demostrar la
técnica para escribir pruebas básicas de algoritmos simples. Esto lo encontró bastante desafiante.
Durante su investigación, Dijkstra descubrió que ciertos usos de las sentencias goto evitan que los
módulos se descompongan recursivamente en unidades cada vez más pequeñas, lo que impide el uso
del enfoque divide y vencerás necesario para pruebas razonables.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 48 48
Sin embargo, otros usos de goto no tenían este problema. Dijkstra se dio cuenta de que estos "buenos"
usos de goto correspondían a estructuras simples de control de iteración y selección como if/then/else y
do/while. Los módulos que usaban solo esos tipos de estructuras de control podrían subdividirse
recursivamente en unidades demostrables.
Dijkstra sabía que esas estructuras de control, cuando se combinaban con la ejecución
secuencial, eran especiales. Habían sido identificados dos años antes por Böhm y Jacopini, quienes
demostraron que todos los programas se pueden construir a partir de solo tres estructuras: secuencia,
selección e iteración.
Este descubrimiento fue notable: las mismas estructuras de control que hicieron que un módulo
fuera demostrable eran el mismo conjunto mínimo de estructuras de control a partir del cual se pueden
construir todos los programas. Así nació la programación estructurada.
Dijkstra demostró que las declaraciones secuenciales pueden demostrarse correctas a través de una
simple enumeración. La técnica trazó matemáticamente las entradas de la secuencia a las salidas de la
secuencia. Este enfoque no era diferente de cualquier prueba matemática normal.
La iteración fue un poco diferente. Para probar que una iteración era correcta, Dijkstra tuvo que
usar la inducción. Probó el caso de 1 por enumeración. Luego probó el caso de que si se suponía que N
era correcto, N + 1 era correcto, nuevamente por enumeración. También probó los criterios de inicio y fin
de la iteración por enumeración.
Tales pruebas eran laboriosas y complejas, pero eran pruebas. Con su desarrollo, la idea de
que se podía construir una jerarquía euclidiana de teoremas parecía alcanzable.
Y el mundo de la programación se incendió. En ese entonces no teníamos Internet, por lo que la gente
no podía publicar memes desagradables de Dijkstra, y no podían criticarlo en línea.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 49 49
Pero podían, y lo hicieron, escribir cartas a los editores de muchas revistas publicadas.
Esas cartas no eran necesariamente todas educadas. Algunos fueron intensamente negativos;
otros expresaron un fuerte apoyo a su posición. Y así se entabló la batalla, que finalmente duró alrededor
de una década.
Finalmente, la discusión se apagó. La razón era simple: Dijkstra había ganado. A medida que
evolucionaron los lenguajes informáticos, la declaración goto se movió cada vez más hacia atrás, hasta
que casi desapareció. La mayoría de los lenguajes modernos no tienen una instrucción goto y, por
supuesto, LISP nunca la tuvo.
Hoy en día todos somos programadores estructurados, aunque no necesariamente por elección. Es
solo que nuestros idiomas no nos dan la opción de usar la transferencia de control directa e indisciplinada.
Algunos pueden apuntar a saltos con nombre en Java o excepciones como análogos de goto . De
hecho, estas estructuras no son las transferencias de control totalmente ilimitadas que alguna vez
tuvieron los lenguajes más antiguos como Fortran o COBOL. De hecho, incluso los idiomas que aún
admiten la palabra clave goto a menudo restringen el objetivo al alcance de la función actual.
DESCOMPOSICIÓN FUNCIONAL
La programación estructurada permite que los módulos se descompongan recursivamente en unidades
comprobables, lo que a su vez significa que los módulos se pueden descomponer funcionalmente. Es
decir, puede tomar el enunciado de un problema a gran escala y descomponerlo en funciones de alto
nivel. Cada una de esas funciones se puede descomponer en funciones de nivel inferior, hasta el infinito.
Además, cada una de esas funciones descompuestas se puede representar utilizando las estructuras
de control restringidas de la programación estructurada.
Sobre esta base, disciplinas como el análisis estructurado y el diseño estructurado se hicieron
populares a fines de la década de 1970 y durante la década de 1980. Hombres como Ed Yourdon,
Larry Constantine, Tom DeMarco y Meilir Page-Jones promovieron y popularizaron estas técnicas
durante ese período. Al seguir estas disciplinas, los programadores podrían desglosar grandes
sistemas propuestos en módulos y componentes que podrían desglosarse aún más en pequeñas
funciones demostrables.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 50 50
Pero las pruebas nunca llegaron. La jerarquía euclidiana de teoremas nunca se construyó.
Y los programadores en general nunca vieron los beneficios de trabajar en el laborioso proceso de probar
formalmente que todas y cada una de las funciones son correctas. Al final, el sueño de Dijkstra se desvaneció y
murió. Pocos de los programadores de hoy creen que las pruebas formales son una forma adecuada de producir
software de alta calidad.
Por supuesto, las pruebas matemáticas formales, al estilo euclidiano, no son la única estrategia para demostrar que
algo es correcto. Otra estrategia de gran éxito es el método científico.
CIENCIA AL RESCATE
La ciencia es fundamentalmente diferente de las matemáticas, en que las teorías y leyes científicas no pueden
probarse como correctas. No puedo demostrarle que la segunda ley del movimiento de Newton, F = ma, o la ley de
no puedo probarlas en el sentido de una prueba matemática. No importa cuántos experimentos realice o cuánta
evidencia empírica recopile, siempre existe la posibilidad de que algún experimento muestre que esas leyes del
movimiento y la gravedad son incorrectas.
Esa es la naturaleza de las teorías y leyes científicas: son falsables pero no comprobables.
Y, sin embargo, apostamos nuestras vidas a estas leyes todos los días. Cada vez que te subes a un auto, apuestas
tu vida a que F = ma es una descripción confiable de cómo funciona el mundo. Cada vez que das un paso, apuestas
La ciencia no funciona demostrando afirmaciones verdaderas, sino demostrando afirmaciones falsas. Aquellas
afirmaciones que no podemos demostrar que son falsas, después de mucho esfuerzo, las consideramos lo
suficientemente verdaderas para nuestros propósitos.
Por supuesto, no todas las afirmaciones son comprobables. La afirmación “Esto es mentira” no es verdadera ni
falsa. Es uno de los ejemplos más simples de una declaración que no es demostrable.
En última instancia, podemos decir que las matemáticas son la disciplina de probar que las afirmaciones
comprobables son verdaderas. La ciencia, en contraste, es la disciplina de probar que las afirmaciones comprobables
son falsas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 51 51
PRUEBAS
Dijkstra dijo una vez: "Las pruebas muestran la presencia, no la ausencia, de errores". En otras palabras, se puede
demostrar que un programa es incorrecto mediante una prueba, pero no se puede demostrar que sea correcto.
Todo lo que pueden hacer las pruebas, después de un esfuerzo de prueba suficiente, es permitirnos considerar que un programa
Tales pruebas de incorrección sólo pueden aplicarse a programas demostrables . Un programa que no es
comprobable, debido al uso desenfrenado de goto, por ejemplo, no puede considerarse correcto sin importar
cuántas pruebas se le apliquen.
CONCLUSIÓN
Es esta capacidad de crear unidades de programación falsificables lo que hace que la programación
estructurada sea valiosa hoy en día. Esta es la razón por la que los lenguajes modernos no suelen admitir
instrucciones goto sin restricciones . Además, a nivel arquitectónico, es por eso que todavía consideramos que la
descomposición funcional es una de nuestras mejores prácticas.
En todos los niveles, desde la función más pequeña hasta el componente más grande, el software es como una
ciencia y, por lo tanto, está impulsado por la falsabilidad. Los arquitectos de software se esfuerzan por definir módulos,
componentes y servicios que sean fácilmente falsificables (comprobables). Para hacerlo, emplean disciplinas
restrictivas similares a la programación estructurada, aunque a un nivel mucho más alto.
Son esas disciplinas restrictivas las que estudiaremos con cierto detalle en los capítulos siguientes.
venir.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 52 52
5
PROGRAMACIÓN ORIENTADA A OBJETOS
Una respuesta a esta pregunta es "La combinación de datos y funciones". Aunque se cita a
menudo, esta es una respuesta muy insatisfactoria porque implica que of() es de alguna
manera diferente de f(o). Esto es absurdo. Los programadores pasaban estructuras de datos
a funciones mucho antes de 1966, cuando Dahl y Nygaard movieron el marco de la pila de
llamadas de función al montón e inventaron OO.
Otra respuesta común a esta pregunta es "Una forma de modelar el mundo real". Esta es una
respuesta evasiva en el mejor de los casos. ¿Qué significa realmente “modelar el mundo real” y
por qué es algo que nos gustaría hacer? Quizá esta declaración pretenda implicar que OO hace
que el software sea más fácil de entender porque tiene una relación más cercana.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 53 53
relación con el mundo real, pero incluso esa declaración es evasiva y está demasiado vagamente
definida. No nos dice qué es OO.
Algunas personas recurren a tres palabras mágicas para explicar la naturaleza de OO:
encapsulación, herencia y polimorfismo. La implicación es que OO es la mezcla adecuada de
estas tres cosas, o al menos que un lenguaje OO debe soportar estas tres cosas.
¿ENCAPSULACIÓN?
La razón por la que se cita la encapsulación como parte de la definición de OO es que los
lenguajes OO proporcionan una encapsulación fácil y efectiva de datos y funciones. Como resultado,
se puede trazar una línea alrededor de un conjunto cohesivo de datos y funciones. Fuera de esa línea,
los datos están ocultos y solo se conocen algunas de las funciones. Vemos este concepto en acción
como los miembros de datos privados y las funciones de miembros públicos de una clase.
Esta idea ciertamente no es exclusiva de OO. De hecho, tuvimos una encapsulación perfecta en C.
Considere este sencillo programa en C:
punto.h
punto de estructura;
struct Point* makePoint(doble x, doble y); doble distancia (struct
Punto *p1, struct Punto *p2);
punto.c
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 54 54
Los usuarios de point.h no tienen acceso alguno a los miembros de struct Point.
Pueden llamar a la función makePoint() y a la función distance() , pero no tienen absolutamente ningún
conocimiento de la implementación de la estructura de datos Point o de las funciones.
Esta es una encapsulación perfecta, en un lenguaje que no es OO. Los programadores de C solían hacer
este tipo de cosas todo el tiempo. Reenviaríamos declaraciones de estructuras de datos y funciones en
archivos de encabezado y luego las implementaríamos en archivos de implementación. Nuestros usuarios
nunca tuvieron acceso a los elementos en esos archivos de implementación.
El compilador de C++, por razones técnicas, necesitaba que las variables miembro de una clase se
declararan en el archivo de encabezado de esa clase. Así que nuestro programa Point cambió para verse
así:
punto.h
Punto de clase
{ público:
Punto(doble x, doble y); doble
distancia(const Point& p) const;
privada:
doble x;
doble y; };
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 55 55
punto.cc
¡ Los clientes del archivo de encabezado point.h conocen las variables miembro x e y! El compilador
evitará el acceso a ellos, pero el cliente aún sabe que existen. Por ejemplo, si se cambian los
nombres de esos miembros, ¡el archivo point.cc debe volver a compilarse!
Se ha roto el encapsulado.
Por estas razones, es difícil aceptar que OO depende de una encapsulación fuerte.
De hecho, muchos lenguajes orientados a objetos2 tienen poca o ninguna encapsulación forzada.
¿HERENCIA?
Si los lenguajes orientados a objetos no nos dieron una mejor encapsulación, ciertamente nos
dieron herencia.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 56 56
nombradoPunto.h
struct PuntoNombrado;
struct NamedPoint* makeNamedPoint(doble x, doble y, char* nombre); void setName(struct NamedPoint* np,
char* nombre); char* getNombre(struct PuntoNombrado* np);
nombradoPunto.c
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 57 57
C Principal
Este tipo de trucos era una práctica común4 de los programadores antes de la llegada de OO. De
hecho, tal engaño es cómo C++ implementa la herencia única.
Por lo tanto, podríamos decir que teníamos una especie de herencia mucho antes de que se inventaran
los lenguajes OO. Sin embargo, esa afirmación no sería del todo cierta. Teníamos un truco, pero no
es tan conveniente como la verdadera herencia. Además, la herencia múltiple es considerablemente
más difícil de lograr mediante este tipo de trucos.
Tenga en cuenta también que en main.c, me vi obligado a convertir los argumentos de NamedPoint en
Point. En un lenguaje OO real, dicha conversión sería implícita.
Es justo decir que, si bien los lenguajes orientados a objetos no nos dieron algo completamente
nuevo, hicieron que el enmascaramiento de estructuras de datos fuera significativamente más conveniente.
Para recapitular: no podemos otorgar ningún punto a OO por encapsulación, y quizás medio punto
por herencia. Hasta ahora, esa no es una gran puntuación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 58 58
¿POLIMORFISMO?
¿Teníamos un comportamiento polimórfico antes de los lenguajes OO? Por supuesto que lo hicimos.
Considere este sencillo programa de copia de C.
#incluir <stdio.h>
La función getchar() lee desde STDIN. Pero, ¿qué dispositivo es STDIN? La función putchar()
escribe en STDOUT. ¿Pero qué dispositivo es ese? Estas funciones son polimórficas: su comportamiento
depende del tipo de STDIN y STDOUT.
Es como si STDIN y STDOUT fueran interfaces de estilo Java que tienen implementaciones para cada
dispositivo. Por supuesto, no hay interfaces en el programa C de ejemplo, entonces, ¿cómo se entrega la
llamada a getchar() al controlador del dispositivo que lee el carácter?
La respuesta a esa pregunta es bastante sencilla. El sistema operativo UNIX requiere que cada controlador
de dispositivo IO proporcione cinco funciones estándar:5 leer, escribir y buscar. Las firmas de esascerrar,
abrir,
funciones deben ser idénticas para cada controlador IO.
La estructura de datos FILE contiene cinco punteros a funciones. En nuestro ejemplo, podría verse así:
estructura ARCHIVO {
void (*abierto)(char* nombre, modo int); vacío (*cerrar)();
int(*leer)(); vacío (*escribir)(char); void (*buscar)(índice
largo, modo int); };
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 59 59
El controlador IO para la consola definirá esas funciones y cargará una estructura de datos de ARCHIVO
con sus direcciones, algo como esto:
#include "archivo.h"
void open(char* nombre, modo int) {/*...*/} void close() {/*...*/}; int
read() {int c;/*...*/ return c;} void write(char c) {/*...*/} void seek(índice
largo, modo int) {/*...* /}
Ahora, si STDIN se define como un ARCHIVO*, y si apunta a la estructura de datos de la consola, entonces
getchar() podría implementarse de esta manera:
En otras palabras, getchar() simplemente llama a la función a la que apunta el puntero de lectura de la estructura
de datos FILE a la que apunta STDIN.
Este simple truco es la base de todos los polimorfismos en OO. En C++, por ejemplo, cada función virtual
dentro de una clase tiene un puntero en una tabla llamada vtable, y todas las llamadas a funciones virtuales
pasan por esa tabla. Los constructores de derivadas simplemente cargan sus versiones de esas funciones en
la vtable del objeto que se está creando.
Ah, pero eso no es del todo correcto. Los lenguajes OO pueden no habernos dado polimorfismo, pero lo han
hecho mucho más seguro y mucho más conveniente.
El problema con el uso explícito de punteros a funciones para crear funciones polimórficas
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 60 60
comportamiento es que los punteros a funciones son peligrosos. Dicho uso está impulsado por un conjunto de
convenciones manuales. Debe recordar seguir la convención para inicializar esos punteros. Debe recordar seguir
la convención para llamar a todas sus funciones a través de esos punteros. Si algún programador no recuerda
estas convenciones, el error resultante puede ser endiabladamente difícil de rastrear y eliminar.
Los lenguajes OO eliminan estas convenciones y, por lo tanto, estos peligros. Usar un lenguaje OO hace que el
polimorfismo sea trivial. Ese hecho proporciona un poder enorme con el que los viejos programadores C solo
podían soñar. Sobre esta base, podemos concluir que OO impone disciplina en la transferencia indirecta de control.
¡No necesitamos ningún cambio en absoluto! De hecho, ni siquiera necesitamos volver a compilar el programa de
copia . ¿Por qué? Porque el código fuente del programa de copia no depende del código fuente de los controladores
IO. Siempre que esos controladores IO implementen las cinco funciones estándar definidas por FILE, el programa de
copia estará feliz de usarlos.
¿Por qué el sistema operativo UNIX creó complementos para dispositivos IO? Porque aprendimos, a fines de la
década de 1950, que nuestros programas deben ser independientes del dispositivo. ¿Por qué? Porque escribimos
muchos programas que dependían del dispositivo, solo para descubrir que realmente queríamos que esos programas
hicieran el mismo trabajo pero usaran un dispositivo diferente.
Por ejemplo, a menudo escribimos programas que leen datos de entrada de barajas de cartas,6 y luego perforamos
nuevas barajas de cartas como salida. Más tarde, nuestros clientes dejaron de darnos barajas de cartas y empezaron
a darnos carretes de cinta magnética. Esto fue muy inconveniente, porque significaba reescribir grandes porciones del
programa original. Sería muy conveniente que el mismo programa funcionara indistintamente con tarjetas o cinta.
La arquitectura del complemento se inventó para admitir este tipo de independencia del dispositivo IO y se ha
implementado en casi todos los sistemas operativos desde su introducción.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 61 61
Aun así, la mayoría de los programadores no extendieron la idea a sus propios programas, porque usar punteros a
funciones era peligroso.
OO permite que la arquitectura del complemento se use en cualquier lugar, para cualquier cosa.
INVERSIÓN DE DEPENDENCIA
Imagine cómo era el software antes de que estuviera disponible un mecanismo seguro y conveniente para
el polimorfismo. En el árbol de llamadas típico, las funciones principales se denominan funciones de alto nivel, que
se denominan funciones de nivel medio, que se denominan funciones de bajo nivel.
En ese árbol de llamadas, sin embargo, las dependencias del código fuente siguieron inexorablemente el flujo de
control (Figura 5.1).
Para que main llamara a una de las funciones de alto nivel, tenía que mencionar el nombre del módulo que
contenía esa función. En C, este era un #include. En Java, era una declaración de importación . En C#, era una
declaración de uso . De hecho, todas las personas que llamaron se vieron obligadas a mencionar el nombre del
módulo que contenía a la persona que llamó.
Este requisito presentaba al arquitecto de software pocas opciones, si es que tenía alguna. El flujo de control fue
dictado por el comportamiento del sistema, y las dependencias del código fuente fueron dictadas por ese flujo de
control.
Sin embargo, cuando entra en juego el polimorfismo, puede suceder algo muy diferente (Figura 5.2).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 62 62
En la Figura 5.2, el módulo HL1 llama a la función F() en el módulo ML1. El hecho de que llame a esta
función a través de una interfaz es una invención del código fuente. En tiempo de ejecución, la interfaz no
existe. HL1 simplemente llama a F() dentro de ML1. 7
Tenga en cuenta, sin embargo, que la dependencia del código fuente (la relación de herencia)
entre ML1 y la interfaz I apunta en la dirección opuesta en comparación con el flujo de control. Esto se
denomina inversión de dependencia y sus implicaciones para el arquitecto de software son profundas.
El hecho de que los lenguajes OO proporcionen polimorfismo seguro y conveniente significa que
cualquier dependencia del código fuente, sin importar dónde se encuentre, se puede invertir.
Ahora mire hacia atrás en ese árbol de llamadas en la Figura 5.1 y sus muchas dependencias
del código fuente. Cualquiera de esas dependencias del código fuente se puede cambiar insertando
una interfaz entre ellas.
Con este enfoque, los arquitectos de software que trabajan en sistemas escritos en lenguajes OO tienen
control absoluto sobre la dirección de todas las dependencias del código fuente en el sistema. No están
obligados a alinear esas dependencias con el flujo de control. No importa qué módulo realice la llamada y
qué módulo se llame, el arquitecto de software puede señalar la dependencia del código fuente en
cualquier dirección.
¡Eso es poder! Ese es el poder que proporciona OO. De eso se trata realmente la OO, al menos
desde el punto de vista del arquitecto.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 63 63
¿Qué puedes hacer con ese poder? Como ejemplo, puede reorganizar las dependencias del código fuente de su sistema para
que la base de datos y la interfaz de usuario (UI) dependan de las reglas comerciales (Figura 5.3), y no al revés.
Figura 5.3 La base de datos y la interfaz de usuario dependen de las reglas de negocio
Esto significa que la interfaz de usuario y la base de datos pueden ser complementos de las reglas comerciales.
Significa que el código fuente de las reglas comerciales nunca menciona la interfaz de usuario o la base de datos.
Como consecuencia, las reglas comerciales, la interfaz de usuario y la base de datos se pueden compilar en tres componentes
separados o unidades de implementación (por ejemplo, archivos jar, DLL o archivos Gem) que tienen las mismas
dependencias que el código fuente. El componente que contiene las reglas comerciales no dependerá de los componentes
que contienen la interfaz de usuario y la base de datos.
A su vez, las reglas comerciales se pueden implementar independientemente de la interfaz de usuario y la base de datos.
Los cambios en la interfaz de usuario o la base de datos no tienen por qué tener ningún efecto en las reglas comerciales.
Esos componentes se pueden implementar por separado e independientemente.
En resumen, cuando cambia el código fuente de un componente, solo es necesario volver a implementar ese componente.
Esta es la capacidad de implementación independiente.
Si los módulos en su sistema se pueden implementar de forma independiente, entonces pueden ser desarrollados de
forma independiente por diferentes equipos. Eso es desarrollo independiente.
CONCLUSIÓN
¿Qué es OO? Hay muchas opiniones y muchas respuestas a esta pregunta. Sin embargo, para el arquitecto de software,
la respuesta es clara: OO es la capacidad, mediante el uso del polimorfismo, de obtener un control absoluto sobre todas las
dependencias del código fuente en el sistema. Permite al arquitecto crear una arquitectura de complementos, en la que los
módulos que contienen políticas de alto nivel son independientes de los módulos que contienen detalles de bajo nivel.
Los detalles de bajo nivel se relegan a módulos de complementos que se pueden implementar y desarrollar de forma
independiente de los módulos que contienen políticas de alto nivel.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 64 64
3. No solo los programadores de C: la mayoría de los lenguajes de esa era tenían la capacidad de enmascarar un dato
estructura como otra.
6. Tarjetas perforadas: tarjetas IBM Hollerith, 80 columnas de ancho. Estoy seguro de que muchos de ustedes nunca han visto uno
de estos, pero eran comunes en los años 50, 60 e incluso 70.
7. Aunque indirectamente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 65 65
6
PROGRAMACION FUNCIONAL
En muchos sentidos, los conceptos de programación funcional son anteriores a la programación misma.
Este paradigma se basa fuertemente en el cálculo l inventado por Alonzo Church en la década de 1930.
CUADRADOS DE ENTEROS
Para explicar qué es la programación funcional, lo mejor es examinar algunos ejemplos.
Investiguemos un problema simple: imprimir los cuadrados de los primeros 25 enteros.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 66 66
Sistema.salida.println(i*i);
}
}
Si no conoce Lisp, entonces esto puede parecer un poco extraño. Permítanme reformatearlo un
poco y agregar algunos comentarios.
Debe quedar claro que println, take, map y range son todas funciones. En Lisp, llamas
a una función poniéndola entre paréntesis. Por ejemplo, (rango) llama a la función de
rango.
La expresión (fn [x] (* xx)) es una función anónima que llama a la función multiplicar, pasando su
argumento de entrada dos veces. En otras palabras, calcula el cuadrado de su entrada.
Mirando todo de nuevo, es mejor comenzar con la llamada de función más interna.
• La función de rango devuelve una lista interminable de enteros que comienzan con 0. •
Esta lista se pasa a la función de mapa , que llama a la función de elevación al cuadrado
anónima en cada elemento, produciendo una nueva lista interminable de todos los cuadrados.
• La lista de cuadrados se pasa a la función tomar , que devuelve una nueva lista con solo los
primeros 25 elementos. • La función println imprime su entrada, que es una lista de los primeros
25 cuadrados de números enteros.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 67 67
Si te aterra el concepto de listas interminables, no te preocupes. Solo se crean los primeros 25 elementos
de esas listas interminables. Esto se debe a que no se evalúa ningún elemento de una lista interminable
hasta que se accede a él.
Si encontró todo eso confuso, entonces puede esperar un tiempo glorioso aprendiendo todo sobre
Clojure y la programación funcional. No es mi objetivo enseñarte sobre estos temas aquí.
En cambio, mi objetivo aquí es señalar algo muy dramático sobre la diferencia entre los programas
Clojure y Java. El programa Java utiliza una variable mutable, una variable que cambia de estado durante
la ejecución del programa. Esa variable es i, la variable de control del bucle. No existe tal variable mutable
en el programa Clojure. En el programa Clojure se inicializan variables como x , pero nunca se modifican.
Esto nos lleva a una afirmación sorprendente: las variables en los lenguajes funcionales no varían.
INMUTABILIDAD Y ARQUITECTURA
¿Por qué es importante este punto como consideración arquitectónica? ¿Por qué un arquitecto
se preocuparía por la mutabilidad de las variables? La respuesta es absurdamente simple: todas las
condiciones de carrera, las condiciones de interbloqueo y los problemas de actualización simultánea se
deben a variables mutables. No puede tener una condición de carrera o un problema de actualización
concurrente si nunca se actualiza ninguna variable. No se pueden tener interbloqueos sin bloqueos mutables.
En otras palabras, todos los problemas a los que nos enfrentamos en las aplicaciones concurrentes,
todos los problemas a los que nos enfrentamos en las aplicaciones que requieren varios subprocesos
y varios procesadores, no pueden ocurrir si no hay variables mutables.
Como arquitecto, debes estar muy interesado en las cuestiones de concurrencia. Desea asegurarse de
que los sistemas que diseñe sean sólidos en presencia de múltiples subprocesos y procesadores. La
pregunta que debes hacerte, entonces, es si la inmutabilidad es practicable.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 68 68
SEGREGACIÓN DE MUTABILIDAD
Uno de los compromisos más comunes con respecto a la inmutabilidad es segregar la aplicación, o los
servicios dentro de la aplicación, en componentes mutables e inmutables. Los componentes inmutables
realizan sus tareas de forma puramente funcional, sin utilizar ninguna variable mutable. Los
componentes inmutables se comunican con uno o más componentes que no son puramente funcionales
y permiten mutar el estado de las variables (Figura 6.1).
Dado que el estado de mutación expone esos componentes a todos los problemas de concurrencia, es
una práctica común usar algún tipo de memoria transaccional para proteger las variables mutables de
las actualizaciones simultáneas y las condiciones de carrera.
La memoria transaccional simplemente trata las variables en la memoria de la misma manera que
una base de datos trata los registros en el disco.1 Protege esas variables con una transacción o reintento.
esquema.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 69 69
En este código, la variable contador se define como un átomo. En Clojure, un átomo es un tipo
especial de variable cuyo valor puede mutar bajo condiciones muy disciplinadas que se imponen
mediante el intercambio. función.
¡ El intercambio! La función, que se muestra en el código anterior, toma dos argumentos: el átomo que se
va a mutar y una función que calcula el nuevo valor que se almacenará en el átomo. En nuestro código de
ejemplo, el átomo del contador se cambiará al valor calculado por la función inc , que simplemente
incrementa su argumento.
La estrategia utilizada por swap! es un algoritmo tradicional de comparación e intercambio . El valor del
contador se lee y se pasa a inc. Cuando inc regresa, el valor del contador se bloquea y se compara con el
valor que se pasó a inc. Si el valor es el mismo, el valor devuelto por inc se almacena en el contador y se
libera el bloqueo.
De lo contrario, se libera el bloqueo y se vuelve a intentar la estrategia desde el principio.
Sería prudente que los arquitectos introduzcan la mayor cantidad de procesamiento posible en los
componentes inmutables y extraigan la mayor cantidad de código posible de aquellos componentes que
deben permitir la mutación.
SUMINISTRO DE EVENTOS
Como ejemplo simple, imagine una aplicación bancaria que mantiene los saldos de las cuentas de
sus clientes. Muta esos saldos al depositar y retirar
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 70 70
Ahora imagine que en lugar de almacenar los saldos de las cuentas, almacenamos solo las transacciones.
Siempre que alguien quiera saber el saldo de una cuenta, simplemente sumamos todas las transacciones de esa cuenta,
desde el principio de los tiempos. Este esquema no requiere variables mutables.
Obviamente, este enfoque suena absurdo. Con el tiempo, el número de transacciones crecería sin límites y la
potencia de procesamiento necesaria para calcular los totales se volvería intolerable. Para que este esquema funcione
para siempre, necesitaríamos un almacenamiento infinito y una potencia de procesamiento infinita.
Pero tal vez no tengamos que hacer que el esquema funcione para siempre. Y tal vez tengamos suficiente
almacenamiento y suficiente poder de procesamiento para hacer que el esquema funcione durante la vida útil razonable
de la aplicación.
2
Esta es la idea detrás del abastecimiento de eventos. El abastecimiento de eventos es una estrategia en la que
almacenar las transacciones, pero no el estado. Cuando se requiere el estado, simplemente aplicamos todas las
transacciones desde el principio de los tiempos.
Por supuesto, podemos tomar atajos. Por ejemplo, podemos calcular y guardar el estado cada medianoche. Luego,
cuando se requiere la información del estado, solo necesitamos calcular las transacciones desde la medianoche.
Ahora considere el almacenamiento de datos requerido para este esquema: necesitaríamos mucho.
Siendo realistas, el almacenamiento de datos fuera de línea ha estado creciendo tan rápido que ahora consideramos
que billones de bytes son pequeños, por lo que tenemos mucho.
Más importante aún, nunca se elimina o actualiza nada de dicho almacén de datos. Como consecuencia, nuestras
aplicaciones no son CRUD; son solo CR. Además, dado que no se producen actualizaciones ni eliminaciones en el
almacén de datos, no puede haber problemas de actualización simultánea.
Si tenemos suficiente almacenamiento y suficiente potencia de procesador, podemos hacer que nuestras
aplicaciones sean completamente inmutables y, por lo tanto, completamente funcionales.
Si esto todavía suena absurdo, podría ayudar si recordara que esta es precisamente la forma en que funciona su
sistema de control de código fuente.
CONCLUSIÓN
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 71 71
Para resumir:
Cada uno de estos tres paradigmas nos ha quitado algo. Cada uno restringe algún aspecto de la
forma en que escribimos código. Ninguno de ellos ha aumentado nuestro poder o nuestras capacidades.
Lo que hemos aprendido durante el último medio siglo es lo que no se debe hacer.
Al darnos cuenta de eso, tenemos que enfrentar un hecho desagradable: el software no es una
tecnología que avanza rápidamente. Las reglas del software son las mismas hoy que en 1946, cuando
Alan Turing escribió el primer código que se ejecutaría en una computadora electrónica. Las herramientas
han cambiado y el hardware ha cambiado, pero la esencia del software sigue siendo la misma.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 72 72
tercero
PRINCIPIOS DE DISEÑO
Los buenos sistemas de software comienzan con un código limpio. Por un lado, si los ladrillos
no están bien hechos, la arquitectura del edificio no importa mucho. Por otro lado, puedes
hacer un lío sustancial con ladrillos bien hechos. Aquí es donde entran los principios SOLID.
Los principios de SOLID nos dicen cómo organizar nuestras funciones y estructuras de datos
en clases, y cómo se deben interconectar esas clases. El uso de la palabra "clase" no implica
que estos principios sean aplicables únicamente al software orientado a objetos.
Una clase es simplemente una agrupación acoplada de funciones y datos. Cada sistema de
software tiene tales agrupaciones, ya sea que se llamen clases o no. Los principios SOLID se
aplican a esos grupos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 73 73
• Toleran el cambio,
• Son fáciles de entender y • Son
la base de los componentes que se pueden usar en muchos sistemas de software.
El término “nivel medio” se refiere al hecho de que estos principios son aplicados por
programadores que trabajan a nivel de módulo. Se aplican justo por encima del nivel del código y
ayudan a definir los tipos de estructuras de software que se utilizan en los módulos y componentes.
Así como es posible crear un desorden sustancial con ladrillos bien hechos, también es posible
crear un desorden en todo el sistema con componentes de nivel medio bien diseñados. Por esta razón,
una vez que hayamos cubierto los principios SOLID, pasaremos a sus contrapartes en el mundo de
los componentes y luego a los principios de la arquitectura de alto nivel.
Más o menos en 2004, Michael Feathers me envió un correo electrónico diciéndome que si
reorganizaba los principios, sus primeras palabras deletrearían la palabra SÓLIDO, y así nacieron los
principios SÓLIDOS.
Los capítulos que siguen describen cada principio más detalladamente. Este es el resumen
ejecutivo:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 74 74
dice que para construir sistemas de software a partir de partes intercambiables, esas partes
deben adherirse a un contrato que permite que esas partes se sustituyan una por otra. • ISP:
el principio de segregación de la interfaz Este principio aconseja a los diseñadores de software
que eviten depender de cosas que no usan.
Estos principios se han descrito en detalle en muchas publicaciones diferentes1 a lo largo de los
años. Los capítulos que siguen se centrarán en las implicaciones arquitectónicas de estos
principios en lugar de repetir esas discusiones detalladas. Si aún no está familiarizado con estos
principios, lo que sigue es insuficiente para comprenderlos en detalle y le recomendamos que los
estudie en los documentos a pie de página.
1. Por ejemplo, desarrollo de software ágil, principios, patrones y prácticas, Robert C. Martin, Prentice Hall,
2002, http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod, y https://en.wikipedia.org/wiki/
SOLID_(object-oriented_design) (o simplemente google SÓLIDO).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 75 75
7
SRP: LA ÚNICA RESPONSABILIDAD
PRINCIPIO
De todos los principios SOLID, el Principio de responsabilidad única (SRP) podría ser el menos
entendido. Eso es probable porque tiene un nombre particularmente inapropiado.
Es demasiado fácil para los programadores escuchar el nombre y luego asumir que significa
que cada módulo debe hacer una sola cosa.
No se equivoquen, hay un principio como ese. Una función debe hacer una y sólo una cosa.
Usamos ese principio cuando estamos refactorizando funciones grandes en funciones más
pequeñas; lo usamos en los niveles más bajos. Pero no es uno de los principios SOLID, no
es el SRP.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 76 76
Los sistemas de software se modifican para satisfacer a los usuarios y partes interesadas; esos
usuarios y partes interesadas son la “razón para cambiar” de la que habla el principio. De hecho,
podemos reformular el principio para decir esto:
Un módulo debe ser responsable ante uno, y solo uno, usuario o parte interesada.
Desafortunadamente, las palabras "usuario" y "parte interesada" no son realmente las palabras
correctas para usar aquí. Es probable que haya más de un usuario o parte interesada que desee
que el sistema cambie de la misma manera. En cambio, en realidad nos estamos refiriendo a un
grupo: una o más personas que requieren ese cambio. Nos referiremos a ese grupo como un actor.
Ahora bien, ¿a qué nos referimos con la palabra “módulo”? La definición más simple es solo un
archivo fuente. La mayoría de las veces esa definición funciona bien. Sin embargo, algunos
lenguajes y entornos de desarrollo no usan archivos fuente para contener su código. En esos casos,
un módulo es solo un conjunto cohesivo de funciones y estructuras de datos.
Esa palabra “cohesivo” implica el SRP. La cohesión es la fuerza que une el código responsable de un
solo actor.
Quizás la mejor manera de entender este principio es observar los síntomas de su violación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 77 77
Esta clase viola el SRP porque esos tres métodos son responsables ante tres actores muy
diferentes.
Al colocar el código fuente de estos tres métodos en una sola clase de empleado , los
desarrolladores han acoplado cada uno de estos actores con los demás. Este acoplamiento
puede hacer que las acciones del equipo del CFO afecten algo de lo que depende el equipo del COO.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 78 78
Supongamos ahora que el equipo del CFO decide que es necesario ajustar la forma en que se
calculan las horas que no son horas extra. Por el contrario, el equipo de RR. HH. del director de
operaciones no quiere ese ajuste en particular porque utilizan horas que no son horas extra para un propósito diferente.
Un desarrollador tiene la tarea de realizar el cambio y ve la conveniente función regularHours() llamada por
el método computePay( ) . Desafortunadamente, ese desarrollador no se da cuenta de que la función
reportHours() también llama a la función.
El desarrollador realiza el cambio requerido y lo prueba cuidadosamente. El equipo del CFO valida que
la nueva función funcione como se desea y se implementa el sistema.
Por supuesto, el equipo del COO no sabe que esto está sucediendo. El personal de recursos humanos
sigue utilizando los informes generados por la función reportHours() , pero ahora contienen números
incorrectos. Finalmente, se descubre el problema y el director de operaciones está furioso porque los datos
incorrectos le han costado millones de dólares a su presupuesto.
Todos hemos visto cosas como esta suceder. Estos problemas ocurren porque ponemos código del que
dependen diferentes actores muy cerca. El SRP dice que separe el código del que dependen los diferentes
actores.
SÍNTOMA 2: FUSIONES
No es difícil imaginar que las fusiones serán comunes en los archivos de origen que contienen muchos
métodos diferentes. Esta situación es especialmente probable si esos métodos son responsables de
diferentes actores.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 79 79
Por ejemplo, suponga que el equipo de DBA del CTO decide que debe haber un cambio de esquema simple
en la tabla Empleado de la base de datos. Suponga también que el equipo de empleados de recursos
humanos del director de operaciones decide que necesita un cambio en el formato del informe de horas.
Dos desarrolladores diferentes, posiblemente de dos equipos diferentes, verifican la clase Empleado y comienzan
a realizar cambios. Desafortunadamente, sus cambios chocan. El resultado es una fusión.
Probablemente no necesito decirte que las fusiones son asuntos riesgosos. Nuestras herramientas son
bastante buenas hoy en día, pero ninguna herramienta puede manejar todos los casos de fusión. Al final,
siempre hay riesgo.
En nuestro ejemplo, la fusión pone en riesgo tanto al CTO como al COO. No es inconcebible que
el director financiero también pueda verse afectado.
Hay muchos otros síntomas que podríamos investigar, pero todos involucran a varias personas que
cambian el mismo archivo de origen por diferentes motivos.
Una vez más, la forma de evitar este problema es separar el código que admite diferentes
actores
SOLUCIONES
Hay muchas soluciones diferentes para este problema. Cada uno mueve las funciones a diferentes clases.
Quizás la forma más obvia de resolver el problema es separar los datos de las funciones. Las tres clases
comparten acceso a EmployeeData, que es una estructura de datos simple sin métodos (Figura 7.3). Cada
clase contiene solo el código fuente necesario para su función particular. No se permite que las tres clases
se conozcan entre sí. Así se evita cualquier duplicación accidental.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 80 80
La desventaja de esta solución es que los desarrolladores ahora tienen tres clases que deben instanciar y
rastrear. Una solución común a este dilema es usar el patrón Fachada (Figura 7.4).
El EmployeeFacade contiene muy poco código. Es responsable de instanciar y delegar a las clases con las
funciones.
Algunos desarrolladores prefieren mantener las reglas comerciales más importantes más cerca de los datos.
Esto se puede hacer manteniendo el método más importante en la clase Empleado original y luego
usando esa clase como una Fachada para las funciones menores (Figura 7.5).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 81 81
Figura 7.5 El método más importante se mantiene en la clase Empleado original y se usa como Fachada
para las funciones menores
Puede objetar estas soluciones sobre la base de que cada clase contendría solo una
función. Este no es el caso. Es probable que la cantidad de funciones requeridas para
calcular el pago, generar un informe o guardar los datos sea grande en cada caso. Cada
una de esas clases tendría muchos métodos privados en ellas.
Cada una de las clases que contienen tal familia de métodos es un alcance. Fuera de
ese ámbito, nadie sabe que existen los miembros privados de la familia.
CONCLUSIÓN
El principio de responsabilidad única se trata de funciones y clases, pero reaparece en
una forma diferente en dos niveles más. A nivel de componentes, se convierte en el
Principio de Cierre Común. A nivel arquitectónico, se convierte en el Eje de Cambio
responsable de la creación de Límites Arquitectónicos. Estudiaremos todas estas ideas en
los próximos capítulos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 82 82
8
OCP: EL PRINCIPIO ABIERTO-CERRADO
El Principio Abierto-Cerrado (OCP) fue acuñado en 1988 por Bertrand Meyer.1 Dice:
Un artefacto de software debe estar abierto para extensión pero cerrado para modificación.
En otras palabras, el comportamiento de un artefacto de software debería ser extensible, sin tener
que modificar ese artefacto.
Esta, por supuesto, es la razón fundamental por la que estudiamos arquitectura de software.
Claramente, si las simples extensiones de los requisitos fuerzan cambios masivos en el
software, entonces los arquitectos de ese sistema de software han cometido un fracaso espectacular.
La mayoría de los estudiantes de diseño de software reconocen el OCP como un principio que los guía
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 83 83
en el diseño de clases y módulos. Pero el principio adquiere una importancia aún mayor
cuando consideramos el nivel de los componentes arquitectónicos.
UN EXPERIMENTO DE PENSAMIENTO
Imagine, por un momento, que tenemos un sistema que muestra un resumen financiero en una
página web. Los datos de la página se pueden desplazar y los números negativos se muestran en
rojo.
Ahora imagine que las partes interesadas piden que esta misma información se convierta en un
informe para imprimir en una impresora en blanco y negro. El informe debe estar correctamente
paginado, con encabezados de página, pies de página y etiquetas de columna apropiados. Los
números negativos deben estar entre paréntesis.
Claramente, se debe escribir algún código nuevo. Pero, ¿cuánto código antiguo tendrá que
cambiar?
¿Cómo? Separando adecuadamente las cosas que cambian por diferentes razones (el Principio de
Responsabilidad Única), y luego organizando las dependencias entre esas cosas adecuadamente
(el Principio de Inversión de Dependencia).
Al aplicar el SRP, podríamos llegar a la vista de flujo de datos que se muestra en la figura 8.1.
Algún procedimiento de análisis inspecciona los datos financieros y produce datos notificables,
que luego los dos procesos informadores les dan el formato adecuado.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 84 84
La idea esencial aquí es que generar el informe implica dos responsabilidades separadas:
el cálculo de los datos informados y la presentación de esos datos en un formato compatible con la
web y la impresora.
Una vez realizada esta separación, debemos organizar las dependencias del código fuente para
garantizar que los cambios en una de esas responsabilidades no provoquen cambios en la otra.
Además, la nueva organización debe asegurarse de que el comportamiento se pueda extender sin
deshacer la modificación.
Logramos esto dividiendo los procesos en clases y separando esas clases en componentes, como
lo muestran las líneas dobles en el diagrama de la Figura 8.2.
En esta figura, el componente en la parte superior izquierda es el controlador. En la parte superior
derecha, tenemos el Interactor. En la parte inferior derecha, está la base de datos. Finalmente, en
la parte inferior izquierda, hay cuatro componentes que representan los Presentadores y las Vistas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 85 85
Figura 8.2 Partición de los procesos en clases y separación de las clases en componentes
Las clases marcadas con <I> son interfaces; los marcados con <DS> son estructuras de datos.
Las puntas de flecha abiertas utilizan relaciones. Las puntas de flecha cerradas son
implementos o relaciones de herencia .
Lo primero que debe notar es que todas las dependencias son dependencias del código fuente .
Una flecha que apunta de la clase A a la clase B significa que el código fuente de la clase A
menciona el nombre de la clase B, pero la clase B no menciona nada sobre la clase A. Por lo
tanto, en la Figura 8.2, FinancialDataMapper conoce FinancialDataGateway a través de una
relación de implementos , pero FinancialDataGateway no sabe nada sobre FinancialDataMapper.
Lo siguiente a notar es que cada línea doble se cruza en una sola dirección. Esto significa que todas
las relaciones entre componentes son unidireccionales, como se muestra en la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 86 86
gráfico de componentes de la figura 8.3. Estas flechas apuntan hacia los componentes que queremos
proteger del cambio.
El interactor se encuentra en la posición que mejor se ajusta a la OCP. Los cambios en la base
de datos, el controlador, los presentadores o las vistas no afectarán al interactor.
¿Por qué el interactiano debería ocupar una posición tan privilegiada? Porque contiene las reglas de
negocio. El Interactor contiene las políticas de más alto nivel de la aplicación.
Todos los demás componentes están lidiando con preocupaciones periféricas. El interactor se ocupa
de la preocupación central.
Aunque el controlador es periférico al interactor, es fundamental para los presentadores y las vistas.
Y si bien los presentadores pueden ser periféricos al controlador, son fundamentales para las vistas.
Observe cómo esto crea una jerarquía de protección basada en la noción de "nivel".
Los interactianos son el concepto de más alto nivel, por lo que son los más protegidos. Las vistas son
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 87 87
entre los conceptos de más bajo nivel, por lo que son los menos protegidos. Los presentadores
tienen un nivel más alto que las Vistas, pero un nivel más bajo que el Controlador o el Interactor.
CONTROL DIRECCIONAL
Si retrocedió horrorizado ante el diseño de la clase que se mostró anteriormente, vuelva a mirar. Gran
parte de la complejidad de ese diagrama estaba destinada a garantizar que las dependencias entre los
componentes apuntaran en la dirección correcta.
OCULTACIÓN DE INFORMACIÓN
La interfaz FinancialReportRequester tiene un propósito diferente. Está ahí para proteger a
FinancialReportController de saber demasiado sobre las partes internas de Interactor. Si esa interfaz no
estuviera allí, entonces el controlador tendría dependencias transitivas en las entidades financieras.
Las dependencias transitivas son una violación del principio general de que las entidades de software
no deben depender de cosas que no usan directamente. Volveremos a encontrar ese principio cuando
hablemos del Principio de segregación de la interfaz y el Principio de reutilización común.
Por lo tanto, aunque nuestra primera prioridad es proteger al interactor de los cambios en el
controlador, también queremos proteger al controlador de los cambios en el interactor al ocultar las
partes internas del interactor.
CONCLUSIÓN
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 88 88
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 89 89
9
LSP: LA SUSTITUCIÓN DE LISKOV
PRINCIPIO
En 1988, Barbara Liskov escribió lo siguiente como una forma de definir los subtipos.
Lo que se busca aquí es algo así como la siguiente propiedad de sustitución: si para cada objeto o1
de tipo S hay un objeto o2 de tipo T tal que para todos los programas P definidos en términos de T,
1
el comportamiento de P no cambia cuando se sustituye o1 para o2 entonces S es un subtipo de T.
Para entender esta idea, que se conoce como Principio de Sustitución de Liskov (LSP),
veamos algunos ejemplos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 90 90
Imagine que tenemos una clase llamada Licencia, como se muestra en la Figura 9.1.
Esta clase tiene un método denominado calcFee(), al que llama la aplicación Facturación .
Hay dos "subtipos" de Licencia: PersonalLicense y BusinessLicense. Utilizan diferentes
algoritmos para calcular la tarifa de la licencia.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 91 91
Rectángulo r = …
r.setW(5);
r.setH(2);
afirmar(r.area() == 10);
La única forma de defenderse contra este tipo de violación de LSP es agregar mecanismos al
Usuario (como una declaración if ) que detecte si el Rectángulo es, de hecho, un Cuadrado.
Dado que el comportamiento del Usuario depende de los tipos que utiliza, dichos tipos no son
sustituibles.
LSP Y ARQUITECTURA
En los primeros años de la revolución orientada a objetos, pensamos en el LSP como una forma
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 92 92
para guiar el uso de la herencia, como se muestra en las secciones anteriores. Sin embargo, a lo largo de los
años, el LSP se transformó en un principio más amplio de diseño de software relacionado con las interfaces y las
implementaciones.
Las interfaces en cuestión pueden ser de muchas formas. Podríamos tener una interfaz de estilo Java,
implementada por varias clases. O podríamos tener varias clases de Ruby que comparten las mismas firmas
de métodos. O podríamos tener un conjunto de servicios que respondan todos a la misma interfaz REST.
En todas estas situaciones, y más, el LSP es aplicable porque hay usuarios que dependen de interfaces
bien definidas y de la sustituibilidad de las implementaciones de esas interfaces.
La mejor forma de entender el LSP desde el punto de vista de la arquitectura es ver qué sucede con la
arquitectura de un sistema cuando se viola el principio.
Supongamos que estamos construyendo un agregador para muchos servicios de despacho de taxis.
Los clientes usan nuestro sitio web para encontrar el taxi más apropiado para usar, independientemente de la
compañía de taxis. Una vez que el cliente toma una decisión, nuestro sistema despacha el taxi elegido mediante
un servicio tranquilo.
Ahora suponga que el URI para el servicio de despacho tranquilo es parte de la información contenida en la
base de datos del controlador. Una vez que nuestro sistema ha elegido un conductor apropiado para el cliente,
obtiene ese URI del registro del conductor y luego lo usa para despachar al conductor.
purplecab.com/driver/Bob
purplecab.com/driver/Bob
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 93 93
Claramente, esto significa que todos los servicios de despacho, para todas las diferentes empresas,
deben ajustarse a la misma interfaz REST. Deben tratar los campos de dirección de recogida, hora de
recogida y destino de forma idéntica.
Ahora suponga que la compañía de taxis Acme contrató a algunos programadores que no leyeron las
especificaciones con mucho cuidado. Abreviaron el campo de destino a simplemente dest. Acme es la
compañía de taxis más grande de nuestra área, y la ex esposa del director ejecutivo de Acme es la nueva
esposa de nuestro director ejecutivo, y... Bueno, te haces una idea. ¿Qué pasaría con la arquitectura de
nuestro sistema?
Obviamente, necesitaríamos agregar un caso especial. La solicitud de envío para cualquier controlador
Acme tendría que construirse utilizando un conjunto de reglas diferente al de todos los demás controladores.
La forma más sencilla de lograr este objetivo sería agregar una declaración if al módulo que construyó
el comando dispatch:
si (controlador.getDispatchUri().startsWith("acme.com"))…
Pero, por supuesto, ningún arquitecto que se precie permitiría que tal construcción existiera en el sistema.
Poner la palabra "acme" en el código crea una oportunidad para todo tipo de errores horribles y misteriosos,
sin mencionar las brechas de seguridad.
Por ejemplo, ¿qué pasaría si Acme tuviera aún más éxito y comprara la empresa Purple Taxi? ¿Qué
pasaría si la compañía fusionada mantuviera las marcas separadas y los sitios web separados, pero
unificara todos los sistemas de las compañías originales? ¿Tendríamos que agregar otra declaración if para
"púrpura"?
Nuestro arquitecto tendría que aislar el sistema de errores como este mediante la creación de algún tipo de
módulo de creación de comandos de envío controlado por una base de datos de configuración codificada
por el URI de envío. Los datos de configuración podrían verse así:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 94 94
Y así nuestro arquitecto ha tenido que añadir un mecanismo significativo y complejo para tratar
con el hecho de que las interfaces de los servicios de descanso no son todas sustituibles.
CONCLUSIÓN
1. Barbara Liskov, "Data Abstraction and Hierarchy", SIGPLAN Notices 23, 5 (mayo de 1988).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 95 95
10
ISP: LA SEGREGACIÓN DE INTERFAZ
PRINCIPIO
El Principio de Segregación de Interfaz (ISP) deriva su nombre del diagrama que se muestra
en la Figura 10.1.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 96 96
En la situación ilustrada en la Figura 10.1, hay varios usuarios que utilizan las
operaciones de la clase OPS . Supongamos que User1 usa solo op1, User2 usa solo op2 y
User3 usa solo op3.
Ahora imagina que OPS es una clase escrita en un lenguaje como Java. Claramente, en ese
caso, el código fuente de User1 dependerá inadvertidamente de op2 y op3, aunque no los
llame. Esta dependencia significa que un cambio en el código fuente de op2 en OPS forzará a
User1 a ser recompilado y reimplementado, aunque nada de lo que le importaba realmente
haya cambiado.
Este problema se puede resolver segregando las operaciones en interfaces como se muestra
en la Figura 10.2.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 97 97
ISP E IDIOMA
Claramente, la descripción dada anteriormente depende críticamente del tipo de idioma.
Los lenguajes de tipo estático como Java obligan a los programadores a crear declaraciones que
los usuarios deben importar, usar o incluir de otra manera . Son estas declaraciones incluidas en el
código fuente las que crean las dependencias del código fuente que fuerzan la recompilación y la
redistribución.
En lenguajes de escritura dinámica como Ruby y Python, tales declaraciones no existen en el código
fuente. En su lugar, se infieren en tiempo de ejecución. Por lo tanto, no hay dependencias del código
fuente que fuercen la recompilación y la redistribución. Esta es la razón principal por la que los
lenguajes tipificados dinámicamente crean sistemas que son más flexibles y menos acoplados que los
lenguajes tipificados estáticamente.
Este hecho podría llevarlo a concluir que el ISP es un problema de idioma, más que un problema de
arquitectura.
ISP Y ARQUITECTURA
Si da un paso atrás y observa las motivaciones fundamentales del ISP, puede ver una preocupación
más profunda al acecho allí. En general, es perjudicial depender de módulos que contienen más
de lo que necesitas. Obviamente, esto es cierto para las dependencias del código fuente que pueden
forzar la recompilación y la redistribución innecesarias, pero también es cierto en un grado mucho mayor.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 98 98
Supongamos ahora que D contiene características que F no usa y, por lo tanto, que a S no le
importan. Los cambios en esas funciones dentro de D pueden forzar la redistribución de F y, por
lo tanto, la redistribución de S. Peor aún, una falla de una de las funciones dentro de D puede
causar fallas en F y S.
CONCLUSIÓN
La lección aquí es que depender de algo que lleva un equipaje que no necesitas puede
causarte problemas que no esperabas.
Exploraremos esta idea con más detalle cuando discutamos el Principio de reutilización común
en el Capítulo 13, "Cohesión de componentes".
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 99 99
11
DIP: LA INVERSIÓN DE LA DEPENDENCIA
PRINCIPIO
El Principio de Inversión de Dependencia (DIP) nos dice que los sistemas más flexibles son aquellos en los
que las dependencias del código fuente se refieren solo a abstracciones, no a concreciones.
En un lenguaje de tipo estático, como Java, esto significa que las declaraciones de uso, importación
e inclusión deben referirse solo a los módulos fuente que contienen interfaces, clases abstractas o
algún otro tipo de declaración abstracta. No se debe depender de nada concreto.
La misma regla se aplica a los lenguajes de escritura dinámica, como Ruby y Python.
Las dependencias del código fuente no deben hacer referencia a módulos concretos. Sin embargo, en estos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 100 100
idiomas es un poco más difícil definir qué es un módulo concreto. En particular, es cualquier módulo en el
que se implementan las funciones que se están llamando.
Claramente, tratar esta idea como una regla no es realista, porque los sistemas de software deben
depender de muchas instalaciones concretas. Por ejemplo, la clase String en Java es concreta y
sería poco realista tratar de forzarla a ser abstracta. La dependencia del código fuente en el
java.lang.string concreto no puede y no debe evitarse.
En comparación, la clase String es muy estable. Los cambios a esa clase son muy raros y están
estrictamente controlados. Los programadores y arquitectos no tienen que preocuparse por cambios
frecuentes y caprichosos en String.
Por estas razones, tendemos a ignorar los antecedentes estables del sistema operativo y las
instalaciones de la plataforma cuando se trata de DIP. Toleramos esas dependencias concretas porque
sabemos que podemos confiar en que no cambiarán.
Son los elementos concretos volátiles de nuestro sistema de los que queremos evitar depender. Esos
son los módulos que estamos desarrollando activamente y que están experimentando cambios
frecuentes.
ABSTRACCIONES ESTABLES
Cada cambio en una interfaz abstracta corresponde a un cambio en sus implementaciones
concretas. Por el contrario, los cambios en implementaciones concretas no siempre, ni siquiera por lo
general, requieren cambios en las interfaces que implementan. Por lo tanto, las interfaces son menos
volátiles que las implementaciones.
De hecho, los buenos diseñadores y arquitectos de software trabajan duro para reducir la volatilidad de
las interfaces. Intentan encontrar formas de agregar funcionalidad a las implementaciones sin realizar
cambios en las interfaces. Esto es Diseño de Software 101.
La implicación, entonces, es que las arquitecturas de software estables son aquellas que evitan
depender de concreciones volátiles y que favorecen el uso de interfaces abstractas estables.
Esta implicación se reduce a un conjunto de prácticas de codificación muy específicas:
• No se refiera a clases concretas volátiles. Consulte las interfaces abstractas en su lugar. Esta regla
se aplica en todos los idiomas, ya sea de tipo estático o dinámico. También impone severas
restricciones a la creación de objetos y, en general, impone el uso de Abstract Factories.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 101 101
FÁBRICAS
Para cumplir con estas reglas, la creación de objetos concretos volátiles requiere un manejo
especial. Esta precaución está garantizada porque, en prácticamente todos los lenguajes, la
creación de un objeto requiere una dependencia del código fuente en la definición concreta de ese
objeto.
En la mayoría de los lenguajes orientados a objetos, como Java, usaríamos una fábrica abstracta
para administrar esta dependencia no deseada.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 102 102
Figura 11.1 Uso del patrón Abstract Factory para gestionar la dependencia
La línea curva divide el sistema en dos componentes: uno abstracto y otro concreto. El
componente abstracto contiene todas las reglas comerciales de alto nivel de la aplicación. El
componente concreto contiene todos los detalles de implementación que manipulan esas
reglas comerciales.
Tenga en cuenta que el flujo de control cruza la línea curva en dirección opuesta a las
dependencias del código fuente. Las dependencias del código fuente se invierten contra el
flujo de control, razón por la cual nos referimos a este principio como inversión de dependencia.
COMPONENTES DE HORMIGÓN
El componente concreto de la Figura 11.1 contiene una sola dependencia, por lo que viola el
DIP. Esto es típico. Las violaciones de DIP no se pueden eliminar por completo, pero se
pueden reunir en una pequeña cantidad de componentes concretos y mantenerlos separados
del resto del sistema.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 103 103
CONCLUSIÓN
A medida que avanzamos en este libro y cubrimos principios arquitectónicos de alto nivel, el
DIP aparecerá una y otra vez. Será el principio organizador más visible en nuestros
diagramas de arquitectura. La línea curva de la Figura 11.1 se convertirá en los límites
arquitectónicos en capítulos posteriores. La forma en que las dependencias cruzan esa línea
curva en una dirección, y hacia entidades más abstractas, se convertirá en una nueva regla
que llamaremos Regla de Dependencia.
1. En otras palabras, la función que invoca el sistema operativo cuando la aplicación se inicia por primera vez.
puesto en marcha
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 104 104
IV
PRINCIPIOS DE LOS COMPONENTES
Si los principios SÓLIDOS nos dicen cómo colocar los ladrillos en paredes y habitaciones, entonces los
principios de los componentes nos dicen cómo colocar las habitaciones en edificios. Los grandes sistemas
de software, como los grandes edificios, se construyen con componentes más pequeños.
En la Parte IV, discutiremos qué son los componentes de software, qué elementos deben componerlos
y cómo deben componerse juntos en sistemas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 105 105
12
COMPONENTES
Los componentes son las unidades de implementación. Son las entidades más pequeñas que se
pueden implementar como parte de un sistema. En Java, son archivos jar. En Ruby, son archivos de gemas.
En .Net, son DLL. En lenguajes compilados, son agregaciones de archivos binarios.
En lenguajes interpretados, son agregaciones de archivos fuente. En todos los idiomas, son el gránulo
del despliegue.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 106 106
Considere el siguiente programa PDP-8 simple. Consiste en una subrutina llamada GETSTR que
ingresa una cadena desde el teclado y la guarda en un búfer. También tiene un pequeño programa
de prueba unitaria para ejercitar GETSTR.
*200
TLS
COMIENZO, ALC
TAD BUFR
JMS GETSTR
ALC
TAD BUFR
PUTSTR, etc.
JMP INICIO 3000
BUFR,
GETSTR, 0
PTR DCA
NXTCH, KSF
JMP-1
CHIMENEA
DCA I PTR
TAD I PTR
Y K177
PTR ISZ
TAD MCR
SZA
JMP NXTCH
K177, 177
MCR, -15
Tenga en cuenta el comando *200 al comienzo de este programa. Le dice al compilador que genere
código que se cargará en la dirección 2008 .
Este tipo de programación es un concepto extraño para la mayoría de los programadores de hoy. Ellos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 107 107
Rara vez hay que pensar en dónde está cargado un programa en la memoria de la computadora.
Pero en los primeros días, esta fue una de las primeras decisiones que un programador necesitaba
tomar. En aquellos días, los programas no eran reubicables.
¿Cómo accedías a una función de biblioteca en esos viejos tiempos? El código anterior ilustra el
enfoque utilizado. Los programadores incluyeron el código fuente de las funciones de la biblioteca
con su código de aplicación y las compilaron todas como un solo programa.1 Las bibliotecas se
mantuvieron en fuente, no en binario.
El problema con este enfoque fue que, durante esta era, los dispositivos eran lentos y la memoria
era costosa y, por lo tanto, limitada. Los compiladores necesitaban hacer varias pasadas sobre el
código fuente, pero la memoria era demasiado limitada para mantener residente todo el código fuente.
En consecuencia, el compilador tuvo que leer el código fuente varias veces usando los dispositivos
lentos.
Esto llevó mucho tiempo, y cuanto más grande era su biblioteca de funciones, más tiempo tardaba el
compilador. Compilar un programa grande podría llevar horas.
Para acortar los tiempos de compilación, los programadores separaron el código fuente de la biblioteca
de funciones de las aplicaciones. Compilaron la biblioteca de funciones por separado y cargaron el
binario en una dirección conocida, por ejemplo, 20008 . Crearon una tabla de símbolos para la biblioteca
de funciones y la compilaron con su código de aplicación. Cuando querían ejecutar una aplicación,
cargaban la biblioteca de funciones binarias2 y luego cargaban la aplicación. La memoria se parecía al
diseño que se muestra en la Figura 12.1.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 108 108
Esto funcionó bien siempre que la aplicación pudiera caber entre las direcciones 00008 y 17778 .
Pero pronto las aplicaciones se hicieron más grandes que el espacio asignado para ellas. En ese
momento, los programadores tenían que dividir sus aplicaciones en dos segmentos de direcciones,
saltando alrededor de la biblioteca de funciones (Figura 12.2).
Obviamente, esta no era una situación sostenible. A medida que los programadores
agregaron más funciones a la biblioteca de funciones, excedió sus límites y tuvieron que
asignarle más espacio (en este ejemplo, cerca de 70008 ). Esta fragmentación de programas y
bibliotecas continuó necesariamente a medida que crecía la memoria de la computadora.
REUBICABILIDAD
La solución fueron los binarios reubicables. La idea detrás de ellos era muy simple. El compilador
se cambió para generar un código binario que un cargador inteligente podría reubicar en la memoria.
Se le diría al cargador dónde cargar el código reubicable. El código reubicable estaba equipado con
banderas que le decían al cargador qué partes de los datos cargados debían modificarse para
cargarse en la dirección seleccionada. Por lo general, esto solo significaba agregar la dirección de
inicio a cualquier dirección de referencia de memoria en el binario.
Ahora el programador podría decirle al cargador dónde cargar la biblioteca de funciones y dónde
cargar la aplicación. De hecho, el cargador aceptaría varias entradas binarias
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 109 109
y simplemente cárguelos en la memoria uno tras otro, reubicándolos a medida que los carga. Esto
permitió a los programadores cargar solo aquellas funciones que necesitaban.
El compilador también se cambió para emitir los nombres de las funciones como metadatos en el binario
reubicable. Si un programa llamara a una función de biblioteca, el compilador emitiría ese nombre como una
referencia externa. Si un programa definiera una función de biblioteca, el compilador emitiría ese nombre
como una definición externa. Luego, el cargador podría vincular las referencias externas a las definiciones
externas una vez que hubiera determinado dónde había cargado esas definiciones.
ENLACE
El cargador de enlace permitió a los programadores dividir sus programas en segmentos compilables y
cargables por separado. Esto funcionó bien cuando se vinculaban programas relativamente pequeños con
bibliotecas relativamente pequeñas. Sin embargo, a fines de la década de 1960 y principios de la de 1970,
los programadores se volvieron más ambiciosos y sus programas se hicieron mucho más grandes.
Eventualmente, los cargadores de enlace eran demasiado lentos para tolerarlos. Las bibliotecas de
funciones se almacenaban en dispositivos lentos, como una cinta magnética. Incluso los discos, en aquel
entonces, eran bastante lentos. Usando estos dispositivos relativamente lentos, los cargadores de enlace
tenían que leer docenas, si no cientos, de bibliotecas binarias para resolver las referencias externas. A
medida que los programas crecían más y más, y se acumulaban más funciones de biblioteca en las
bibliotecas, un cargador de enlace podía tardar más de una hora en cargar el programa.
Finalmente, la carga y el enlace se separaron en dos fases. Los programadores tomaron la parte lenta, la
parte que hizo ese enlace, y la pusieron en una aplicación separada llamada enlazador. La salida del
enlazador era un reubicable enlazado que un cargador reubicable podía cargar muy rápidamente. Esto
permitió a los programadores preparar un ejecutable usando el enlazador lento, pero luego podían cargarlo
rápidamente, en cualquier momento.
Luego vino la década de 1980. Los programadores trabajaban en C o en algún otro lenguaje de alto
nivel. A medida que crecían sus ambiciones, también lo hacían sus programas. Los programas que
contaban con cientos de miles de líneas de código no eran inusuales.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 110 110
módulo individual fue relativamente rápido, pero compilar todos los módulos tomó un poco de tiempo. El enlazador
tardaría aún más tiempo. El tiempo de respuesta había vuelto a crecer a una hora o más en muchos casos.
Parecía como si los programadores estuvieran condenados a perseguirse la cola sin cesar. A lo largo de las décadas
de 1960, 1970 y 1980, todos los cambios realizados para acelerar el flujo de trabajo se vieron frustrados por las
ambiciones de los programadores y el tamaño de los programas que escribieron. Parecía que no podían escapar de
los tiempos de respuesta de una hora. El tiempo de carga se mantuvo rápido, pero los tiempos de compilación del
enlace fueron el cuello de botella.
Por supuesto, estábamos experimentando la ley del tamaño del programa de Murphy:
Los programas crecerán para llenar todo el tiempo disponible de compilación y enlace.
Pero Murphy no fue el único contendiente en la ciudad. Llegó Moore3 y, a fines de la década de 1980, los dos se
enfrentaron. Moore ganó esa batalla. Los discos comenzaron a encogerse y se volvieron significativamente más
rápidos. La memoria de la computadora comenzó a ser tan ridículamente barata que gran parte de los datos en el disco
podían almacenarse en caché en la RAM. Las velocidades de reloj de la computadora aumentaron de 1 MHz a 100
MHz.
A mediados de la década de 1990, el tiempo dedicado a la vinculación había comenzado a reducirse más
rápido de lo que nuestras ambiciones podían hacer crecer los programas. En muchos casos, el tiempo de enlace se
redujo a una cuestión de segundos. Para trabajos pequeños, la idea de un cargador articulado volvió a ser factible.
Esta fue la era de Active-X, las bibliotecas compartidas y los comienzos de los archivos .jar .
Las computadoras y los dispositivos se volvieron tan rápidos que pudimos, una vez más, hacer la vinculación en el
momento de la carga. Podríamos vincular varios archivos .jar , o varias bibliotecas compartidas en cuestión de segundos,
y ejecutar el programa resultante. Y así nació la arquitectura de complementos de componentes.
En la actualidad, enviamos habitualmente archivos .jar , DLL o bibliotecas compartidas como complementos para las
aplicaciones existentes. Si desea crear un mod para Minecraft, por ejemplo, simplemente incluya sus archivos .jar
personalizados en una carpeta determinada. Si desea conectar Resharper a Visual Studio, simplemente incluya las DLL
correspondientes.
CONCLUSIÓN
Estos archivos vinculados dinámicamente, que se pueden conectar durante el tiempo de ejecución, son los
componentes de software de nuestras arquitecturas. Ha tomado 50 años, pero hemos llegado a un lugar donde la
arquitectura de complementos de componentes puede ser el valor predeterminado casual en lugar de
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 111 111
1. Mi primer empleador tenía varias docenas de cubiertas del código fuente de la biblioteca de subrutinas en un estante.
Cuando escribiste un nuevo programa, simplemente agarraste uno de esos mazos y lo golpeaste en el extremo de tu mazo.
2. En realidad, la mayoría de esas máquinas antiguas usaban memoria central, que no se borraba cuando apagaba la computadora. A
menudo dejamos la biblioteca de funciones cargada durante días.
3. Ley de Moore: la velocidad, la memoria y la densidad de la computadora se duplican cada 18 meses. Esta ley se mantuvo desde la década
de 1950 hasta el 2000, pero luego, al menos para las frecuencias de reloj, se detuvo en seco.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 112 112
13
COHESIÓN DE COMPONENTES
¿Qué clases pertenecen a qué componentes? Esta es una decisión importante y requiere
la orientación de buenos principios de ingeniería de software. Desafortunadamente, a lo largo
de los años, esta decisión se ha tomado de manera ad hoc basada casi en su totalidad en
contexto.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 113 113
LA EQUIVALENCIA REUTILIZACIÓN/LIBERACIÓN
PRINCIPIO
El gránulo de reutilización es el gránulo de liberación.
Esto no se debe simplemente a que, sin los números de versión, no habría forma de garantizar que
todos los componentes reutilizados sean compatibles entre sí. Más bien, también refleja el hecho de que los
desarrolladores de software necesitan saber cuándo llegarán nuevos lanzamientos y qué cambios traerán
esos nuevos lanzamientos.
No es raro que los desarrolladores reciban alertas sobre una nueva versión y decidan, en función de los
cambios realizados en esa versión, continuar usando la versión anterior.
Por lo tanto, el proceso de lanzamiento debe producir las notificaciones y la documentación de lanzamiento
adecuadas para que los usuarios puedan tomar decisiones informadas sobre cuándo y si integrar el nuevo
lanzamiento.
Desde el punto de vista del diseño y la arquitectura del software, este principio significa que las clases y los
módulos que forman un componente deben pertenecer a un grupo cohesivo. El componente no puede
consistir simplemente en una mezcolanza aleatoria de clases y módulos; en cambio, debe haber algún tema o
propósito general que todos esos módulos compartan.
Por supuesto, esto debería ser obvio. Sin embargo, hay otra manera de ver este tema que quizás no sea tan
obvia. Las clases y los módulos que se agrupan en un componente deben poder liberarse juntos. El hecho de
que compartan el mismo número de versión y el mismo seguimiento de lanzamiento, y estén incluidos en la
misma documentación de lanzamiento, debería tener sentido tanto para el autor como para los usuarios.
Este es un consejo débil: Decir que algo debería “tener sentido” es solo una forma de
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 114 114
agitando las manos en el aire y tratando de sonar autoritario. El consejo es débil porque es
difícil explicar con precisión el vínculo que une las clases y los módulos en un solo componente.
Por débil que sea el consejo, el principio en sí es importante, porque las violaciones son
fáciles de detectar: no “tienen sentido”. Si viola el REP, sus usuarios lo sabrán y no quedarán
impresionados con sus habilidades arquitectónicas.
La debilidad de este principio es más que compensada por la fuerza de los siguientes dos
principios. De hecho, el PCCh y el CRP definen fuertemente este principio, pero en un sentido
negativo.
Este es el Principio de Responsabilidad Única reafirmado para los componentes. Así como el
SRP dice que una clase no debe contener múltiples razones para cambiar, el Principio de
Cierre Común (CCP) dice que un componente no debe tener múltiples razones para cambiar.
El PCCh nos insta a reunir en un solo lugar a todas las clases que probablemente cambien por
las mismas razones. Si dos clases están tan estrechamente unidas, ya sea física o
conceptualmente, que siempre cambian juntas, entonces pertenecen al mismo componente.
Esto minimiza la carga de trabajo relacionada con la liberación, revalidación y reimplementación
del software.
Este principio está estrechamente asociado con el Principio Abierto Cerrado (OCP). De hecho,
es “cierre” en el sentido OCP de la palabra que aborda el PCCh. El OCP establece que las
clases deben estar cerradas para modificación pero abiertas para extensión. Debido a que el
cierre del 100% no es alcanzable, el cierre debe ser estratégico. Diseñamos nuestras clases de
manera que estén cerradas a los tipos de cambios más comunes que esperamos o tenemos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 115 115
experimentado.
El CCP amplía esta lección reuniendo en un mismo componente aquellas clases que están cerradas a
los mismos tipos de cambios. Por lo tanto, cuando surge un cambio en los requisitos, ese cambio tiene
una buena probabilidad de estar restringido a un número mínimo de componentes.
Reúna aquellas cosas que cambian en los mismos tiempos y por las mismas razones.
Separe aquellas cosas que cambian en diferentes momentos o por diferentes razones.
El Principio de reutilización común (CRP) es otro principio más que nos ayuda a decidir qué clases y
módulos deben colocarse en un componente. Establece que las clases y módulos que tienden a
reutilizarse juntos pertenecen al mismo componente.
Las clases rara vez se reutilizan de forma aislada. Más típicamente, las clases reutilizables colaboran
con otras clases que forman parte de la abstracción reutilizable. El CRP establece que estas clases
pertenecen juntas en el mismo componente. En tal componente, esperaríamos ver clases que tengan
muchas dependencias entre sí.
Un ejemplo simple podría ser una clase de contenedor y sus iteradores asociados. Estas clases
se reutilizan juntas porque están estrechamente acopladas entre sí. Por lo tanto, deberían estar en el
mismo componente.
Pero el CRP nos dice más que solo qué clases juntar en un componente: también nos dice qué clases
no mantener juntas en un componente. Cuando un componente usa otro, se crea una dependencia
entre los componentes. Quizás el componente de uso usa solo una clase dentro del componente
usado , pero eso aún no debilita la dependencia. El componente de uso todavía depende del
componente usado .
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 116 116
Debido a esa dependencia, cada vez que se cambia el componente utilizado , es probable que el
componente en uso necesite los cambios correspondientes . Incluso si no es necesario realizar cambios en el
componente de uso , es probable que deba volver a compilarse, revalidarse y volver a implementarse. Esto es
cierto incluso si el componente que lo usa no se preocupa por el cambio realizado en el componente usado .
Por lo tanto, cuando dependemos de un componente, queremos asegurarnos de que dependemos de todas
las clases de ese componente. Dicho de otra manera, queremos asegurarnos de que las clases que ponemos
en un componente sean inseparables, que sea imposible depender de algunas y no de otras. De lo contrario,
volveremos a implementar más componentes de los necesarios y desperdiciaremos un esfuerzo significativo.
Por lo tanto, el CRP nos dice más sobre qué clases no deberían estar juntas que sobre qué clases
deberían estar juntas. El CRP dice que las clases que no están estrechamente vinculadas entre sí no deben
estar en el mismo componente.
El CRP es la versión genérica del ISP. El ISP nos aconseja no depender de clases que tengan métodos
que no usamos. El CRP nos aconseja no depender de componentes que tengan clases que no usamos.
La figura 13.1 es un diagrama de tensión2 que muestra cómo interactúan entre sí los tres principios de
cohesión. Los bordes del diagrama describen el costo de abandonar el principio en el vértice opuesto.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 117 117
Un arquitecto que se enfoca solo en REP y CRP encontrará que demasiados componentes
se ven afectados cuando se realizan cambios simples. Por el contrario, un arquitecto que se centre
demasiado en el CCP y el REP hará que se generen demasiados lanzamientos innecesarios.
Un buen arquitecto encuentra una posición en ese triángulo de tensión que cumple con las
preocupaciones actuales del equipo de desarrollo, pero también es consciente de que esas
preocupaciones cambiarán con el tiempo. Por ejemplo, al principio del desarrollo de un proyecto, el CCP
es mucho más importante que el REP, porque la capacidad de desarrollo es más importante que la reutilización.
Generalmente, los proyectos tienden a comenzar en el lado derecho del triángulo, donde el único
sacrificio es la reutilización. A medida que el proyecto madura, y otros proyectos comienzan a sacar
provecho de él, el proyecto se deslizará hacia la izquierda. Esto significa que la estructura de
componentes de un proyecto puede variar con el tiempo y la madurez. Tiene más que ver con la forma
en que se desarrolla y utiliza el proyecto que con lo que el proyecto realmente hace.
CONCLUSIÓN
En el pasado, nuestra visión de la cohesión era mucho más simple de lo que implicaban REP, CCP
y CRP. Una vez pensamos que la cohesión era simplemente el atributo de que un módulo realiza
una y solo una función. Sin embargo, los tres principios de cohesión de componentes describen una
variedad de cohesión mucho más compleja. Al elegir las clases para agruparlas en componentes,
debemos considerar las fuerzas opuestas involucradas en la reutilización y la capacidad de desarrollo.
Equilibrar estas fuerzas con las necesidades de la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 118 118
1. Consulte la sección sobre "El problema del gatito" en el Capítulo 27, "Servicios: grandes y
pequeños". 2. Gracias a Tim Ottinger por esta idea.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 119 119
14
ACOPLAMIENTO DE COMPONENTES
Los siguientes tres principios se ocupan de las relaciones entre los componentes. Aquí
nuevamente nos encontraremos con la tensión entre la capacidad de desarrollo y el diseño
lógico. Las fuerzas que inciden sobre la arquitectura de una estructura componente son
técnicas, políticas y volátiles.
¿Alguna vez trabajó todo el día, hizo que algunas cosas funcionaran y luego se fue a casa, solo
para llegar a la mañana siguiente y descubrir que sus cosas ya no funcionan? ¿Por qué no?
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 120 120
¿trabajar? ¡Porque alguien se quedó más tarde que tú y cambió algo de lo que dependes! A esto lo llamo “el
síndrome del día después”.
El “síndrome del día siguiente” ocurre en entornos de desarrollo donde muchos desarrolladores están
modificando los mismos archivos fuente. En proyectos relativamente pequeños con solo unos pocos
desarrolladores, no es un problema demasiado grande. Pero a medida que crece el tamaño del proyecto y el
equipo de desarrollo, las mañanas posteriores pueden convertirse en una pesadilla. No es raro que pasen
semanas sin que el equipo pueda construir una versión estable del proyecto. En cambio, todos siguen
cambiando y cambiando su código tratando de que funcione con los últimos cambios que hizo otra persona.
Durante las últimas décadas, han evolucionado dos soluciones a este problema, ambas provenientes de la
industria de las telecomunicaciones. El primero es "la compilación semanal" y el segundo es el Principio de
Dependencias Acíclicas (ADP).
LA CONSTRUCCIÓN SEMANAL
La compilación semanal solía ser común en proyectos de tamaño mediano. Funciona así: todos los
desarrolladores se ignoran durante los primeros cuatro días de la semana. Todos trabajan en copias
privadas del código y no se preocupan por integrar su trabajo de forma colectiva. Luego, el viernes, integran
todos sus cambios y construyen el sistema.
Este enfoque tiene la maravillosa ventaja de permitir a los desarrolladores vivir en un mundo aislado durante
cuatro días de cada cinco. La desventaja, por supuesto, es la gran sanción de integración que se paga el
viernes.
Desafortunadamente, a medida que crece el proyecto, se vuelve menos factible terminar de integrar el
proyecto el viernes. La carga de integración crece hasta que comienza a desbordarse hasta el sábado. Unos
pocos sábados de este tipo son suficientes para convencer a los desarrolladores de que la integración
realmente debería comenzar el jueves, por lo que el inicio de la integración avanza lentamente hacia la mitad
de la semana.
A medida que disminuye el ciclo de trabajo de desarrollo versus integración, también disminuye la eficiencia
del equipo. Eventualmente, esta situación se vuelve tan frustrante que los desarrolladores o los gerentes de
proyecto declaran que el cronograma debe cambiarse a una compilación quincenal. Esto es suficiente por un
tiempo, pero el tiempo de integración continúa creciendo con el tamaño del proyecto.
Eventualmente, este escenario conduce a una crisis. Para mantener la eficiencia, el programa de construcción
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 121 121
tiene que alargarse continuamente, pero alargar el cronograma de construcción aumenta los riesgos
del proyecto. La integración y las pruebas se vuelven cada vez más difíciles de realizar y el equipo pierde
el beneficio de una retroalimentación rápida.
A medida que se ponen a disposición nuevas versiones de un componente, otros equipos pueden decidir
si adoptarán inmediatamente la nueva versión. Si deciden no hacerlo, simplemente continúan usando la
versión anterior. Una vez que deciden que están listos, comienzan a usar la nueva versión.
Así ningún equipo está a merced de los demás. No es necesario que los cambios realizados en un
componente tengan un efecto inmediato en otros equipos. Cada equipo puede decidir por sí mismo
cuándo adaptar sus propios componentes a las nuevas versiones de los componentes. Además, la
integración ocurre en pequeños incrementos. No existe un punto único en el tiempo en el que todos los
desarrolladores deban unirse e integrar todo lo que están haciendo.
Este es un proceso muy simple y racional, y es ampliamente utilizado. Sin embargo, para que funcione
correctamente, debe administrar la estructura de dependencia de los componentes. No puede haber
ciclos. Si hay ciclos en la estructura de dependencia, entonces no se puede evitar el “síndrome del día
después”.
Considere el diagrama de componentes de la figura 14.1. Muestra una estructura bastante típica de
componentes ensamblados en una aplicación. La función de esta aplicación no es importante para el
propósito de este ejemplo. Lo importante es la estructura de dependencia de los componentes. Observe que
esta estructura es un gráfico dirigido. Los componentes son los nodos y las relaciones de dependencia son
los bordes dirigidos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 122 122
Observe una cosa más: independientemente del componente en el que comience, es imposible seguir las
relaciones de dependencia y terminar de nuevo en ese componente. Esta estructura no tiene ciclos. Es
un gráfico acíclico dirigido (DAG).
Ahora considere lo que sucede cuando el equipo responsable de los presentadores hace una nueva
versión de su componente. Es fácil averiguar quién se ve afectado por este lanzamiento; simplemente
sigue las flechas de dependencia hacia atrás. Por lo tanto, View y Main se verán afectados. Los
desarrolladores que trabajan actualmente en esos componentes tendrán que decidir cuándo deben
integrar su trabajo con la nueva versión de Presenters.
Tenga en cuenta también que cuando se suelta Main , no tiene ningún efecto en ninguno de los otros
componentes del sistema. No conocen Main y no les importa cuándo cambia. Esto es bonito. Significa
que el impacto de liberar Main es relativamente pequeño.
Cuando los desarrolladores que trabajan en el componente Presentadores deseen ejecutar una prueba
de ese componente, solo necesitan crear su versión de Presentadores con las versiones de los
componentes Interactores y Entidades que están utilizando actualmente.
Ninguno de los otros componentes del sistema necesita estar involucrado. Esto es bonito. Significa que
los desarrolladores que trabajan en Presentadores tienen relativamente poco trabajo que hacer para
configurar una prueba y que tienen relativamente pocas variables que considerar.
Cuando llega el momento de liberar todo el sistema, el proceso procede de abajo hacia
arriba. Primero, el componente Entidades se compila, prueba y libera. Luego se hace lo
mismo para Base de datos e Interactores. Estos componentes van seguidos de Presentadores,
Vista, Controladores y, a continuación , Autorizador. Principal va último. Este proceso
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 123 123
es muy claro y fácil de manejar. Sabemos cómo construir el sistema porque entendemos
las dependencias entre sus partes.
Supongamos que un nuevo requisito nos obliga a cambiar una de las clases en Entidades
para que haga uso de una clase en Autorizador. Por ejemplo, digamos que la clase Usuario
en Entidades usa la clase Permisos en Autorizador. Esto crea un ciclo de dependencia, como
se muestra en la Figura 14.2.
Este ciclo crea algunos problemas inmediatos. Por ejemplo, los desarrolladores que trabajan
en el componente Base de datos saben que para liberarlo, el componente debe ser compatible
con Entidades. Sin embargo, con el ciclo implementado, el componente Base de datos ahora
también debe ser compatible con Autorizador. Pero el Autorizador depende de los Interactores.
Esto hace que la base de datos sea mucho más difícil de liberar. De hecho, las entidades, el
autorizador y los interactianos se han convertido en un gran componente, lo que significa que
todos los desarrolladores que trabajen en cualquiera de esos componentes experimentarán el
temido "síndrome del día después". Se pisarán unos a otros porque todos deben usar
exactamente la misma versión de los componentes de cada uno.
Pero esto es sólo parte del problema. Considere lo que sucede cuando queremos probar el
componente Entidades . Para nuestro disgusto, encontramos que debemos construir e
integrarnos con Authorizer e Interactors. Este nivel de acoplamiento entre los componentes es
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 124 124
preocupante, si no intolerable.
Es posible que se haya preguntado por qué tiene que incluir tantas bibliotecas diferentes y tantas cosas
de todos los demás, solo para ejecutar una prueba unitaria simple de una de sus clases. Si investigas
un poco el asunto, probablemente descubras que hay ciclos en el gráfico de dependencia. Dichos ciclos
hacen que sea muy difícil aislar los componentes. Las pruebas unitarias y la liberación se vuelven muy
difíciles y propensas a errores. Además, los problemas de construcción crecen geométricamente con el
número de módulos.
Además, cuando hay ciclos en el gráfico de dependencia, puede ser muy difícil determinar el orden en
el que debe construir los componentes. De hecho, probablemente no haya un orden correcto. Esto
puede generar algunos problemas muy desagradables en lenguajes como Java que leen sus
declaraciones de archivos binarios compilados.
ROMPIENDO EL CICLO
Siempre es posible romper un ciclo de componentes y restablecer el gráfico de dependencia como
un DAG. Hay dos mecanismos principales para hacerlo:
2. Cree un nuevo componente del que dependan tanto las Entidades como el Autorizador . Muevete
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 125 125
las clases de las que ambos dependen en ese nuevo componente (Figura 14.4).
Figura 14.4 El nuevo componente del que dependen tanto las Entidades como el Autorizador
LOS "NERVIOS"
La segunda solución implica que la estructura del componente es volátil en presencia de requisitos
cambiantes. De hecho, a medida que crece la aplicación, la estructura de dependencia de los
componentes se altera y crece. Por lo tanto, la estructura de dependencia siempre debe ser
monitoreada por ciclos. Cuando ocurren ciclos, deben romperse de alguna manera.
A veces esto significará crear nuevos componentes, haciendo crecer la estructura de
dependencia.
DISEÑO ARRIBA-ABAJO
Los problemas que hemos discutido hasta ahora conducen a una conclusión ineludible:
la estructura del componente no se puede diseñar de arriba hacia abajo. No es una de las
primeras cosas del sistema que se diseña, sino que evoluciona a medida que el sistema crece y
cambia.
Algunos lectores pueden encontrar este punto contradictorio. Hemos llegado a esperar que las
descomposiciones de grano grande, como los componentes, también sean descomposiciones
funcionales de alto nivel.
Cuando vemos una agrupación granular, como una estructura de dependencia de componentes,
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 126 126
creemos que los componentes deben representar de alguna manera las funciones del sistema.
Sin embargo, esto no parece ser un atributo de los diagramas de dependencia de componentes.
De hecho, los diagramas de dependencia de componentes tienen muy poco que ver con la
descripción de la función de la aplicación. En cambio, son un mapa de la capacidad de construcción
y mantenimiento de la aplicación. Por eso no se diseñan al principio del proyecto. No hay software
para construir o mantener, por lo que no hay necesidad de un mapa de construcción y mantenimiento.
Pero a medida que se acumulan más y más módulos en las primeras etapas de implementación y
diseño, existe una necesidad creciente de administrar las dependencias para que el proyecto pueda
desarrollarse sin el "síndrome del día siguiente". Además, queremos mantener los cambios lo más
localizados posible, por lo que comenzamos a prestar atención al SRP y al CCP y colocamos las
clases que probablemente cambien juntas.
A medida que la aplicación sigue creciendo, empezamos a preocuparnos por crear elementos
reutilizables. En este punto, la CRP comienza a influir en la composición de los componentes.
Finalmente, a medida que aparecen los ciclos, se aplica el ADP y el gráfico de dependencia de los
componentes tiembla y crece.
Los diseños no pueden ser completamente estáticos. Cierta volatilidad es necesaria si el diseño va a
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 127 127
ser mantenido. Al cumplir con el Principio de Cierre Común (CCP), creamos componentes que
son sensibles a ciertos tipos de cambios pero inmunes a otros.
Algunos de estos componentes están diseñados para ser volátiles. Esperamos que cambien.
Cualquier componente que esperamos que sea volátil no debe depender de un componente
que sea difícil de cambiar. De lo contrario, el componente volátil también será difícil de cambiar.
Es la perversidad del software que un módulo que ha diseñado para que sea fácil de cambiar
pueda ser difícil de cambiar por otra persona que simplemente depende de él. No es necesario
cambiar una sola línea de código fuente en su módulo, sin embargo, su módulo de repente se
volverá más difícil de cambiar. Al cumplir con el Principio de Dependencias Estables (SDP), nos
aseguramos de que los módulos que están destinados a ser fáciles de cambiar no dependan de
módulos que son más difíciles de cambiar.
ESTABILIDAD
¿Qué se entiende por “estabilidad”? Pon un centavo de costado. ¿Es estable en esa posición?
Probablemente dirías "no". Sin embargo, a menos que se le moleste, permanecerá en esa posición
durante mucho tiempo. Por lo tanto, la estabilidad no tiene nada que ver directamente con la
frecuencia de cambio. El centavo no cambia, pero es difícil pensar que es estable.
¿Cómo se relaciona esto con el software? Muchos factores pueden hacer que un componente
de software sea difícil de cambiar, por ejemplo, su tamaño, complejidad y claridad, entre otras
características. Ignoraremos todos esos factores y nos centraremos en algo diferente aquí. Una
forma segura de hacer que un componente de software sea difícil de cambiar es hacer que muchos
otros componentes de software dependan de él. Un componente con muchas dependencias
entrantes es muy estable porque requiere una gran cantidad de trabajo para reconciliar cualquier
cambio con todos los componentes dependientes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 128 128
La figura 14.6 muestra Y, que es un componente muy inestable. Ningún otro componente
depende de Y, por lo que decimos que es irresponsable. Y también tiene tres componentes de
los que depende, por lo que los cambios pueden provenir de tres fuentes externas. Decimos que
Y es dependiente.
MEDIDAS DE ESTABILIDAD
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 129 129
Las métricas Fan-in y Fan- out1 se calculan contando el número de clases fuera del
componente en cuestión que tienen dependencias con las clases dentro del componente en
cuestión. Considere el ejemplo de la figura 14.7.
Digamos que queremos calcular la estabilidad de la componente Cc. Encontramos que hay
tres clases fuera de Cc que dependen de las clases en Cc. Por lo tanto, Fan-in = 3.
Además, hay una clase fuera de Cc de la que dependen las clases en Cc . Por lo tanto, Fan-
out = 1 e I = 1/4.
Cuando la métrica I es igual a 1, significa que ningún otro componente depende de este
componente (Fan-in = 0), y este componente depende de otros componentes (Fan-out > 0).
Esta situación es tan inestable como puede llegar a ser un componente; es irresponsable y
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 130 130
Por el contrario, cuando la métrica I es igual a 0, significa que el componente depende de otros
componentes (Fan-in > 0), pero no depende de ningún otro componente (Fan-out = 0). Tal componente
es responsable e independiente. Es tan estable como puede ser. Sus dependientes dificultan el cambio
del componente y no tiene dependencias que puedan obligarlo a cambiar.
El SDP dice que la métrica I de un componente debe ser mayor que la métrica I de los componentes
de los que depende. Es decir, las métricas I deberían disminuir en la dirección de la dependencia.
Los componentes cambiables están en la parte superior y dependen del componente estable en la
parte inferior. Poner los componentes inestables en la parte superior del diagrama es una convención
útil porque cualquier flecha que apunte hacia arriba está violando el SDP (y, como veremos más
adelante, el ADP).
Figura 14.8 Una configuración ideal para un sistema con tres componentes
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 131 131
Flexible es un componente que hemos diseñado para que sea fácil de cambiar. Queremos
que Flexible sea inestable. Sin embargo, algún desarrollador, trabajando en el componente
llamado Estable, ha colgado una dependencia en Flexible. Esto viola el SDP porque la métrica
I para Estable es mucho más pequeña que la métrica I para Flexible. Como resultado, Flexible
ya no será fácil de cambiar. Un cambio a Flexible nos obligará a tratar con Estable y todos sus
dependientes.
Para solucionar este problema, de alguna manera tenemos que romper la dependencia
de Estable de Flexible. ¿Por qué existe esta dependencia? Supongamos que hay una
clase C dentro de Flexible que necesita usar otra clase U dentro de Estable (Figura 14.10).
Podemos arreglar esto empleando el DIP. Creamos una clase de interfaz llamada US y la
colocamos en un componente llamado UServer. Nos aseguramos de que esta interfaz declare
todos los métodos que U necesita usar. Luego hacemos que C implemente esta interfaz como
se muestra en la Figura 14.11. Esto rompe la dependencia de Stable en Flexible y obliga a ambos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 132 132
Componentes abstractos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 133 133
Parte del software del sistema no debería cambiar muy a menudo. Este software representa
decisiones de política y arquitectura de alto nivel. No queremos que estas decisiones
comerciales y arquitectónicas sean volátiles. Por lo tanto, el software que encapsula las
políticas de alto nivel del sistema debe colocarse en componentes estables (I = 0).
Los componentes inestables (I = 1) deben contener solo el software que es volátil,
software que queremos poder cambiar rápida y fácilmente.
Sin embargo, si las políticas de alto nivel se colocan en componentes estables, será
difícil cambiar el código fuente que representa esas políticas. Esto podría hacer que la
arquitectura general sea inflexible. ¿Cómo puede un componente que es máximamente
estable (I = 0) ser lo suficientemente flexible para resistir el cambio? La respuesta se
encuentra en el OCP. Este principio nos dice que es posible y deseable crear clases que
sean lo suficientemente flexibles para ser extendidas sin necesidad de modificaciones. ¿Qué
tipo de clases se ajustan a este principio? Clases abstractas .
Por lo tanto, para que un componente sea estable, debe constar de interfaces y clases
abstractas para que pueda extenderse. Los componentes estables que son extensibles son
flexibles y no restringen demasiado la arquitectura.
El SAP y el SDP combinados ascienden al DIP para los componentes. Esto es cierto
porque SDP dice que las dependencias deben funcionar en la dirección de la estabilidad, y
SAP dice que la estabilidad implica abstracción. Así , las dependencias se ejecutan en la
dirección de la abstracción.
El DIP, sin embargo, es un principio que se ocupa de las clases, y con las clases no hay
matices de gris. O una clase es abstracta o no lo es. La combinación del SDP y el SAP trata
con componentes, y permite que un componente pueda ser parcialmente abstracto y
parcialmente estable.
ABSTRACCIÓN DE MEDICIÓN
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 134 134
LA SECUENCIA PRINCIPAL
Estamos ahora en condiciones de definir la relación entre estabilidad (I) y abstracción
(A). Para hacerlo, creamos un gráfico con A en el eje vertical e I en el eje horizontal (Figura
14.12). Si trazamos los dos tipos de componentes "buenos" en este gráfico, encontraremos los
componentes que son máximamente estables y abstractos en la parte superior izquierda en (0,
1). Los componentes que son máximamente inestables y concretos están en la parte inferior
derecha en (1, 0).
No todos los componentes caen en una de estas dos posiciones, porque los componentes a
menudo tienen grados de abstracción y estabilidad. Por ejemplo, es muy común que una clase
abstracta derive de otra clase abstracta. La derivada es una abstracción.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 135 135
que tiene una dependencia. Por lo tanto, aunque sea máximamente abstracto, no será
máximamente estable. Su dependencia disminuirá su estabilidad.
Dado que no podemos hacer cumplir una regla de que todos los componentes se sientan en (0, 1) o (1,
0), debemos suponer que hay un lugar geométrico de puntos en el gráfico A/I que define posiciones
razonables para los componentes. Podemos inferir cuál es ese lugar encontrando las áreas donde los
componentes no deberían estar; en otras palabras, determinando las zonas de exclusión (Figura 11.13).
Considere un componente en el área de (0, 0). Este es un componente altamente estable y concreto.
Tal componente no es deseable porque es rígido. No se puede extender porque no es abstracto, y
es muy difícil de cambiar debido a su estabilidad. Por lo tanto, normalmente no esperamos ver
componentes bien diseñados cerca de (0, 0). El área alrededor de (0, 0) es una zona de exclusión
llamada Zona del Dolor.
Algunas entidades de software, de hecho, caen dentro de la Zona de Dolor. Un ejemplo sería un esquema
de base de datos. Los esquemas de bases de datos son notoriamente volátiles, extremadamente
concretos y altamente dependientes. Esta es una de las razones por las que la interfaz entre las aplicaciones
OO y las bases de datos es tan difícil de administrar, y por las que las actualizaciones de esquemas suelen
ser dolorosas.
Otro ejemplo de software que se encuentra cerca del área de (0, 0) es una biblioteca de utilidades
concretas. Aunque dicha biblioteca tiene una métrica I de 1, en realidad puede ser no volátil.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 136 136
Considere el componente String , por ejemplo. Aunque todas las clases dentro de él son concretas,
se usa tan comúnmente que cambiarlo crearía un caos. Por lo tanto , String es no volátil.
Los componentes no volátiles son inofensivos en la zona (0, 0) ya que no es probable que se
cambien. Por esa razón, solo los componentes de software volátiles son problemáticos en Zone
of Pain. Cuanto más volátil es un componente en la Zona de dolor, más "doloroso" es. De hecho,
podríamos considerar la volatilidad como un tercer eje del gráfico. Con este entendimiento, la figura
14.13 muestra solo el plano más doloroso, donde la volatilidad = 1.
La zona de la inutilidad
Las entidades de software que habitan en esta región son una especie de detritus. A menudo
son clases abstractas sobrantes que nadie implementó nunca. Los encontramos en los sistemas
de vez en cuando, sentados en la base del código, sin usar.
Un componente que tenga una posición profunda dentro de la Zona de inutilidad debe contener una
fracción significativa de dichas entidades. Claramente, la presencia de tales entidades inútiles es
indeseable.
Parece claro que nuestros componentes más volátiles deben mantenerse lo más alejados
posible de ambas zonas de exclusión. El lugar geométrico de los puntos que están a la máxima
distancia de cada zona es la línea que conecta (1, 0) y (0, 1). Llamo a esta línea la Secuencia
Principal. 2
La posición más deseable para un componente es uno de los dos extremos de la Secuencia
Principal. Los buenos arquitectos se esfuerzan por colocar la mayoría de sus componentes en
esos puntos finales. Sin embargo, en mi experiencia, una pequeña fracción de la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 137 137
• D3 : Distancia. D = |A+I–1| . El rango de esta métrica es [0, 1]. Un valor de 0 indica que el componente está
directamente en la secuencia principal. Un valor de 1 indica que el componente está lo más alejado posible
de la Secuencia Principal.
Dada esta métrica, un diseño puede analizarse por su conformidad general con la Secuencia Principal. Se
puede calcular la métrica D para cada componente. Cualquier componente que tenga un valor D que no se
acerque a cero se puede volver a examinar y reestructurar.
En el diagrama de dispersión de la figura 14.14, vemos que la mayor parte de los componentes se
encuentran a lo largo de la secuencia principal, pero algunos de ellos están a más de una desviación
estándar (Z = 1) de la media. Vale la pena examinar más de cerca estos componentes aberrantes.
Por alguna razón, son muy abstractos con pocos dependientes o muy concretos con muchos dependientes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 138 138
Otra forma de usar las métricas es trazar la métrica D de cada componente a lo largo del tiempo.
El gráfico de la figura 14.15 es una maqueta de dicho gráfico. Puede ver que algunas
dependencias extrañas se han infiltrado en el componente Nómina en las últimas versiones. El
gráfico muestra un umbral de control en D = 0,1. El punto R2.1 ha superado este límite de
control, por lo que valdría la pena averiguar por qué este componente está tan alejado de la
secuencia principal.
CONCLUSIÓN
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 139 139
1. En publicaciones anteriores, utilicé los nombres de acoplamientos eferentes y aferentes (Ce y Ca) para Fan out y
Fan-in, respectivamente. Eso fue solo arrogancia de mi parte: me gustó la metáfora del sistema nervioso central.
2. El autor pide indulgencia al lector por la arrogancia de tomar prestado un término tan importante
de la astronomía.
3. En publicaciones anteriores, llamé a esta métrica Dÿ. No veo ninguna razón para continuar con esa práctica.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 140 140
EN
ARQUITECTURA
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 141 141
15
¿QUÉ ES LA ARQUITECTURA?
La palabra “arquitectura” evoca visiones de poder y misterio. Nos hace pensar en decisiones de peso
y destreza técnica profunda. La arquitectura de software está en la cúspide de los logros técnicos.
Cuando pensamos en un arquitecto de software, pensamos en alguien que tiene poder y que inspira
respeto. ¿Qué joven aspirante a desarrollador de software no ha soñado con algún día convertirse
en arquitecto de software?
Pero, ¿qué es la arquitectura de software? ¿Qué hace un arquitecto de software y cuándo lo hace?
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 142 142
el resto del equipo hacia un diseño que maximice la productividad. Es posible que los arquitectos de
software no escriban tanto código como otros programadores, pero continúan participando en tareas de
programación. Lo hacen porque no pueden hacer su trabajo correctamente si no están experimentando
los problemas que están creando para el resto de los programadores.
La arquitectura de un sistema de software es la forma que le dan a ese sistema quienes lo construyen.
La forma de esa forma está en la división de ese sistema en componentes, la disposición de esos
componentes y las formas en que esos componentes se comunican entre sí.
La estrategia detrás de esa facilitación es dejar tantas opciones abiertas como sea posible, durante el
mayor tiempo posible.
Quizás esta afirmación te haya sorprendido. Quizás pensó que el objetivo de la arquitectura de
software era hacer que el sistema funcionara correctamente. Ciertamente, queremos que el sistema
funcione correctamente y, ciertamente, la arquitectura del sistema debe admitir eso como una de sus
principales prioridades.
Sin embargo, la arquitectura de un sistema tiene muy poca relación con el funcionamiento de ese
sistema. Hay muchos sistemas por ahí, con arquitecturas terribles, que funcionan muy bien. Sus
problemas no radican en su funcionamiento; más bien, ocurren en su implementación, mantenimiento
y desarrollo continuo.
Esto no quiere decir que la arquitectura no desempeñe un papel en el soporte del comportamiento
adecuado del sistema. Ciertamente lo hace, y ese papel es fundamental. Pero el papel es pasivo y
cosmético, no activo ni esencial. Hay pocas opciones de comportamiento , si es que hay alguna, que la
arquitectura de un sistema pueda dejar abiertas.
El propósito principal de la arquitectura es soportar el ciclo de vida del sistema. Una buena arquitectura
hace que el sistema sea fácil de entender, fácil de desarrollar, fácil de mantener y fácil de implementar.
El objetivo final es minimizar el costo de vida útil del sistema y maximizar la productividad del
programador.
DESARROLLO
Un sistema de software que es difícil de desarrollar no es probable que tenga una larga y saludable
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 143 143
toda la vida. Por lo tanto, la arquitectura de un sistema debe hacer que ese sistema sea fácil de desarrollar
para los equipos que lo desarrollan.
Diferentes estructuras de equipo implican diferentes decisiones arquitectónicas. Por un lado, un pequeño
equipo de cinco desarrolladores puede trabajar juntos con bastante eficacia para desarrollar un sistema
monolítico sin componentes o interfaces bien definidos. De hecho, tal equipo probablemente encontraría
las restricciones de una arquitectura como un impedimento durante los primeros días de desarrollo. Esta
es probablemente la razón por la que tantos sistemas carecen de una buena arquitectura: se comenzaron
sin ninguna, porque el equipo era pequeño y no quería el impedimento de una superestructura.
Por otro lado, un sistema que está siendo desarrollado por cinco equipos diferentes, cada uno de los
cuales incluye siete desarrolladores, no puede progresar a menos que el sistema esté dividido en
componentes bien definidos con interfaces confiables y estables. Si no se consideran otros factores, la
arquitectura de ese sistema probablemente evolucionará en cinco componentes, uno para cada equipo.
No es probable que una arquitectura de componente por equipo de este tipo sea la mejor arquitectura
para la implementación, el funcionamiento y el mantenimiento del sistema. Sin embargo, es la arquitectura
hacia la que gravitará un grupo de equipos si están impulsados únicamente por el cronograma de
desarrollo.
DESPLIEGUE
Para ser efectivo, un sistema de software debe ser implementable. Cuanto mayor sea el costo de
implementación, menos útil será el sistema. Entonces, un objetivo de una arquitectura de software
debe ser crear un sistema que se pueda implementar fácilmente con una sola acción.
Por ejemplo, en el desarrollo inicial de un sistema, los desarrolladores pueden decidir utilizar una
"arquitectura de microservicio". Pueden encontrar que este enfoque hace que el sistema sea muy fácil de
desarrollar ya que los límites de los componentes son muy firmes y las interfaces relativamente estables.
Sin embargo, cuando llegue el momento de implementar el sistema, es posible que descubran que la
cantidad de microservicios se ha vuelto abrumadora; configurar las conexiones entre ellos y el momento de
su inicio también puede convertirse en una gran fuente de errores.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 144 144
OPERACIÓN
El impacto de la arquitectura en la operación del sistema tiende a ser menos dramático que el
impacto de la arquitectura en el desarrollo, implementación y mantenimiento. Casi cualquier
dificultad operativa se puede resolver agregando más hardware al sistema sin afectar drásticamente
la arquitectura del software.
De hecho, hemos visto que esto sucede una y otra vez. Los sistemas de software que tienen
arquitecturas ineficientes a menudo pueden funcionar de manera efectiva simplemente agregando
más almacenamiento y más servidores. El hecho de que el hardware sea barato y las personas
caras significa que las arquitecturas que impiden el funcionamiento no son tan costosas como las
arquitecturas que impiden el desarrollo, la implementación y el mantenimiento.
Esto no quiere decir que no sea deseable una arquitectura que esté bien adaptada al funcionamiento
del sistema. ¡Está! Es solo que la ecuación de costos se inclina más hacia el desarrollo, la
implementación y el mantenimiento.
Habiendo dicho eso, hay otro papel que juega la arquitectura en la operación del sistema: una
buena arquitectura de software comunica las necesidades operativas del sistema.
Quizás una mejor manera de decir esto es que la arquitectura de un sistema hace que el
funcionamiento del sistema sea evidente para los desarrolladores. La arquitectura debe revelar
la operación. La arquitectura del sistema debe elevar los casos de uso, las funciones y los
comportamientos requeridos del sistema a entidades de primera clase que son hitos visibles
para los desarrolladores. Esto simplifica la comprensión del sistema y, por lo tanto, ayuda mucho en
el desarrollo y mantenimiento.
MANTENIMIENTO
De todos los aspectos de un sistema de software, el mantenimiento es el más costoso. El desfile
interminable de nuevas características y el rastro inevitable de defectos y correcciones consumen
una gran cantidad de recursos humanos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 145 145
Como describimos en un capítulo anterior, el software tiene dos tipos de valor: el valor de su
comportamiento y el valor de su estructura. El segundo de estos es el mayor de los dos porque
es este valor el que hace que el software sea suave.
La forma de mantener el software suave es dejar tantas opciones abiertas como sea posible,
durante el mayor tiempo posible. ¿Cuáles son las opciones que debemos dejar abiertas? Son los
detalles que no importan.
Los detalles son aquellas cosas que son necesarias para permitir que los humanos, otros sistemas
y programadores se comuniquen con la política, pero que no afectan en absoluto el comportamiento
de la política. Incluyen dispositivos IO, bases de datos, sistemas web, servidores, marcos, protocolos
de comunicación, etc.
El objetivo del arquitecto es crear una forma para el sistema que reconozca la política como el
elemento más esencial del sistema, al mismo tiempo que hace que los detalles sean irrelevantes
para esa política. Esto permite que las decisiones sobre esos detalles se retrasen y difieran.
Por ejemplo:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 146 146
• No es necesario elegir un sistema de base de datos en los primeros días de desarrollo, porque a
la política de alto nivel no debería importarle qué tipo de base de datos se utilizará.
De hecho, si el arquitecto es cuidadoso, a la política de alto nivel no le importará si la base de datos
es relacional, distribuida, jerárquica o simplemente archivos sin formato.
• No es necesario elegir un servidor web al principio del desarrollo, porque la política de alto nivel no
debe saber que se está entregando a través de la web. Si la política de alto nivel desconoce
HTML, AJAX, JSP, JSF o cualquier otra sopa de letras del desarrollo web, entonces no necesita
decidir qué sistema web usar hasta mucho más tarde en el proyecto. De hecho, ni siquiera tiene
que decidir si el sistema se entregará a través de la web.
• No es necesario adoptar REST al principio del desarrollo, porque la política de alto nivel debe
ser independiente de la interfaz con el mundo exterior. Tampoco es necesario adoptar un marco
de microservicios o un marco SOA. Una vez más, la política de alto nivel no debería preocuparse
por estas cosas. • No es necesario adoptar un marco de inyección de dependencia al principio del
desarrollo, porque a la política de alto nivel no debería importarle cómo se resuelven las dependencias.
Creo que entiendes el punto. Si puede desarrollar la política de alto nivel sin
comprometerse con los detalles que la rodean, puede retrasar y aplazar las decisiones sobre esos
detalles durante mucho tiempo. Y cuanto más espere para tomar esas decisiones, más información
tendrá para tomarlas correctamente.
Esto también te deja la opción de probar diferentes experimentos. Si tiene una parte de la política de
alto nivel funcionando y es independiente de la base de datos, puede intentar conectarla a varias
bases de datos diferentes para verificar la aplicabilidad y el rendimiento.
Lo mismo ocurre con los sistemas web, los marcos web o incluso la propia web.
Cuanto más tiempo deje las opciones abiertas, más experimentos podrá ejecutar, más cosas
podrá intentar y más información tendrá cuando llegue al punto en el que esas decisiones ya no se
puedan aplazar.
¿Qué pasa si las decisiones ya las ha tomado otra persona? ¿Qué sucede si su empresa se
ha comprometido con una determinada base de datos, un determinado servidor web o un determinado
marco? Un buen arquitecto finge que la decisión no se ha tomado y da forma al sistema de tal manera
que esas decisiones aún se pueden aplazar o cambiar durante el mayor tiempo posible.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 147 147
Como ejemplo de este tipo de pensamiento, hagamos un viaje a la década de 1960, cuando las
computadoras eran adolescentes y la mayoría de los programadores eran matemáticos o ingenieros de
otras disciplinas (y un tercio o más eran mujeres).
En esos días cometimos muchos errores. No sabíamos que eran errores en ese momento, por supuesto.
¿Cómo podríamos?
Uno de esos errores fue vincular nuestro código directamente a los dispositivos IO. Si necesitábamos
imprimir algo en una impresora, escribíamos un código que usaba las instrucciones IO que controlarían la
impresora. Nuestro código dependía del dispositivo.
Por ejemplo, cuando escribí programas PDP-8 que se imprimían en la teleimpresora, usé un conjunto de
instrucciones de máquina que se veían así:
PRTCHR, 0 TSF
PRTCHR es una subrutina que imprime un carácter en la teleimpresora. El cero inicial se utilizó como
almacenamiento para la dirección del remitente. (No pregunte). La instrucción TSF omitió la siguiente
instrucción si la teleimpresora estaba lista para imprimir un carácter. Si la teleimpresora estaba ocupada,
entonces TSF simplemente pasó a la instrucción JMP .-1 , que simplemente saltó de regreso a la instrucción
TSF . Si la teleimpresora estaba lista, la TSF saltaba a la instrucción TLS , que enviaba el carácter del
registro A a la teleimpresora.
Luego, la instrucción JMP I PRTCHR regresó a la persona que llamó.
Al principio esta estrategia funcionó bien. Si necesitábamos leer tarjetas del lector de tarjetas, usábamos un
código que hablaba directamente con el lector de tarjetas. Si necesitábamos perforar tarjetas, escribíamos
un código que manipulaba directamente el perforado. Los programas funcionaron perfectamente.
¿Cómo podríamos saber que esto fue un error?
Pero los lotes grandes de tarjetas perforadas son difíciles de manejar. Se pueden perder,
mutilar, girar, barajar o dejar caer. Las tarjetas individuales se pueden perder y se pueden insertar tarjetas
adicionales. Así que la integridad de los datos se convirtió en un problema importante.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 148 148
La cinta magnética fue la solución. Podríamos mover las imágenes de la tarjeta a la cinta. Si dejas caer
una cinta magnética, los registros no se barajan. No puede perder accidentalmente un registro o insertar
un registro en blanco simplemente entregando la cinta. La cinta es mucho más segura.
También es más rápido de leer y escribir, y es muy fácil hacer copias de seguridad.
Desafortunadamente, todo nuestro software fue escrito para manipular lectores de tarjetas y
perforaciones de tarjetas. Esos programas tuvieron que ser reescritos para usar cinta magnética. Ese fue
un gran trabajo.
Ahora el mismo programa podía leer y escribir tarjetas, o leer y escribir cintas, sin ningún cambio. El
Principio Abierto-Cerrado nació (pero aún no tiene nombre).
CORREO BASURA
A fines de la década de 1960, trabajé para una empresa que imprimía correo basura para los
clientes. Los clientes nos enviaban cintas magnéticas con registros unitarios que contenían los
nombres y direcciones de sus clientes, y escribíamos programas que imprimían bonitos anuncios
personalizados.
Ya sabes el tipo:
¡Felicidades!
Te elegimos a TI entre todos los demás que viven en Witchwood Lane para participar en nuestra nueva
y fantástica oferta única...
Los clientes nos enviaban enormes rollos de cartas modelo con todas las palabras excepto el
nombre y la dirección, y cualquier otro elemento que quisieran que imprimiéramos. Escribimos
programas que extraían los nombres, direcciones y otros elementos de la cinta magnética e imprimíamos
esos elementos exactamente donde debían aparecer en los formularios.
Estos rollos de cartas modelo pesaban 500 libras y contenían miles de cartas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 149 149
Los clientes nos enviaban cientos de estos rollos. Imprimiríamos cada uno individualmente.
Al principio, teníamos una IBM 360 imprimiendo en su impresora de línea única. Podríamos imprimir unos
cuantos miles de cartas por turno. Desafortunadamente, esto inmovilizó una máquina muy costosa durante
mucho tiempo. En aquellos días, los IBM 360 se alquilaban por decenas de miles de dólares al mes.
Entonces le dijimos al sistema operativo que usara cinta magnética en lugar de la impresora de línea. A nuestros
programas no les importaba, porque habían sido escritos para usar las abstracciones de IO del sistema operativo.
La 360 podía producir una cinta completa en aproximadamente 10 minutos, lo suficiente para imprimir varios
rollos de cartas modelo. Las cintas se sacaron de la sala de computadoras y se montaron en unidades de cinta
conectadas a impresoras fuera de línea. Teníamos cinco de ellos, y hacíamos funcionar esas cinco impresoras las
24 horas del día, los siete días de la semana, imprimiendo cientos de miles de piezas de correo basura cada
semana.
¡El valor de la independencia del dispositivo fue enorme! Podríamos escribir nuestros programas sin saber ni
importarnos qué dispositivo se usaría. Podríamos probar esos programas usando la impresora de línea local
conectada a la computadora. Entonces podríamos decirle al sistema operativo que "imprimiera" en cinta magnética
y ejecutara cientos de miles de formularios.
Nuestros programas tenían una forma. Esa forma desconectó la política del detalle. La política era el formateo de
los registros de nombre y dirección. El detalle fue el dispositivo. Aplazamos la decisión sobre qué dispositivo
usaríamos.
DIRECCIONAMIENTO FÍSICO
A principios de la década de 1970, trabajé en un gran sistema de contabilidad para un sindicato local de camioneros.
Teníamos una unidad de disco de 25 MB en la que almacenamos registros para Agentes, Empleadores y Miembros.
Los diferentes registros tenían diferentes tamaños, por lo que formateamos los primeros cilindros del disco para que
cada sector tuviera el tamaño de un registro de Agente . Los siguientes cilindros se formatearon para tener sectores
que se ajusten a los registros del empleador . Los últimos cilindros se formatearon para ajustarse a los registros de
los miembros .
Escribimos nuestro software para conocer la estructura detallada del disco. Sabía que el
disco tenía 200 cilindros y 10 cabezas, y que cada cilindro tenía varias decenas de
sectores por cabeza. Sabía qué cilindros contenían los Agentes, los Empleadores y los Miembros.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 150 150
Mantuvimos un índice en el disco que nos permitió buscar a cada uno de los Agentes,
Empleadores y Miembros. Este índice estaba en otro conjunto de cilindros con un formato especial
en el disco. El índice de Agente estaba compuesto por registros que contenían la identificación de un
agente y el número de cilindro, número de cabeza y número de sector de ese registro de Agente . Los
empleadores y los diputados tenían índices similares. Los miembros también se mantuvieron en una
lista doblemente enlazada en el disco. Cada registro de miembro contenía el cilindro, la cabeza y el
número de sector del siguiente registro de miembro y del registro de miembro anterior .
¿Qué sucedería si tuviéramos que actualizar a una nueva unidad de disco, una con más cabezales,
una con más cilindros o una con más sectores por cilindro? Tuvimos que escribir un programa especial
para leer los datos antiguos del disco antiguo y luego escribirlos en el disco nuevo, traduciendo todos los
números de cilindro/cabeza/sector. También tuvimos que cambiar todo el cableado de nuestro código,
¡y ese cableado estaba en todas partes! Todas las reglas de negocio conocían al detalle el esquema
cilindro/culata/sector.
Un día, un programador más experimentado se unió a nuestras filas. Cuando vio lo que habíamos
hecho, la sangre abandonó su rostro y nos miró horrorizado, como si fuéramos algún tipo de
extraterrestres. Luego, amablemente, nos aconsejó que cambiáramos nuestro esquema de
direccionamiento para usar direcciones relativas.
Nuestro colega más sabio sugirió que consideráramos el disco como una gran matriz lineal de sectores,
cada uno direccionable por un número entero secuencial. Entonces podríamos escribir una pequeña
rutina de conversión que conociera la estructura física del disco y pudiera traducir la dirección relativa
a un número de cilindro/cabeza/sector sobre la marcha.
Afortunadamente para nosotros, seguimos su consejo. Cambiamos la política de alto nivel del
sistema para que sea independiente de la estructura física del disco. Eso nos permitió desvincular
la decisión sobre la estructura de la unidad de disco de la aplicación.
CONCLUSIÓN
Las dos historias de este capítulo son ejemplos, en lo pequeño, de un principio que los
arquitectos emplean en lo grande. Los buenos arquitectos separan cuidadosamente los detalles de la
política y luego desvinculan la política de los detalles de manera tan completa que la política no tiene
conocimiento de los detalles y no depende de los detalles de ninguna manera. Los buenos arquitectos
diseñan la política para que las decisiones sobre los detalles se puedan retrasar y diferir el mayor tiempo
posible.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 151 151
dieciséis
INDEPENDENCIA
CASOS DE USO
La primera viñeta, casos de uso, significa que la arquitectura del sistema debe respaldar la
intención del sistema. Si el sistema es una aplicación de carrito de compras, entonces el
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 152 152
la arquitectura debe admitir los casos de uso del carrito de la compra. De hecho, esta es la primera
preocupación del arquitecto y la primera prioridad de la arquitectura. La arquitectura debe soportar los
casos de uso.
Sin embargo, como comentamos anteriormente, la arquitectura no ejerce mucha influencia sobre el
comportamiento del sistema. Hay muy pocas opciones de comportamiento que la arquitectura pueda
dejar abiertas. Pero la influencia no lo es todo. Lo más importante que puede hacer una buena arquitectura
para respaldar el comportamiento es aclarar y exponer ese comportamiento para que la intención del
sistema sea visible a nivel arquitectónico.
Una aplicación de carrito de compras con una buena arquitectura se parecerá a una aplicación de
carrito de compras. Los casos de uso de ese sistema serán claramente visibles dentro de la estructura
de ese sistema. Los desarrolladores no tendrán que buscar comportamientos, porque esos
comportamientos serán elementos de primera clase visibles en el nivel superior del sistema. Esos
elementos serán clases o funciones o módulos que tienen posiciones destacadas dentro de la
arquitectura, y tendrán nombres que describan claramente su función.
OPERACIÓN
La arquitectura juega un papel más sustancial y menos cosmético en el apoyo a la operación del
sistema. Si el sistema debe manejar 100 000 clientes por segundo, la arquitectura debe admitir ese
tipo de rendimiento y tiempo de respuesta para cada caso de uso que lo requiera. Si el sistema debe
consultar grandes cubos de datos en milisegundos, entonces la arquitectura debe estar estructurada
para permitir este tipo de operación.
Para algunos sistemas, esto significará organizar los elementos de procesamiento del sistema en una
serie de pequeños servicios que se pueden ejecutar en paralelo en muchos servidores diferentes.
Para otros sistemas, significará una plétora de pequeños subprocesos livianos que comparten el espacio
de direcciones de un solo proceso dentro de un solo procesador. Otros sistemas necesitarán solo unos
pocos procesos ejecutándose en espacios de direcciones aislados. Y algunos sistemas pueden incluso
sobrevivir como simples programas monolíticos que se ejecutan en un solo proceso.
Por extraño que parezca, esta decisión es una de las opciones que un buen arquitecto deja abiertas.
Un sistema que está escrito como un monolito, y que depende de esa estructura monolítica, no puede
actualizarse fácilmente a múltiples procesos, múltiples subprocesos o microservicios si surge la
necesidad. En comparación, una arquitectura que mantiene el aislamiento adecuado de sus componentes
y no asume los medios de
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 153 153
la comunicación entre esos componentes será mucho más fácil de transitar a través del espectro de
subprocesos, procesos y servicios a medida que las necesidades operativas del sistema cambien con el
tiempo.
DESARROLLO
La arquitectura juega un papel importante en el apoyo al entorno de desarrollo.
Aquí es donde entra en juego la ley de Conway. La ley de Conway dice:
Cualquier organización que diseñe un sistema producirá un diseño cuya estructura es una copia de
la estructura de comunicación de la organización.
Un sistema que debe ser desarrollado por una organización con muchos equipos y muchas
preocupaciones debe tener una arquitectura que facilite las acciones independientes de esos
equipos, para que los equipos no interfieran entre sí durante el desarrollo. Esto se logra mediante la
partición adecuada del sistema en componentes bien aislados que se pueden desarrollar de forma
independiente. Luego, esos componentes se pueden asignar a equipos que pueden trabajar de forma
independiente entre sí.
DESPLIEGUE
La arquitectura también juega un papel muy importante en la determinación de la facilidad con
la que se implementa el sistema. El objetivo es el "despliegue inmediato". Una buena arquitectura no
se basa en docenas de pequeños scripts de configuración y ajustes de archivos de propiedades. No
requiere la creación manual de directorios o archivos que deben organizarse así. Una buena arquitectura
ayuda a que el sistema se pueda implementar inmediatamente después de la construcción.
Una vez más, esto se logra mediante la partición y el aislamiento adecuados de los
componentes del sistema, incluidos los componentes maestros que unen todo el sistema y aseguran
que cada componente se inicie, integre y supervise correctamente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 154 154
La realidad es que lograr este equilibrio es bastante difícil. El problema es que la mayoría de las
veces no sabemos cuáles son todos los casos de uso, ni conocemos las restricciones operativas, la
estructura del equipo o los requisitos de implementación. Peor aún, incluso si los conociéramos,
inevitablemente cambiarán a medida que el sistema avanza a través de su ciclo de vida. En
resumen, las metas que debemos cumplir son indistintas e inconstantes. Bienvenidos al mundo real.
Pero no todo está perdido: algunos principios de la arquitectura son relativamente económicos
de implementar y pueden ayudar a equilibrar esas preocupaciones, incluso cuando no tiene una
idea clara de los objetivos que debe alcanzar. Esos principios nos ayudan a dividir nuestros sistemas
en componentes bien aislados que nos permiten dejar tantas opciones abiertas como sea posible,
durante el mayor tiempo posible.
Una buena arquitectura hace que el sistema sea fácil de cambiar, en todas las formas en que
debe cambiar, dejando opciones abiertas.
CAPAS DE DESACOPLAMIENTO
Considere los casos de uso. El arquitecto quiere que la estructura del sistema admita todos los casos
de uso necesarios, pero no sabe cuáles son todos esos casos de uso. Sin embargo , el arquitecto
conoce la intención básica del sistema. Es un sistema de carrito de compras, o es un sistema de lista
de materiales, o es un sistema de procesamiento de pedidos. Entonces, el arquitecto puede emplear
el principio de responsabilidad única y el principio de cierre común para separar las cosas que
cambian por diferentes razones y recopilar las cosas que cambian por las mismas razones, dado el
contexto de la intención del sistema.
¿Qué cambia por diferentes razones? Hay algunas cosas obvias. Las interfaces de usuario cambian
por razones que no tienen nada que ver con las reglas comerciales. Los casos de uso tienen
elementos de ambos. Claramente, entonces, un buen arquitecto querrá separar las porciones de UI
de un caso de uso de las porciones de reglas de negocios de tal manera que puedan cambiarse
independientemente unas de otras, manteniendo esos casos de uso visibles y claros.
Las propias reglas de negocio pueden estar estrechamente vinculadas a la aplicación o pueden
ser más generales. Por ejemplo, la validación de campos de entrada es una regla comercial que
está estrechamente ligada a la propia aplicación. Por el contrario, el cálculo de los intereses de
una cuenta y el recuento del inventario son reglas de negocio que se asocian más estrechamente
con el dominio. Estos dos tipos diferentes de reglas cambiarán a diferentes ritmos y por diferentes
razones, por lo que deben separarse para que puedan cambiarse de forma independiente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 155 155
La base de datos, el lenguaje de consulta e incluso el esquema son detalles técnicos que no tienen nada que
ver con las reglas comerciales o la interfaz de usuario. Cambiarán a tasas y por razones que son independientes
de otros aspectos del sistema. En consecuencia, la arquitectura debe separarlos del resto del sistema para que
puedan cambiarse de forma independiente.
¿Qué más cambia por diferentes razones? ¡Los casos de uso en sí mismos! Es casi seguro que el caso de uso para
agregar un pedido a un sistema de entrada de pedidos cambiará a un ritmo diferente y por diferentes razones que
el caso de uso que elimina un pedido del sistema. Los casos de uso son una forma muy natural de dividir el sistema.
Al mismo tiempo, los casos de uso son cortes verticales estrechos que atraviesan las capas horizontales del
sistema. Cada caso de uso utiliza alguna interfaz de usuario, algunas reglas comerciales específicas de la aplicación,
algunas reglas comerciales independientes de la aplicación y alguna funcionalidad de la base de datos.
Por lo tanto, a medida que dividimos el sistema en capas horizontales, también dividimos el sistema en casos de
uso verticales delgados que atraviesan esas capas.
Para lograr este desacoplamiento, separamos la interfaz de usuario del caso de uso de orden de adición de la
interfaz de usuario del caso de uso de orden de eliminación. Hacemos lo mismo con las reglas de negocio, y con la
base de datos. Mantenemos los casos de uso separados por la altura vertical del sistema.
Puedes ver el patrón aquí. Si desacopla los elementos del sistema que cambian por diferentes razones, puede
continuar agregando nuevos casos de uso sin interferir con los antiguos. Si también agrupa la interfaz de usuario y
la base de datos para admitir esos casos de uso, de modo que cada caso de uso use un aspecto diferente de la
interfaz de usuario y la base de datos, es poco probable que agregar nuevos casos de uso afecte a los más antiguos.
MODO DE DESACOPLAMIENTO
Ahora piense en lo que significa todo ese desacoplamiento para la segunda viñeta: operaciones. Si se separan los
diferentes aspectos de los casos de uso, es probable que los que deben ejecutarse con un rendimiento alto ya estén
separados de los que deben ejecutarse con un rendimiento bajo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 156 156
Si la interfaz de usuario y la base de datos se han separado de las reglas comerciales, pueden ejecutarse
en diferentes servidores. Aquellos que requieren mayor ancho de banda se pueden replicar en muchos
servidores.
En resumen, el desacoplamiento que hicimos por el bien de los casos de uso también ayuda con
las operaciones. Sin embargo, para aprovechar el beneficio operativo, el desacoplamiento debe
tener el modo apropiado. Para ejecutarse en servidores separados, los componentes separados no
pueden depender de estar juntos en el mismo espacio de direcciones de un procesador. Deben ser
servicios independientes, que se comunican a través de una red de algún tipo.
Si esa nomenclatura activa algunas alarmas en su mente, no se preocupe. No voy a decirles que
SoA es la mejor arquitectura posible, o que los microservicios son la ola del futuro. El punto que se
destaca aquí es que a veces tenemos que separar nuestros componentes hasta el nivel de servicio.
Recuerde, una buena arquitectura deja opciones abiertas. El modo de desacoplamiento es una de esas
opciones.
Antes de explorar más ese tema, echemos un vistazo a las otras dos viñetas.
Siempre que las capas y los casos de uso estén desacoplados, la arquitectura del sistema admitirá la
organización de los equipos, independientemente de si están organizados como equipos de funciones,
equipos de componentes, equipos de capas o alguna otra variación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 157 157
IMPLEMENTACIÓN INDEPENDIENTE
El desacoplamiento de los casos de uso y las capas también ofrece un alto grado de flexibilidad en la
implementación. De hecho, si el desacoplamiento se hace bien, entonces debería ser posible intercambiar
capas en caliente y casos de uso en sistemas en ejecución. Agregar un nuevo caso de uso podría ser tan
simple como agregar algunos archivos jar o servicios nuevos al sistema y dejar el resto solo.
DUPLICACIÓN
Los arquitectos a menudo caen en una trampa, una trampa que depende de su miedo a la duplicación.
Pero hay diferentes tipos de duplicación. Existe una verdadera duplicación, en la que cada cambio en una
instancia requiere el mismo cambio en cada duplicado de esa instancia. Luego está la duplicación falsa o
accidental. Si dos secciones de código aparentemente duplicadas evolucionan a lo largo de diferentes caminos,
si cambian a diferentes velocidades y por diferentes razones, entonces no son verdaderos duplicados. Regrese
a ellos en unos años y encontrará que son muy diferentes entre sí.
Ahora imagine dos casos de uso que tienen estructuras de pantalla muy similares. Es probable que los
arquitectos se sientan fuertemente tentados a compartir el código de esa estructura. ¿Pero deberían?
¿Es eso una verdadera duplicación? ¿O es accidental?
Lo más probable es que sea accidental. A medida que pasa el tiempo, lo más probable es que esas dos
pantallas diverjan y eventualmente se vean muy diferentes. Por esta razón, se debe tener cuidado para evitar
unificarlos. De lo contrario, separarlos más tarde será un desafío.
Cuando esté separando verticalmente los casos de uso, se encontrará con este problema y su tentación será
emparejar los casos de uso porque tienen estructuras de pantalla similares, algoritmos similares o consultas
de base de datos y/o esquemas similares.
Ten cuidado. Resista la tentación de cometer el pecado de la eliminación automática de la duplicación.
Asegúrate de que la duplicación sea real.
De la misma manera, cuando separa capas horizontalmente, puede notar que la estructura de datos de un
registro de base de datos en particular es muy similar a los datos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 158 158
Volver a los modos. Hay muchas formas de desacoplar capas y casos de uso. Se pueden desacoplar
en el nivel del código fuente, en el nivel del código binario (implementación) y en el nivel de la unidad de
ejecución (servicio).
• Nivel de fuente. Podemos controlar las dependencias entre los módulos de código fuente para que
los cambios en un módulo no fuercen los cambios o la recompilación de otros (por ejemplo, Ruby
Gems).
a través de paquetes de red, de modo que cada unidad de ejecución sea completamente independiente
de los cambios binarios y de origen en otras (por ejemplo, servicios o microservicios).
La respuesta es que es difícil saber qué modo es mejor durante las primeras fases de un proyecto. De
hecho, a medida que el proyecto madura, el modo óptimo puede cambiar.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 159 159
Por ejemplo, no es difícil imaginar que un sistema que se ejecuta cómodamente en un servidor en este
momento podría crecer hasta el punto en que algunos de sus componentes deberían ejecutarse en
servidores separados. Si bien el sistema se ejecuta en un solo servidor, el desacoplamiento a nivel de
fuente podría ser suficiente. Más tarde, sin embargo, podría requerir el desacoplamiento en unidades
desplegables o incluso servicios.
Una solución (que parece ser popular en este momento) es simplemente desacoplar en el nivel de servicio
de forma predeterminada. Un problema con este enfoque es que es costoso y fomenta el desacoplamiento
de grano grueso. No importa cuán "micro" sean los microservicios, es probable que el desacoplamiento no
sea lo suficientemente detallado.
Otro problema con el desacoplamiento de nivel de servicio es que es costoso, tanto en tiempo de
desarrollo como en recursos del sistema. Tratar con límites de servicio donde no se necesita ninguno es
una pérdida de esfuerzo, memoria y ciclos. Y sí, sé que los dos últimos son baratos, pero el primero no lo
es.
Con este enfoque, inicialmente los componentes se separan a nivel del código fuente.
Eso puede ser lo suficientemente bueno para la duración de la vida del proyecto. Sin embargo, si
surgen problemas de implementación o desarrollo, llevar parte del desacoplamiento a un nivel de
implementación puede ser suficiente, al menos por un tiempo.
A medida que aumentan los problemas de desarrollo, implementación y operación, elijo cuidadosamente
qué unidades desplegables convertir en servicios y cambio gradualmente el sistema en esa dirección.
Con el tiempo, las necesidades operativas del sistema pueden disminuir. Lo que antes requería
desacoplamiento a nivel de servicio ahora puede requerir solo desacoplamiento a nivel de
implementación o incluso a nivel de fuente.
Una buena arquitectura permitirá que un sistema nazca como un monolito, implementado en un solo
archivo, pero luego crezca hasta convertirse en un conjunto de unidades implementables de forma
independiente, y luego todo el camino hasta servicios y/o microservicios independientes. Más tarde, a
medida que las cosas cambien, debería permitir revertir esa progresión y deslizarse hacia abajo hasta
convertirse en un monolito.
Una buena arquitectura protege la mayor parte del código fuente de esos cambios. Deja el modo de
desacoplamiento abierto como una opción para que las implementaciones grandes puedan usar uno
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 160 160
CONCLUSIÓN
Sí, esto es complicado. Y no digo que el cambio de modos de desacoplamiento deba ser una opción de
configuración trivial (aunque a veces eso es apropiado). Lo que digo es que el modo de desacoplamiento de un
sistema es una de esas cosas que es probable que cambie con el tiempo, y un buen arquitecto prevé y facilita
adecuadamente esos cambios.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 161 161
17
LÍMITES: LÍNEAS DE DIBUJO
La arquitectura de software es el arte de dibujar líneas que yo llamo límites. Esos límites
separan los elementos de software entre sí y restringen a los de un lado para que no conozcan
a los del otro. Algunas de esas líneas se dibujan muy temprano en la vida de un proyecto,
incluso antes de que se escriba ningún código. Otros se dibujan mucho más tarde. Los que
se extraen antes se extraen con el fin de diferir las decisiones durante el mayor tiempo posible
y de evitar que esas decisiones contaminen la lógica empresarial central.
¿Qué tipo de decisiones son prematuras? Decisiones que no tienen nada que ver con el
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 162 162
los requisitos comerciales, los casos de uso, del sistema. Estos incluyen decisiones sobre marcos,
bases de datos, servidores web, bibliotecas de utilidades, inyección de dependencia y similares. Una
buena arquitectura de sistema es aquella en la que decisiones como estas se vuelven auxiliares y
diferibles. Una buena arquitectura de sistema no depende de esas decisiones. Una buena arquitectura
de sistema permite que esas decisiones se tomen en el último momento posible, sin un impacto
significativo.
Pero luego, a fines de la década de 1990, la web emergió como una fuerza. De repente todo el mundo
tenía que tener una solución web, y P no fue la excepción. Los clientes de P pedían a gritos una
versión del producto en la web. Para satisfacer esta demanda, la compañía contrató a un grupo de
veinteañeros programadores de Java y se embarcó en un proyecto para webizar su producto.
Los muchachos de Java soñaban con granjas de servidores danzando en sus cabezas, por lo que
adoptaron una rica “arquitectura”1 de tres niveles que podían distribuir a través de tales granjas. Habría
servidores para la GUI, servidores para el middleware y servidores para la base de datos. Por supuesto.
Los programadores decidieron, desde el principio, que todos los objetos de dominio tendrían tres
instancias: una en el nivel de la GUI, una en el nivel del middleware y otra en el nivel de la base de
datos. Dado que estas instancias vivían en diferentes máquinas, se estableció un rico sistema de
comunicaciones entre procesadores y entre niveles. Las invocaciones de métodos entre niveles se
convirtieron en objetos, se serializaron y se clasificaron a través de la red.
Ahora imagine lo que se necesita para implementar una función simple como agregar un nuevo
campo a un registro existente. Ese campo tuvo que agregarse a las clases en los tres niveles y a
varios de los mensajes entre niveles. Dado que los datos viajaban en ambas direcciones, era
necesario diseñar cuatro protocolos de mensajes. Cada protocolo tenía un lado de envío y uno de
recepción, por lo que se requerían ocho controladores de protocolo. Se tuvieron que construir tres
ejecutables, cada uno con tres objetos comerciales actualizados, cuatro mensajes nuevos y ocho
controladores nuevos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 163 163
Y piense en lo que tuvieron que hacer esos ejecutables para implementar las características más simples.
Piense en todas las instancias de objetos, todas las serializaciones, todo el cálculo y desclasificación,
toda la construcción y análisis de mensajes, todas las comunicaciones de socket, administradores de
tiempo de espera, escenarios de reintento y todas las demás cosas adicionales que tiene que hacer solo
para hacer una cosa simple.
Por supuesto, durante el desarrollo, los programadores no tenían una granja de servidores. De hecho,
simplemente ejecutaron los tres ejecutables en tres procesos diferentes en una sola máquina.
Se desarrollaron de esta manera durante varios años. Pero estaban convencidos de que su
arquitectura era correcta. Y así, a pesar de que se estaban ejecutando en una sola máquina, continuaron
con todas las instancias de objetos, todas las serializaciones, todas las serializaciones y desmarshaling,
toda la construcción y análisis de mensajes, todas las comunicaciones de socket y todas las cosas
adicionales. en una sola máquina.
La ironía es que la empresa P nunca vendió un sistema que requiriera una granja de servidores. Cada
sistema que implementaron alguna vez fue un solo servidor. Y en ese único servidor, los tres ejecutables
continuaron todas las instancias de objetos, todas las serializaciones, todas las serializaciones y
desmarshaling, toda la construcción y análisis de mensajes, todas las comunicaciones de socket y
todas las cosas adicionales, en previsión de un servidor. granja que nunca existió, y nunca existiría.
La tragedia es que los arquitectos, al tomar una decisión prematura, multiplicaron enormemente el
esfuerzo de desarrollo.
Considere W, una empresa local que administra flotas de automóviles de empresa. Recientemente
contrataron a un "Arquitecto" para controlar su esfuerzo de software irregular. Y, déjame decirte,
control era el segundo nombre de este tipo. Rápidamente se dio cuenta de que lo que esta pequeña
operación necesitaba era una "ARQUITECTURA" completa, a escala empresarial y orientada al
servicio . Creó un enorme modelo de dominio de todos los diferentes "objetos" en el negocio, diseñó un
conjunto de servicios para administrar estos objetos de dominio y puso a todos los desarrolladores en el
camino al infierno. Como ejemplo simple, suponga que desea agregar el nombre, la dirección y el número
de teléfono de una persona de contacto a un registro de ventas. Tenía que ir a ServiceRegistry y solicitar
el ID de servicio de ContactService. Luego, tenía que enviar un mensaje CreateContact al ContactService.
Por supuesto, este mensaje tenía docenas de campos que todos tenían que tener
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 164 164
datos válidos en ellos, datos a los que el programador no tenía acceso, ya que todo lo que tenía el
programador era un nombre, una dirección y un número de teléfono. Después de falsificar los datos, el
programador tuvo que insertar la identificación del contacto recién creado en el registro de ventas y enviar el
mensaje UpdateContact a SaleRecordService.
Por supuesto, para probar cualquier cosa, tenía que activar todos los servicios necesarios, uno por uno, y
activar el bus de mensajes, y el servidor BPel, y... Y luego, estaban los retrasos en la propagación a medida
que estos mensajes rebotaban de un servicio a otro. , y esperó cola tras cola.
Y luego, si quisiera agregar una nueva función, bueno, puede imaginar el acoplamiento entre todos esos
servicios y el gran volumen de WSDL que necesitaban cambios, y todas las redespliegues que esos
cambios requirieron...
No hay nada intrínsecamente malo en un sistema de software estructurado en torno a servicios. El error en
W fue la adopción prematura y la aplicación de un conjunto de herramientas que prometían SoA, es decir, la
adopción prematura de un conjunto masivo de servicios de objetos de dominio. El costo de esos errores fue
puras horas-persona, horas-persona en masa, arrojadas por el vórtice de SoA.
Podría seguir describiendo un fracaso arquitectónico tras otro. Pero mejor hablemos de un éxito
arquitectónico.
APTITUD FÍSICA
Mi hijo Micah y yo comenzamos a trabajar en FitNesse en 2001. La idea era crear un wiki simple que
envolviera la herramienta FIT de Ward Cunningham para escribir pruebas de aceptación.
Esto fue en los días anteriores a que Maven "resolviera" el problema del archivo jar. Estaba convencido de que
cualquier cosa que produjéramos no debería requerir que las personas descargaran más de un archivo jar.
Llamé a esta regla "Descargar y listo". Esta regla impulsó muchas de nuestras decisiones.
Una de las primeras decisiones fue escribir nuestro propio servidor web, específico para las necesidades de
FitNesse. Esto puede sonar absurdo. Incluso en 2001 había muchos servidores web de código abierto que
podríamos haber utilizado. Sin embargo, escribir el nuestro resultó ser una muy buena decisión porque un
servidor web básico es una pieza de software muy simple de escribir y nos permitió posponer cualquier
decisión sobre el marco web hasta mucho más tarde.2
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 165 165
Otra decisión inicial fue evitar pensar en una base de datos. Teníamos MySQL en el fondo de nuestras
mentes, pero retrasamos deliberadamente esa decisión al emplear un diseño que hizo que la decisión fuera
irrelevante. Ese diseño fue simplemente para poner una interfaz entre todos los accesos a los datos y el
depósito de datos en sí.
Ponemos los métodos de acceso a datos en una interfaz llamada WikiPage. Esos métodos proporcionaron
toda la funcionalidad que necesitábamos para encontrar, recuperar y guardar páginas. Por supuesto, no
implementamos esos métodos al principio; simplemente los apagamos mientras trabajábamos en funciones
que no implicaban buscar y guardar los datos.
De hecho, durante tres meses simplemente trabajamos en la traducción del texto wiki a HTML. Esto no
requería ningún tipo de almacenamiento de datos, por lo que creamos una clase llamada MockWikiPage que
simplemente dejó los métodos de acceso a los datos bloqueados.
Eventualmente, esos stubs se volvieron insuficientes para las funciones que queríamos escribir.
Necesitábamos acceso a datos reales, no stubs. Así que creamos un nuevo derivado de WikiPage llamado
InMemoryPage. Este derivado implementó el método de acceso a datos para administrar una tabla hash de
páginas wiki, que guardamos en RAM.
Esto nos permitió escribir artículo tras artículo durante un año completo. De hecho, obtuvimos toda la primera
versión del programa FitNesse funcionando de esta manera. Podríamos crear páginas, vincular a otras
páginas, hacer todo el formato wiki elegante e incluso ejecutar pruebas con FIT. Lo que no pudimos hacer
fue guardar nada de nuestro trabajo.
Cuando llegó el momento de implementar la persistencia, volvimos a pensar en MySQL, pero decidimos
que no era necesario a corto plazo, porque sería muy fácil escribir las tablas hash en archivos planos. Así
que implementamos FileSystemWikiPage, que simplemente movió la funcionalidad a archivos planos, y
luego continuamos desarrollando más funciones.
Tres meses después, llegamos a la conclusión de que la solución de archivo plano era lo suficientemente
buena; decidimos abandonar la idea de MySQL por completo. Diferimos esa decisión hasta la inexistencia
y nunca miramos hacia atrás.
Ese sería el final de la historia si no fuera por uno de nuestros clientes que decidió que necesitaba poner el
wiki en MySQL para sus propios fines. Le mostramos la arquitectura de WikiPages que nos había permitido
aplazar la decisión. Regresó un día después con todo el sistema funcionando en MySQL. Simplemente
escribió un derivado de MySqlWikiPage y lo hizo funcionar.
Solíamos empaquetar esa opción con FitNesse, pero nadie más la usó, así que
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 166 166
finalmente lo dejamos caer. Incluso el cliente que escribió el derivado finalmente lo abandonó.
Al principio del desarrollo de FitNesse, trazamos una línea divisoria entre las reglas comerciales y las bases de
datos. Esa línea impedía que las reglas comerciales supieran nada sobre la base de datos, aparte de los métodos
simples de acceso a datos. Esa decisión nos permitió aplazar la elección e implementación de la base de datos
durante más de un año. Nos permitió probar la opción del sistema de archivos, y nos permitió cambiar de dirección
cuando vimos una mejor solución. Sin embargo, no impidió, ni siquiera impidió, moverse en la dirección original
(MySQL) cuando alguien lo quería.
El hecho de que no tuviéramos una base de datos funcionando durante 18 meses de desarrollo significó
que, durante 18 meses, no tuvimos problemas de esquema, problemas de consulta, problemas de servidor
de base de datos, problemas de contraseña, problemas de tiempo de conexión y todos los demás problemas
desagradables que levantan sus feas cabezas cuando abres una base de datos. También significó que todas
nuestras pruebas se ejecutaron rápido, porque no había una base de datos para ralentizarlas.
En resumen, trazar las líneas divisorias nos ayudó a retrasar y diferir las decisiones y, en última instancia,
nos ahorró una enorme cantidad de tiempo y dolores de cabeza. Y eso es lo que debe hacer una buena
arquitectura.
Trazas líneas entre las cosas que importan y las que no. La GUI no importa para las reglas comerciales, por lo
que debe haber una línea entre ellas. La base de datos no le importa a la GUI, por lo que debe haber una línea
entre ellos. La base de datos no importa para las reglas comerciales, por lo que debe haber una línea entre
ellas.
Algunos de ustedes pueden haber rechazado una o más de esas declaraciones, especialmente la parte sobre
las reglas comerciales que no se preocupan por la base de datos. A muchos de nosotros nos han enseñado a
creer que la base de datos está inextricablemente conectada con las reglas comerciales. Algunos de nosotros
incluso estamos convencidos de que la base de datos es la encarnación de las reglas comerciales.
Pero, como veremos en otro capítulo, esta idea está equivocada. La base de datos es una herramienta que las
reglas de negocio pueden utilizar indirectamente. Las reglas comerciales no necesitan saber sobre el esquema,
el lenguaje de consulta o cualquier otro detalle sobre la base de datos. Todo lo que las reglas de negocio necesitan
saber es que hay un conjunto de funciones que
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 167 167
se puede utilizar para obtener o guardar datos. Esto nos permite poner la base de datos detrás de
una interfaz.
Las clases e interfaces en este diagrama son simbólicas. En una aplicación real, habría muchas clases
de reglas comerciales, muchas clases de interfaz de base de datos y muchas implementaciones de
acceso a la base de datos. Todos ellos, sin embargo, seguirían más o menos el mismo patrón.
¿Dónde está la línea divisoria? El límite se dibuja a lo largo de la relación de herencia, justo
debajo de la interfaz de la base de datos (Figura 17.2).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 168 168
Tenga en cuenta las dos flechas que salen de la clase DatabaseAccess . Esas dos
flechas apuntan fuera de la clase DatabaseAccess . Eso significa que ninguna de estas
clases sabe que existe la clase DatabaseAccess .
Tenga en cuenta la dirección de la flecha. La base de datos conoce las BusinessRules. Las
BusinessRules no conocen la Base de Datos. Esto implica que el
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 169 169
La dirección de esta línea es importante. Muestra que la base de datos no es importante para BusinessRules,
pero la base de datos no puede existir sin BusinessRules.
Si eso le parece extraño, recuerde este punto: El componente Base de datos contiene el
código que traduce las llamadas realizadas por BusinessRules al lenguaje de consulta de la
base de datos. Es ese código de traducción el que sabe sobre el
Reglas del negocio.
Habiendo dibujado esta línea límite entre los dos componentes, y habiendo establecido la
dirección de la flecha hacia BusinessRules, ahora podemos ver que BusinessRules podría
usar cualquier tipo de base de datos. El componente de base de datos podría reemplazarse
con muchas implementaciones diferentes; a BusinessRules no le importa.
La base de datos podría implementarse con Oracle, MySQL, Couch, Datomic o incluso archivos
planos. A las reglas de negocio no les importa en absoluto. Y eso significa que la decisión de la
base de datos se puede aplazar y usted puede concentrarse en escribir y probar las reglas
comerciales antes de tener que tomar la decisión de la base de datos.
Los desarrolladores y los clientes a menudo se confunden acerca de qué es el sistema. Ven la
GUI y piensan que la GUI es el sistema. Definen un sistema en términos de la GUI, por lo que
creen que deberían ver que la GUI comience a funcionar de inmediato. No se dan cuenta de un
principio de importancia crítica: el IO es irrelevante.
Esto puede ser difícil de entender al principio. A menudo pensamos en el comportamiento del
sistema en términos del comportamiento del IO. Considere un videojuego, por ejemplo. Su
experiencia está dominada por la interfaz: la pantalla, el mouse, los botones y los sonidos. Olvidas
que detrás de esa interfaz hay un modelo, un conjunto sofisticado de funciones y estructuras de
datos, que lo impulsa. Más importante aún, ese modelo no necesita la interfaz. Ejecutaría
felizmente sus deberes, modelando todos los eventos en el juego, sin que el juego se muestre en
la pantalla. La interfaz no importa para el modelo: las reglas comerciales.
Y así, una vez más, vemos los componentes GUI y BusinessRules separados por una línea de
límite (Figura 17.4). Una vez más, vemos que el componente menos relevante
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 170 170
depende del componente más relevante. Las flechas muestran qué componente conoce al otro y,
por lo tanto, qué componente se preocupa por el otro. La GUI se preocupa por BusinessRules.
Habiendo dibujado este límite y esta flecha, ahora podemos ver que la GUI podría reemplazarse
con cualquier otro tipo de interfaz, y a BusinessRules no le importaría.
ARQUITECTURA DE PLUGIN
En conjunto, estas dos decisiones sobre la base de datos y la GUI crean una especie de patrón
para la adición de otros componentes. Ese patrón es el mismo patrón que utilizan los sistemas
que permiten complementos de terceros.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 171 171
Debido a que la interfaz de usuario en este diseño se considera un complemento, hemos hecho posible
conectar muchos tipos diferentes de interfaces de usuario. Pueden estar basados en web, basados en cliente/
servidor, basados en SOA, basados en Consola o basados en cualquier otro tipo de tecnología de interfaz de
usuario.
Lo mismo ocurre con la base de datos. Dado que hemos optado por tratarlo como un complemento, podemos
reemplazarlo con cualquiera de las diversas bases de datos SQL, una base de datos NOSQL, una base de
datos basada en un sistema de archivos o cualquier otro tipo de tecnología de base de datos que consideremos
necesaria en el futuro. .
Estos reemplazos pueden no ser triviales. Si la implementación inicial de nuestro sistema se basó en la web,
entonces escribir el complemento para una interfaz de usuario cliente-servidor podría ser un desafío. Es
probable que algunas de las comunicaciones entre las reglas comerciales y la nueva interfaz de usuario deban
modificarse. Aun así, al comenzar con la presunción de una estructura de complemento, al menos hemos
hecho que ese cambio sea práctico.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 172 172
¿Qué equipo puede dañar al otro? ¿Qué equipo es inmune al otro? La estructura de
dependencia cuenta la historia (Figura 17.6). El código fuente de ReSharper depende del código
fuente de Visual Studio. Por lo tanto, no hay nada que el equipo de ReSharper pueda hacer para
molestar al equipo de Visual Studio. Pero el equipo de Visual Studio podría desactivar por completo al
equipo de ReSharper si así lo desearan.
Esa es una relación profundamente asimétrica, y es una que deseamos tener en nuestros propios
sistemas. Queremos que ciertos módulos sean inmunes a otros. Por ejemplo, no queremos que las
reglas comerciales se rompan cuando alguien cambia el formato de una página web o cambia el
esquema de la base de datos. No queremos que los cambios en una parte del sistema provoquen que
otras partes no relacionadas del sistema se rompan. No queremos que nuestros sistemas muestren ese
tipo de fragilidad.
Los límites se dibujan donde hay un eje de cambio. Los componentes de un lado de la frontera cambian
a diferentes velocidades y por diferentes razones que los componentes del otro lado de la frontera.
Las GUI cambian en diferentes momentos y a diferentes ritmos que las reglas comerciales, por lo
que debe haber un límite entre ellas. Las reglas comerciales cambian en diferentes momentos y por
diferentes motivos que los marcos de inyección de dependencia, por lo que debe haber un límite entre
ellos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 173 173
Este es simplemente el Principio de Responsabilidad Única de nuevo. El SRP nos dice dónde trazar
nuestros límites.
CONCLUSIÓN
Para dibujar líneas de límite en una arquitectura de software, primero se divide el sistema en
componentes. Algunos de esos componentes son reglas comerciales básicas; otros son complementos
que contienen funciones necesarias que no están directamente relacionadas con el negocio principal.
Luego, organiza el código en esos componentes de manera que las flechas entre ellos apunten en
una dirección: hacia el negocio principal.
Debe reconocer esto como una aplicación del Principio de Inversión de Dependencia y el Principio
de Abstracción Estable. Las flechas de dependencia están dispuestas para señalar desde detalles de
nivel inferior a abstracciones de nivel superior.
1. La palabra “arquitectura” aparece entre comillas aquí porque la arquitectura de tres niveles no es una arquitectura;
es una topología. Es exactamente el tipo de decisión que una buena arquitectura se esfuerza por diferir.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 174 174
18
ANATOMÍA DE LÍMITE
CRUCE DE LÍMITES
En tiempo de ejecución, un cruce de límites no es más que una función en un lado del
límite que llama a una función en el otro lado y pasa algunos datos. El truco para crear un
cruce de límites apropiado es administrar las dependencias del código fuente.
¿Por qué código fuente? Porque cuando un módulo de código fuente cambia, otra fuente
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 175 175
Es posible que los módulos de código deban cambiarse o recompilarse y luego volver a implementarse.
Administrar y construir firewalls contra este cambio es de lo que se tratan los límites.
EL TEMIDO MONOLITO
El más simple y común de los límites arquitectónicos no tiene una representación física estricta. Es simplemente
una segregación disciplinada de funciones y datos dentro de un solo procesador y un solo espacio de direcciones.
En un capítulo anterior, llamé a esto el modo de desacoplamiento a nivel de fuente.
Desde el punto de vista de la implementación, esto equivale a nada más que un solo archivo ejecutable,
el llamado monolito. Este archivo puede ser un proyecto C o C++ enlazado estáticamente, un conjunto de
archivos de clase Java enlazados en un archivo jar ejecutable, un conjunto de archivos binarios .NET enlazados
en un único archivo .EXE , etc.
El hecho de que los límites no sean visibles durante el despliegue de un monolito no significa que no estén presentes
y significativos. Incluso cuando está vinculado estáticamente a un solo ejecutable, la capacidad de desarrollar y
ordenar de forma independiente los diversos componentes para el ensamblaje final es inmensamente valiosa.
Estas arquitecturas casi siempre dependen de algún tipo de polimorfismo1 dinámico para administrar sus
dependencias internas. Esta es una de las razones por las que el desarrollo orientado a objetos se ha convertido en
un paradigma tan importante en las últimas décadas. Sin OO, o una forma equivalente de polimorfismo, los
arquitectos deben recurrir a la peligrosa práctica de usar punteros a funciones para lograr el desacoplamiento
adecuado. La mayoría de los arquitectos consideran que el uso prolífico de punteros a funciones es demasiado
arriesgado, por lo que se ven obligados a abandonar cualquier tipo de partición de componentes.
El cruce de límites más simple posible es una llamada de función de un cliente de bajo nivel a un servicio de nivel
superior. Tanto la dependencia del tiempo de ejecución como la dependencia del tiempo de compilación apuntan
en la misma dirección, hacia el componente de nivel superior.
En la Figura 18.1, el flujo de control cruza el límite de izquierda a derecha. El Cliente llama a la función f()
en el Servicio. Pasa una instancia de Data. El marcador <DS> simplemente indica una estructura de datos. Los
datos se pueden pasar como un argumento de función o por algún otro medio más elaborado. Tenga en cuenta que
la definición de los datos está en el lado llamado del límite.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 176 176
Figura 18.1 El flujo de control cruza la frontera de un nivel inferior a un nivel superior
Cuando un cliente de alto nivel necesita invocar un servicio de nivel inferior, se usa
polimorfismo dinámico para invertir la dependencia contra el flujo de control. La dependencia
de tiempo de ejecución se opone a la dependencia de tiempo de compilación.
En la Figura 18.2, el flujo de control cruza el límite de izquierda a derecha como antes.
El Cliente de alto nivel llama a la función f() del ServiceImpl de nivel inferior a través de la interfaz de
Servicio . Tenga en cuenta, sin embargo, que todas las dependencias cruzan el límite de derecha a
izquierda hacia el componente de nivel superior. Tenga en cuenta, también, que la definición de la
estructura de datos está en el lado de llamada del límite.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 177 177
la partición puede ser de gran ayuda en el trabajo de desarrollo, prueba e implementación del proyecto.
Los equipos pueden trabajar independientemente unos de otros en sus propios componentes sin pisar
los dedos de los pies. Los componentes de alto nivel permanecen independientes de los detalles de nivel
inferior.
Las comunicaciones entre los componentes de un monolito son muy rápidas y económicas.
Por lo general, son solo llamadas a funciones. En consecuencia, las comunicaciones a través de los límites
desacoplados del nivel de fuente pueden ser muy comunicativas.
Dado que la implementación de monolitos generalmente requiere compilación y vinculación estática, los
componentes de estos sistemas generalmente se entregan como código fuente.
COMPONENTES DE IMPLEMENTACIÓN
La representación física más simple de un límite arquitectónico es una biblioteca enlazada dinámicamente
como una DLL de .Net, un archivo jar de Java, una gema de Ruby o una biblioteca compartida de UNIX. La
implementación no implica compilación. En su lugar, los componentes se entregan en formato binario o en
alguna forma implementable equivalente. Este es el modo de desacoplamiento de nivel de implementación.
El acto de implementación es simplemente la reunión de estas unidades desplegables en alguna forma
conveniente, como un archivo WAR, o incluso simplemente un directorio.
Con esa única excepción, los componentes de nivel de implementación son los mismos que los monolitos.
Por lo general, todas las funciones existen en el mismo procesador y espacio de direcciones. Las
estrategias para segregar los componentes y gestionar sus dependencias son las mismas.2
Al igual que con los monolitos, las comunicaciones a través de los límites de los componentes de
implementación son solo llamadas de función y, por lo tanto, son muy económicas. Puede haber un único golpe
para la vinculación dinámica o la carga en tiempo de ejecución, pero las comunicaciones a través de estos
límites aún pueden ser muy comunicativas.
HILOS
Tanto los monolitos como los componentes de implementación pueden hacer uso de subprocesos. Los
subprocesos no son límites arquitectónicos ni unidades de implementación, sino una forma de organizar la
programación y el orden de ejecución. Pueden estar completamente contenidos dentro de un componente o
repartidos entre muchos componentes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 178 178
PROCESOS LOCALES
Un límite arquitectónico físico mucho más fuerte es el proceso local. Un proceso local normalmente se
crea desde la línea de comando o una llamada al sistema equivalente. Los procesos locales se ejecutan
en el mismo procesador o en el mismo conjunto de procesadores dentro de un núcleo múltiple, pero se
ejecutan en espacios de direcciones separados. La protección de la memoria generalmente evita que
dichos procesos compartan la memoria, aunque a menudo se usan particiones de memoria compartida.
La mayoría de las veces, los procesos locales se comunican entre sí mediante sockets o algún otro
tipo de instalación de comunicaciones del sistema operativo, como buzones de correo o colas de
mensajes.
Cada proceso local puede ser un monolito vinculado estáticamente o puede estar compuesto por
componentes de implementación vinculados dinámicamente. En el primer caso, varios procesos
monolíticos pueden tener los mismos componentes compilados y vinculados a ellos. En este último,
pueden compartir los mismos componentes de implementación vinculados dinámicamente.
Piense en un proceso local como una especie de supercomponente: el proceso consta de componentes
de nivel inferior que administran sus dependencias a través del polimorfismo dinámico.
La estrategia de segregación entre procesos locales es la misma que para monolitos y componentes
binarios. Las dependencias del código fuente apuntan en la misma dirección a través del límite y siempre
hacia el componente de nivel superior.
Para los procesos locales, esto significa que el código fuente de los procesos de nivel superior no
debe contener los nombres, las direcciones físicas o las claves de búsqueda del registro de los procesos
de nivel inferior. Recuerde que el objetivo de la arquitectura es que los procesos de nivel inferior sean
complementos de los procesos de nivel superior.
La comunicación a través de los límites de los procesos locales implica llamadas al sistema operativo,
clasificación y decodificación de datos y cambios de contexto entre procesos, que son moderadamente
caros. La charlatanería debe limitarse cuidadosamente.
SERVICIOS
El límite más fuerte es un servicio. Un servicio es un proceso, generalmente iniciado desde la línea de
comandos o mediante una llamada al sistema equivalente. Los servicios no dependen de su ubicación
física. Dos servicios comunicantes pueden, o no, operar en el mismo
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 179 179
Procesador físico o multinúcleo. Los servicios asumen que todas las comunicaciones tienen lugar a través de la
red.
Las comunicaciones a través de los límites del servicio son muy lentas en comparación con las llamadas a funciones.
Los tiempos de respuesta pueden oscilar entre decenas de milisegundos y segundos. Se debe tener cuidado
para evitar chatear cuando sea posible. Las comunicaciones a este nivel deben lidiar con altos niveles de latencia.
De lo contrario, se aplican las mismas reglas a los servicios que a los procesos locales. Los servicios de nivel
inferior deben "conectarse" a los servicios de nivel superior. El código fuente de los servicios de nivel superior no
debe contener ningún conocimiento físico específico (por ejemplo, un URI) de ningún servicio de nivel inferior.
CONCLUSIÓN
La mayoría de los sistemas, aparte de los monolitos, utilizan más de una estrategia de límites. Un sistema que hace
uso de límites de servicios también puede tener algunos límites de procesos locales.
De hecho, un servicio es a menudo solo una fachada para un conjunto de procesos locales que interactúan.
Es casi seguro que un servicio, o un proceso local, será un monolito compuesto por componentes de código
fuente o un conjunto de componentes de implementación vinculados dinámicamente.
Esto significa que los límites de un sistema a menudo serán una mezcla de límites locales parlanchines y límites
más preocupados por la latencia.
1. El polimorfismo estático (p. ej., genéricos o plantillas) a veces puede ser un medio viable de
gestión de dependencias en sistemas monolíticos, especialmente en lenguajes como C++. Sin embargo, el
desacoplamiento que brindan los genéricos no puede protegerlo de la necesidad de volver a compilar y
redistribuir la forma en que lo hace el polimorfismo dinámico.
2. Aunque el polimorfismo estático no es una opción en este caso.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 180 180
19
POLÍTICA Y NIVEL
Los sistemas de software son declaraciones de política. De hecho, en esencia, eso es todo lo que realmente
es un programa de computadora. Un programa de computadora es una descripción detallada de la política
mediante la cual las entradas se transforman en salidas.
En la mayoría de los sistemas no triviales, esa política se puede dividir en muchas declaraciones de
política más pequeñas. Algunas de esas declaraciones describirán cómo se calcularán las reglas comerciales
particulares. Otros describirán cómo deben formatearse ciertos informes. Aún otros describirán cómo se
validarán los datos de entrada.
Parte del arte de desarrollar una arquitectura de software es separar cuidadosamente esas políticas entre
sí y reagruparlas en función de las formas en que cambian.
Las políticas que cambian por las mismas razones y en los mismos momentos, están en el mismo nivel
y pertenecen juntas en el mismo componente. Políticas que cambian para diferentes
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 181 181
Esas dependencias son código fuente, dependencias en tiempo de compilación. En Java, son
sentencias de importación . En C#, están usando declaraciones. En Ruby, son declaraciones de
requisitos . Son las dependencias que son necesarias para que el compilador funcione.
NIVEL
Una definición estricta de "nivel" es "la distancia de las entradas y salidas". Cuanto más lejos
esté una política tanto de las entradas como de las salidas del sistema, mayor será su nivel. Las
políticas que administran la entrada y la salida son las políticas de nivel más bajo del sistema.
El diagrama de flujo de datos de la figura 19.1 muestra un programa de cifrado simple que lee los
caracteres de un dispositivo de entrada, los traduce mediante una tabla y luego escribe los
caracteres traducidos en un dispositivo de salida. Los flujos de datos se muestran como flechas
continuas curvas. Las dependencias del código fuente correctamente diseñadas se muestran como
líneas discontinuas rectas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 182 182
El componente Traducir es el componente de más alto nivel en este sistema porque es el componente
que está más alejado de las entradas y salidas.1
Tenga en cuenta que los flujos de datos y las dependencias del código fuente no siempre apuntan en
la misma dirección. Esto, nuevamente, es parte del arte de la arquitectura de software. Queremos que
las dependencias del código fuente se desacoplen del flujo de datos y se acoplen al nivel.
Sería fácil crear una arquitectura incorrecta escribiendo el programa de encriptación de esta
manera:
Esta es una arquitectura incorrecta porque la función de cifrado de alto nivel depende de las funciones
readChar y writeChar de nivel inferior .
En el diagrama de clases de la figura 19.2 se muestra una mejor arquitectura para este sistema .
Tenga en cuenta el borde discontinuo que rodea la clase Encrypt y las interfaces CharWriter y
CharReader . Todas las dependencias que cruzan esa frontera apuntan hacia adentro. Esta unidad es
el elemento de más alto nivel en el sistema.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 183 183
Figura 19.2 Diagrama de clases que muestra una mejor arquitectura para el sistema
Observe cómo esta estructura desacopla la política de cifrado de alto nivel de las políticas de
entrada/salida de nivel inferior. Esto hace que la política de cifrado se pueda utilizar en una amplia
gama de contextos. Cuando se realizan cambios en las políticas de entrada y salida, no es probable
que afecten la política de cifrado.
Recuerde que las políticas se agrupan en componentes según la forma en que cambian.
Las pólizas que cambian por las mismas razones y en los mismos momentos son agrupadas por
SRP y CCP. Las políticas de nivel superior, las que están más alejadas de los insumos y productos,
tienden a cambiar con menos frecuencia y por razones más importantes que las políticas de nivel
inferior. Las políticas de nivel inferior, las que están más cerca de los insumos y productos, tienden
a cambiar con frecuencia y con mayor urgencia, pero por razones menos importantes.
razones.
Por ejemplo, incluso en el ejemplo trivial del programa de cifrado, es mucho más probable que
los dispositivos IO cambien a que cambie el algoritmo de cifrado.
Si el algoritmo de cifrado cambia, es probable que sea por una razón más importante que un
cambio en uno de los dispositivos IO.
Mantener estas políticas separadas, con todas las dependencias del código fuente apuntando en
la dirección de las políticas de nivel superior, reduce el impacto del cambio. Los cambios triviales
pero urgentes en los niveles más bajos del sistema tienen poco o ningún impacto en los niveles
más altos y más importantes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 184 184
Otra forma de ver este problema es tener en cuenta que los componentes de nivel inferior
deben ser complementos de los componentes de nivel superior. El diagrama de
componentes de la figura 19.3 muestra este arreglo. El componente Encryption no sabe
nada del componente IODevices ; el componente IODevices depende del componente de
cifrado .
Figura 19.3 Los componentes de nivel inferior deben conectarse a los componentes de nivel superior
CONCLUSIÓN
En este punto, esta discusión de políticas ha involucrado una mezcla del Principio
de responsabilidad única, el Principio abierto-cerrado, el Principio de cierre común, el
Principio de inversión de dependencia, el Principio de dependencias estables y el Principio
de abstracciones estables. Mire hacia atrás y vea si puede identificar dónde se usó cada
principio y por qué.
1. Meilir Page-Jones llamó a este componente la "Transformación central" en su libro La guía práctica para el diseño
de sistemas estructurados, 2ª ed. (Yourdon Press, 1988).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 185 185
20
REGLAS DE NEGOCIO
Si vamos a dividir nuestra aplicación en reglas comerciales y complementos, será mejor que
comprendamos bien qué son realmente las reglas comerciales. Resulta que hay varios tipos
diferentes.
Estrictamente hablando, las reglas comerciales son reglas o procedimientos que generan
o ahorran dinero a la empresa. En términos muy estrictos, estas reglas generarían o ahorrarían
dinero a la empresa, independientemente de si se implementaron en una computadora. Ganarían
o ahorrarían dinero incluso si se ejecutaran manualmente.
El hecho de que un banco cobre un interés del N% por un préstamo es una regla comercial que
hace que el banco gane dinero. No importa si un programa de computadora calcula el interés,
o si un empleado con un ábaco calcula el interés.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 186 186
Llamaremos a estas reglas Reglas Comerciales Críticas, porque son críticas para el negocio en sí
mismo, y existirían incluso si no hubiera un sistema para automatizarlas.
Las reglas comerciales críticas generalmente requieren algunos datos para trabajar. Por ejemplo, nuestro
préstamo requiere un saldo del préstamo, una tasa de interés y un calendario de pagos.
A estos datos los llamaremos Datos comerciales críticos. Estos son los datos que existirían incluso si el
sistema no estuviera automatizado.
Las reglas críticas y los datos críticos están inextricablemente unidos, por lo que son un buen
candidato para un objeto. Llamaremos a este tipo de objeto una Entidad. 1
ENTIDADES
Una Entidad es un objeto dentro de nuestro sistema informático que incorpora un pequeño conjunto
de reglas comerciales críticas que operan en Datos comerciales críticos. El objeto Entidad contiene los
datos comerciales críticos o tiene un acceso muy fácil a esos datos. La interfaz de la Entidad está compuesta
por las funciones que implementan las Reglas Críticas de Negocio que operan sobre esos datos.
Por ejemplo, la figura 20.1 muestra cómo se vería nuestra entidad Préstamo como una clase en UML.
Tiene tres piezas de datos comerciales críticos y presenta tres reglas comerciales críticas relacionadas en su
interfaz.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 187 187
Cuando creamos este tipo de clase, reunimos el software que implementa un concepto que es crítico
para el negocio y lo separamos de cualquier otra preocupación en el sistema automatizado que estamos
construyendo. Esta clase está sola como representante del negocio. No tiene preocupaciones sobre bases de
datos, interfaces de usuario o marcos de terceros. Podría servir al negocio en cualquier sistema,
independientemente de cómo se presentara ese sistema, o cómo se almacenaran los datos, o cómo se
organizaran las computadoras en ese sistema. La Entidad es puro negocio y nada más.
Algunos de ustedes pueden estar preocupados porque lo llamé una clase. no seas No necesita usar un
lenguaje orientado a objetos para crear una Entidad. Todo lo que se requiere es que vincule los Datos
comerciales críticos y las Reglas comerciales críticas en un módulo de software único e independiente.
CASOS DE USO
No todas las reglas de negocio son tan puras como las Entidades. Algunas reglas comerciales generan
o ahorran dinero para el negocio al definir y restringir la forma en que opera un sistema automatizado .
Estas reglas no se usarían en un entorno manual, porque solo tienen sentido como parte de un sistema
automatizado.
Por ejemplo, imagine una aplicación que utilizan los funcionarios bancarios para crear un nuevo préstamo.
El banco puede decidir que no quiere que los oficiales de crédito ofrezcan estimaciones de pago del
préstamo hasta que primero hayan recopilado y validado la información de contacto y se hayan asegurado de
que el puntaje crediticio del candidato sea de 500 o más. Por esta razón, el banco puede especificar que el
sistema no pasará a la pantalla de estimación de pago hasta que se haya llenado y verificado la pantalla de
información de contacto, y se haya confirmado que el puntaje de crédito es mayor que el límite.
Este es un caso de uso.2 Un caso de uso es una descripción de la forma en que se utiliza un sistema
automatizado. Especifica la entrada que debe proporcionar el usuario, la salida que se devolverá al usuario y
los pasos de procesamiento involucrados en la producción de esa salida. Un caso de uso describe las reglas
comerciales específicas de la aplicación en lugar de las Reglas comerciales críticas dentro de las Entidades.
La Figura 20.2 muestra un ejemplo de un caso de uso. Observe que en la última línea menciona al Cliente.
Esta es una referencia a la entidad Cliente, que contiene las Reglas Comerciales Críticas que rigen la relación
entre el banco y sus clientes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 188 188
Los casos de uso contienen las reglas que especifican cómo y cuándo se invocan las reglas comerciales
críticas dentro de las entidades. Los casos de uso controlan el baile de las Entidades.
Tenga en cuenta también que el caso de uso no describe la interfaz de usuario más que para especificar
informalmente los datos que entran desde esa interfaz y los datos que salen a través de esa interfaz. A partir
del caso de uso, es imposible saber si la aplicación se entrega en la web, en un cliente pesado, en una consola
o si es un servicio puro.
Esto es muy importante. Los casos de uso no describen cómo aparece el sistema para el usuario.
En su lugar, describen las reglas específicas de la aplicación que rigen la interacción entre los usuarios y
las Entidades. La forma en que los datos entran y salen del sistema es irrelevante para los casos de uso.
Un caso de uso es un objeto. Tiene una o más funciones que implementan las reglas comerciales específicas
de la aplicación. También tiene elementos de datos que incluyen los datos de entrada, los datos de salida y las
referencias a las Entidades apropiadas con las que interactúa.
Las entidades no tienen conocimiento de los casos de uso que las controlan. Este es otro ejemplo de la
dirección de las dependencias siguiendo el Principio de Inversión de Dependencia. Los conceptos de alto nivel,
como las Entidades, no saben nada de los conceptos de nivel inferior, como los casos de uso. En cambio, los
casos de uso de nivel inferior conocen las Entidades de nivel superior.
¿Por qué las Entidades son de alto nivel y los casos de uso de bajo nivel? Porque los casos de uso son específicos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 189 189
a una sola aplicación y, por lo tanto, están más cerca de las entradas y salidas de ese sistema. Las
entidades son generalizaciones que se pueden usar en muchas aplicaciones diferentes, por lo que están más
alejadas de las entradas y salidas del sistema. Los casos de uso dependen de las Entidades; Las entidades no
dependen de los casos de uso.
La clase de caso de uso acepta estructuras de datos de solicitud simples como entrada y devuelve estructuras
de datos de respuesta simples como salida. Estas estructuras de datos no dependen de nada. No se derivan de
interfaces de marco estándar como HttpRequest y HttpResponse. No saben nada de la web, ni comparten
ninguna de las trampas de cualquier interfaz de usuario que pueda existir.
Esta falta de dependencias es fundamental. Si los modelos de solicitud y respuesta no son independientes,
los casos de uso que dependen de ellos estarán vinculados indirectamente a las dependencias que los
modelos lleven consigo.
Es posible que tenga la tentación de que estas estructuras de datos contengan referencias a objetos Entity.
Podría pensar que esto tiene sentido porque las Entidades y los modelos de solicitud/respuesta comparten
muchos datos. ¡Evita esta tentación! El propósito de estos dos objetos es muy diferente. Con el tiempo, cambiarán
por razones muy diferentes, por lo que vincularlos de alguna manera viola los principios de cierre común y
responsabilidad única. El resultado sería una gran cantidad de datos de trampa y muchos condicionales en su
código.
CONCLUSIÓN
Las reglas de negocio son la razón por la que existe un sistema de software. Son la funcionalidad
principal. Llevan el código que genera o ahorra dinero. Son las joyas de la familia.
Las reglas comerciales deben permanecer prístinas, sin contaminarse por preocupaciones más básicas, como
la interfaz de usuario o la base de datos utilizada. Idealmente, el código que representa las reglas de negocio
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 190 190
debe ser el corazón del sistema, con preocupaciones menores conectadas a ellos. Las
reglas de negocio deben ser el código más independiente y reutilizable del sistema.
1. Este es el nombre de Ivar Jacobson para este concepto (I. Jacobson et al., Object Oriented Software
Ingeniería, Addison-Wesley, 1992).
2. Ibíd.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 191 191
21
ARQUITECTURA QUE GRITA
Imagina que estás mirando los planos de un edificio. Este documento, elaborado por un arquitecto,
proporciona los planos del edificio. ¿Qué te dicen estos planes?
Si los planos que está viendo son para una residencia unifamiliar, es probable que vea una entrada
principal, un vestíbulo que conduce a una sala de estar y tal vez a un comedor. Es probable que
haya una cocina a poca distancia, cerca del comedor. Tal vez haya un área de comedor al lado de
la cocina, y probablemente una sala familiar cerca de eso. Cuando miraste esos planos, no habría
duda de que estabas mirando una casa unifamiliar. La arquitectura gritaría: “HOGAR”.
Ahora suponga que está mirando la arquitectura de una biblioteca. Es probable que vea una gran
entrada, un área para los empleados de registro de entrada/salida, áreas de lectura, pequeñas
salas de conferencias y galería tras galería capaces de albergar estantes para todos los libros en
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 192 192
Las arquitecturas no son (o no deberían ser) sobre marcos. Las arquitecturas no deben ser
proporcionadas por marcos. Los marcos son herramientas para usar, no arquitecturas para
conformarse. Si su arquitectura se basa en marcos, entonces no puede basarse en sus casos de uso.
Una buena arquitectura de software permite aplazar y retrasar las decisiones sobre marcos, bases
de datos, servidores web y otras cuestiones y herramientas ambientales.
Los marcos son opciones que se dejan abiertas. Una buena arquitectura hace innecesario decidirse
por Rails, Spring, Hibernate, Tomcat o MySQL, hasta mucho más tarde en el proyecto. Una buena
arquitectura también facilita el cambio de opinión sobre esas decisiones. Una buena arquitectura
enfatiza los casos de uso y los desvincula de las preocupaciones periféricas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 193 193
PERO, ¿Y LA WEB?
¿Es la web una arquitectura? ¿El hecho de que su sistema se entregue en la web dicta la
arquitectura de su sistema? ¡Por supuesto que no! La web es un mecanismo de entrega, un
dispositivo IO, y la arquitectura de su aplicación debe tratarlo como tal.
El hecho de que su aplicación se entregue a través de la web es un detalle y no debe dominar
la estructura de su sistema. De hecho, la decisión de que su solicitud se entregará a través de
la web es algo que debe aplazar. La arquitectura de su sistema debe ser lo más ignorante posible
acerca de cómo se entregará. Debería poder entregarlo como una aplicación de consola, una
aplicación web, una aplicación de cliente pesado, o incluso una aplicación de servicio web, sin
complicaciones indebidas o cambios en la arquitectura fundamental.
Mire cada marco con ojo hastiado. Míralo con escepticismo. Sí, podría ayudar, pero ¿a qué costo?
Pregúntese cómo debe usarlo y cómo debe protegerse de él. Piense en cómo puede preservar el
énfasis en el caso de uso de su arquitectura. Desarrolle una estrategia que evite que el marco se
apodere de esa arquitectura.
ARQUITECTURAS COMPROBABLES
Si la arquitectura de su sistema tiene que ver con los casos de uso, y si ha mantenido sus
marcos a distancia, entonces debería poder realizar pruebas unitarias de todos esos casos de
uso sin ninguno de los marcos en su lugar. No debería necesitar el servidor web en ejecución para
ejecutar sus pruebas. No debería necesitar la base de datos conectada para ejecutar sus pruebas.
Sus objetos de Entidad deben ser objetos simples y antiguos que no tengan dependencias en los marcos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 194 194
o bases de datos u otras complicaciones. Sus objetos de caso de uso deben coordinar sus
objetos de Entidad. Finalmente, todos juntos deben ser comprobables in situ, sin ninguna de las
complicaciones de los marcos.
CONCLUSIÓN
Su arquitectura debe informar a los lectores sobre el sistema, no sobre los marcos que utilizó en
su sistema. Si está construyendo un sistema de atención médica, cuando los nuevos
programadores miren el repositorio de origen, su primera impresión debería ser: "Oh, este es un
sistema de atención médica". Esos nuevos programadores deberían poder aprender todos los
casos de uso del sistema, pero aún no saber cómo se entrega el sistema. Pueden venir a ti y
decirte:
“Vemos algunas cosas que parecen modelos, pero ¿dónde están las vistas y los
controladores?”
Y debes responder:
“Oh, esos son detalles que no deben preocuparnos en este momento. Decidiremos sobre
ellos más tarde.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 195 195
22
LA ARQUITECTURA LIMPIA
En las últimas décadas hemos visto una amplia gama de ideas con respecto a la arquitectura
de los sistemas. Éstos incluyen:
• Arquitectura Hexagonal (también conocida como Puertos y Adaptadores), desarrollada por Alistair
Cockburn, y adoptado por Steve Freeman y Nat Pryce en su maravilloso libro
Desarrollo de software orientado a objetos con
pruebas • DCI de James Coplien y Trygve Reenskaug •
BCE, presentado por Ivar Jacobson de su libro Software orientado a objetos
Ingeniería: un enfoque basado en casos de uso
Aunque todas estas arquitecturas varían algo en sus detalles, son muy similares.
Todos tienen un mismo objetivo, que es la separación de preocupaciones. Todos ellos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 196 196
lograr esta separación dividiendo el software en capas. Cada uno tiene al menos una capa para
reglas comerciales y otra capa para interfaces de usuario y sistema.
Cada una de estas arquitecturas produce sistemas que tienen las siguientes características:
• Independiente de la base de datos. Puede cambiar Oracle o SQL Server por Mongo, BigTable,
CouchDB o cualquier otra cosa. Sus reglas comerciales no están vinculadas a la base de datos.
El diagrama de la figura 22.1 es un intento de integrar todas estas arquitecturas en una sola idea
procesable.
LA REGLA DE DEPENDENCIA
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 197 197
La regla primordial que hace que esta arquitectura funcione es la regla de dependencia:
Las dependencias del código fuente deben apuntar solo hacia adentro, hacia políticas de nivel superior.
Nada en un círculo interior puede saber absolutamente nada sobre algo en un círculo exterior.
En particular, el nombre de algo declarado en un círculo exterior no debe ser mencionado por el
código en un círculo interior. Eso incluye funciones, clases, variables o cualquier otra entidad de
software con nombre.
Del mismo modo, los formatos de datos declarados en un círculo exterior no deben ser utilizados por
un círculo interior, especialmente si esos formatos son generados por un marco en un círculo exterior.
No queremos que nada en un círculo exterior impacte en los círculos interiores.
ENTIDADES
Las entidades encapsulan las reglas comerciales críticas de toda la empresa. Una entidad puede
ser un objeto con métodos o puede ser un conjunto de funciones y estructuras de datos. No
importa, siempre que las entidades puedan ser utilizadas por muchas aplicaciones diferentes en
la empresa.
Si no tiene una empresa y está escribiendo una sola aplicación, estas entidades son los objetos
comerciales de la aplicación. Encapsulan las reglas más generales y de alto nivel. Son los menos
propensos a cambiar cuando algo externo cambia. Por ejemplo, no esperaría que estos objetos se
vieran afectados por un cambio en la navegación o la seguridad de la página. Ningún cambio operativo
en ninguna aplicación en particular debería afectar la capa de entidad.
CASOS DE USO
No esperamos que los cambios en esta capa afecten a las entidades. Tampoco esperamos que esta
capa se vea afectada por cambios en externalidades como la base de datos, la interfaz de usuario o
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 198 198
cualquiera de los marcos comunes. La capa de casos de uso está aislada de tales preocupaciones.
Sin embargo, esperamos que los cambios en el funcionamiento de la aplicación afecten los casos
de uso y, por lo tanto, el software en esta capa. Si los detalles de un caso de uso cambian,
entonces parte del código de esta capa se verá afectado.
ADAPTADORES DE INTERFAZ
De manera similar, los datos se convierten, en esta capa, de la forma más conveniente para las
entidades y los casos de uso, a la forma más conveniente para cualquier marco de persistencia
que se esté utilizando (es decir, la base de datos). Ningún código dentro de este círculo debería
saber nada sobre la base de datos. Si la base de datos es una base de datos SQL, entonces todo el
SQL debe restringirse a esta capa y, en particular, a las partes de esta capa que tienen que ver con
la base de datos.
También en esta capa hay cualquier otro adaptador necesario para convertir datos de algún
formato externo, como un servicio externo, al formato interno utilizado por los casos de uso y las
entidades.
MARCOS Y CONDUCTORES
La capa de marcos y controladores es donde van todos los detalles. La web es un detalle. La base
de datos es un detalle. Mantenemos estas cosas en el exterior donde pueden hacer poco daño.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 199 199
Los círculos de la figura 22.1 pretenden ser esquemáticos: es posible que necesite más que estos
cuatro. No hay una regla que diga que siempre debes tener solo estos cuatro. Sin embargo, siempre
se aplica la regla de dependencia. Las dependencias del código fuente siempre apuntan hacia
adentro. A medida que avanza hacia adentro, aumenta el nivel de abstracción y política. El círculo
exterior consta de detalles de hormigón de bajo nivel. A medida que avanza hacia adentro, el software
se vuelve más abstracto y encapsula políticas de nivel superior. El círculo más interno es el nivel más
general y más alto.
CRUZANDO LÍMITES
En la parte inferior derecha del diagrama de la Figura 22.1 hay un ejemplo de cómo cruzamos los
límites del círculo. Muestra a los controladores y presentadores comunicándose con los casos de
uso en la siguiente capa. Tenga en cuenta el flujo de control: comienza en el controlador, se mueve
a través del caso de uso y luego termina ejecutándose en el presentador. Tenga en cuenta también
las dependencias del código fuente: cada uno apunta hacia los casos de uso.
Por ejemplo, suponga que el caso de uso necesita llamar al presentador. Esta llamada no debe ser
directa porque violaría la regla de dependencia: ningún nombre en un círculo externo puede ser
mencionado por un círculo interno. Entonces, tenemos el caso de uso que llama a una interfaz (que
se muestra en la Figura 22.1 como "puerto de salida del caso de uso") en el círculo interior, y el
presentador en el círculo exterior lo implementa.
La misma técnica se utiliza para cruzar todos los límites en las arquitecturas. Aprovechamos el
polimorfismo dinámico para crear dependencias de código fuente que se oponen al flujo de control
para que podamos ajustarnos a la regla de dependencia, sin importar en qué dirección viaja el flujo de
control.
Normalmente, los datos que cruzan los límites consisten en estructuras de datos simples. Puede usar
estructuras básicas u objetos simples de transferencia de datos si lo desea. O los datos pueden ser
simplemente argumentos en llamadas a funciones. O puede empaquetarlo en un hashmap o construirlo
en un objeto. Lo importante es que las estructuras de datos simples y aisladas se transmiten a través
de los límites. No queremos hacer trampa y pasar objetos de Entidad o
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 200 200
filas de la base de datos. No queremos que las estructuras de datos tengan ningún tipo de
dependencia que viole la regla de dependencia.
Por ejemplo, muchos marcos de bases de datos devuelven un formato de datos conveniente en
respuesta a una consulta. Podríamos llamar a esto una "estructura de fila". No queremos pasar esa
estructura de filas hacia adentro a través de un límite. Hacerlo violaría la regla de dependencia porque
obligaría a un círculo interno a saber algo sobre un círculo externo.
Por lo tanto, cuando pasamos datos a través de un límite, siempre es en la forma más
conveniente para el círculo interior.
UN ESCENARIO TÍPICO
El diagrama de la figura 22.2 muestra un escenario típico para un sistema Java basado en web
que utiliza una base de datos. El servidor web recopila datos de entrada del usuario y se los entrega
al Controlador en la parte superior izquierda. El controlador empaqueta esos datos en un objeto
Java simple y antiguo y pasa este objeto a través de InputBoundary a UseCaseInteractor. El
UseCaseInteractor interpreta esos datos y los usa para controlar el baile de las Entidades. También
utiliza la interfaz de acceso a datos para llevar los datos utilizados por esas entidades a la memoria
desde la base de datos. Al finalizar, UseCaseInteractor recopila datos de las Entidades y construye
OutputData como otro objeto Java simple y antiguo. Luego, los datos de salida se pasan a través de
la interfaz OutputBoundary al presentador.
Figura 22.2 Un escenario típico para un sistema Java basado en web que utiliza una base de datos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 201 201
El trabajo del presentador es volver a empaquetar los datos de salida en un formato visible como el modelo
de vista, que es otro objeto simple de Java. El modelo de vista contiene principalmente cadenas y banderas
que usa la vista para mostrar los datos. Mientras que los datos de salida pueden contener objetos de
fecha , el presentador cargará el modelo de vista con las cadenas correspondientes ya formateadas
correctamente para el usuario. Lo mismo se aplica a los objetos de moneda o cualquier otro dato
relacionado con el negocio. Los nombres de Button y MenuItem se colocan en ViewModel, al igual que las
banderas que le indican a View si esos Buttons y MenuItems deben ser grises.
Esto deja a View con casi nada que hacer más que mover los datos de ViewModel a la página HTML .
Tenga en cuenta las direcciones de las dependencias. Todas las dependencias cruzan las líneas de límite
que apuntan hacia adentro, siguiendo la regla de dependencia.
CONCLUSIÓN
Cumplir con estas reglas simples no es difícil y le ahorrará muchos dolores de cabeza en el futuro.
Al separar el software en capas y cumplir con la regla de dependencia, creará un sistema que es
intrínsecamente comprobable, con todos los beneficios que ello implica. Cuando alguna de las partes
externas del sistema se vuelve obsoleta, como la base de datos o el marco web, puede reemplazar esos
elementos obsoletos con un mínimo de esfuerzo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 202 202
23
PRESENTADORES Y OBJETOS HUMILDES
En el Capítulo 22, presentamos la noción de presentadores. Los presentadores son una forma del
patrón Humble Object , que nos ayuda a identificar y proteger los límites arquitectónicos.
En realidad, la arquitectura limpia del último capítulo estaba llena de implementaciones de
Humble Object .
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 203 203
Por ejemplo, las GUI son difíciles de realizar pruebas unitarias porque es muy difícil escribir
pruebas que puedan ver la pantalla y verificar que los elementos apropiados se muestren allí.
Sin embargo, la mayor parte del comportamiento de una GUI es, de hecho, fácil de probar.
Usando el patrón Humble Object , podemos separar estos dos tipos de comportamientos en dos
clases diferentes llamadas Presenter y View.
PRESENTADORES Y OPINIONES
La Vista es el objeto humilde que es difícil de probar. El código de este objeto se mantiene lo más
simple posible. Mueve datos a la GUI pero no procesa esos datos.
Cada botón en la pantalla tendrá un nombre. Ese nombre será una cadena en el modelo de vista,
colocado allí por el presentador. Si esos botones deben estar atenuados, el presentador establecerá
una bandera booleana adecuada en el modelo de vista. Cada nombre de elemento de menú es
una cadena en el modelo de vista, cargada por el presentador. El presentador carga los nombres
de cada botón de opción, casilla de verificación y campo de texto en cadenas y valores booleanos
apropiados en el modelo de vista. El presentador carga las tablas de números que deben mostrarse
en la pantalla en tablas de cadenas con el formato adecuado en el modelo de vista.
Todo lo que aparece en la pantalla, y sobre lo que la aplicación tiene algún tipo de control, se
representa en el modelo de vista como una cadena, un booleano o una enumeración. No le queda
nada más que hacer a la vista que cargar los datos del modelo de vista en la pantalla. Así la Vista
es humilde.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 204 204
PRUEBAS Y ARQUITECTURA
Durante mucho tiempo se ha sabido que la capacidad de prueba es un atributo de las buenas
arquitecturas. El patrón Humble Object es un buen ejemplo, porque la separación de los comportamientos
en partes comprobables y no comprobables a menudo define un límite arquitectónico. El límite
Presentador/Vista es uno de estos límites, pero hay muchos otros.
Recuerde que no permitimos SQL en la capa de casos de uso; en su lugar, usamos interfaces de
puerta de enlace que tienen métodos apropiados. Esas puertas de enlace son implementadas por
clases en la capa de la base de datos. Esa implementación es el objeto humilde. Simplemente utiliza
SQL, o cualquiera que sea la interfaz de la base de datos, para acceder a los datos requeridos por cada uno
de los métodos. Los interactuadores, por el contrario, no son humildes porque encapsulan reglas comerciales
específicas de la aplicación. Aunque no son humildes, esos interactores se pueden probar, porque las
puertas de enlace se pueden reemplazar con stubs y test-doubles apropiados.
MAPEADORES DE DATOS
Volviendo al tema de las bases de datos, ¿a qué capa crees que pertenecen los ORM como
Hibernate?
Primero, aclaremos algo: no existe un mapeador relacional de objetos (ORM). La razón es simple:
los objetos no son estructuras de datos. Al menos, no son estructuras de datos desde el punto de vista de
sus usuarios. Los usuarios de un objeto no pueden ver los datos, ya que todos son privados. Esos usuarios
solo ven los métodos públicos de ese objeto. Entonces, desde el punto de vista del usuario, un objeto es
simplemente un conjunto de operaciones.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 205 205
Una estructura de datos, por el contrario, es un conjunto de variables de datos públicos que no tienen un
comportamiento implícito. Los ORM se llamarían mejor "mapeadores de datos", porque cargan datos en
estructuras de datos desde tablas de bases de datos relacionales.
¿Dónde deberían residir dichos sistemas ORM? En la capa de base de datos, por supuesto. De hecho,
los ORM forman otro tipo de límite de objeto humilde entre las interfaces de la puerta de enlace y la base
de datos.
OYENTES DE SERVICIO
¿Qué pasa con los servicios? Si su aplicación debe comunicarse con otros servicios, o si su aplicación
proporciona un conjunto de servicios, ¿encontraremos el patrón Humble Object creando un límite de servicio?
¡Por supuesto! La aplicación cargará datos en estructuras de datos simples y luego pasará esas estructuras
a través del límite a módulos que formatean correctamente los datos y los envían a servicios externos. En
el lado de la entrada, los oyentes del servicio recibirán datos de la interfaz del servicio y los formatearán en
una estructura de datos simple que la aplicación puede usar. Esa estructura de datos luego se pasa a través
del límite del servicio.
CONCLUSIÓN
En cada límite arquitectónico, es probable que encontremos el patrón del Objeto Humilde al acecho en
algún lugar cercano. La comunicación a través de ese límite casi siempre implicará algún tipo de estructura
de datos simple, y el límite con frecuencia dividirá algo que es difícil de probar de algo que es fácil de probar.
El uso de este patrón en los límites arquitectónicos aumenta enormemente la capacidad de prueba de todo el
sistema.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 206 206
24
LÍMITES PARCIALES
En muchas situaciones, un buen arquitecto podría juzgar que el costo de dicho límite es demasiado
alto, pero aún así podría querer reservar un lugar para dicho límite en caso de que se necesite más
adelante.
Este tipo de diseño anticipatorio a menudo está mal visto por muchos en la comunidad
Agile como una violación de YAGNI: "No lo vas a necesitar". Los arquitectos, sin embargo, a
veces miran el problema y piensan: "Sí, pero podría". En ese caso,
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 207 207
Obviamente, este tipo de límite parcial requiere la misma cantidad de código y trabajo de
diseño preparatorio que un límite completo. Sin embargo, no requiere la administración
de múltiples componentes. No hay seguimiento del número de versión ni carga de
administración de versiones. Esa diferencia no debe tomarse a la ligera.
Esta fue la estrategia inicial detrás de FitNesse. El componente del servidor web de
FitNesse fue diseñado para ser separable de la wiki y parte de prueba de FitNesse.
La idea era que podríamos querer crear otras aplicaciones basadas en web utilizando ese
componente web. Al mismo tiempo, no queríamos que los usuarios tuvieran que descargar
dos componentes. Recuerde que uno de nuestros objetivos de diseño era "descargar y listo".
Nuestra intención era que los usuarios descargaran un archivo jar y lo ejecutaran sin tener que
buscar otros archivos jar, resolver compatibilidades de versiones, etc.
La historia de FitNesse también señala uno de los peligros de este enfoque. Con el tiempo,
cuando quedó claro que nunca sería necesario un componente web separado, la separación
entre el componente web y el componente wiki comenzó a debilitarse. Las dependencias
comenzaron a cruzar la línea en la dirección equivocada. Hoy en día, sería una especie de tarea
volver a separarlos.
LÍMITES UNIDIMENSIONALES
El límite arquitectónico completo utiliza interfaces de límite recíprocas para mantener el
aislamiento en ambas direcciones. Mantener la separación en ambas direcciones es costoso
tanto en la configuración inicial como en el mantenimiento continuo.
En la Figura 24.1 se muestra una estructura más simple que sirve para mantener el lugar para
su posterior extensión a un límite completo . Ejemplifica el patrón de estrategia tradicional. Los
clientes utilizan una interfaz ServiceBoundary y ServiceImpl la implementa.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 208 208
clases
Debe quedar claro que esto prepara el escenario para un futuro límite arquitectónico. La
inversión de dependencia necesaria está en su lugar en un intento de aislar el Cliente del
ServiceImpl. También debe quedar claro que la separación puede degradarse con bastante
rapidez, como lo muestra la desagradable flecha punteada en el diagrama. Sin interfaces
recíprocas, nada impide este tipo de backchannel más que la diligencia y la disciplina de los
desarrolladores y arquitectos.
FACHADAS
Un límite aún más simple es el patrón Fachada , ilustrado en la Figura 24.2. En este caso,
incluso se sacrifica la inversión de dependencia. El límite lo define simplemente la clase
Facade , que enumera todos los servicios como métodos y despliega las llamadas de servicio
a las clases a las que se supone que el cliente no debe acceder.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 209 209
Tenga en cuenta, sin embargo, que el Cliente tiene una dependencia transitiva en todas
esas clases de servicio. En lenguajes estáticos, un cambio en el código fuente en una de las clases
de Servicio obligará al Cliente a recompilar. Además, puedes imaginar lo fácil que es crear
backchannels con esta estructura.
CONCLUSIÓN
Hemos visto tres formas sencillas de implementar parcialmente un límite arquitectónico.
Hay, por supuesto, muchos otros. Estas tres estrategias se ofrecen simplemente como
ejemplos.
Cada uno de estos enfoques tiene su propio conjunto de costos y beneficios. Cada uno es
apropiado, en ciertos contextos, como marcador de posición para un eventual límite completo.
Cada uno también puede degradarse si ese límite nunca se materializa.
Una de las funciones de un arquitecto es decidir dónde podría existir algún día un límite
arquitectónico, y si implementar total o parcialmente ese límite.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 210 210
25
CAPAS Y LÍMITES
Es fácil pensar que los sistemas están compuestos por tres componentes: interfaz de usuario, reglas
comerciales y base de datos. Para algunos sistemas simples, esto es suficiente. Sin embargo, para la
mayoría de los sistemas, la cantidad de componentes es mayor que eso.
Considere, por ejemplo, un simple juego de computadora. Es fácil imaginar los tres componentes.
La interfaz de usuario maneja todos los mensajes del jugador a las reglas del juego. Las reglas del
juego almacenan el estado del juego en algún tipo de estructura de datos persistente. ¿Pero es eso todo
lo que hay?
CAZAR EL WUMPUS
Pongamos un poco de carne en estos huesos. Supongamos que el juego es el venerable
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 211 211
Caza el juego de aventuras Wumpus de 1972. Este juego basado en texto usa comandos muy
simples como GO EAST y SHOOT WEST. El jugador ingresa un comando y la computadora
responde con lo que el jugador ve, huele, escucha y experimenta. El jugador está buscando un Wumpus
en un sistema de cavernas y debe evitar trampas, pozos y otros peligros que acechan. Si estás interesado,
las reglas del juego son fáciles de encontrar en la web.
Supongamos que mantendremos la interfaz de usuario basada en texto, pero la desvincularemos de las
reglas del juego para que nuestra versión pueda usar diferentes idiomas en diferentes mercados. Las
reglas del juego se comunicarán con el componente de la interfaz de usuario mediante una API independiente
del idioma, y la interfaz de usuario traducirá la API al idioma humano adecuado.
Si las dependencias del código fuente se administran correctamente, como se muestra en la Figura 25.1,
cualquier número de componentes de la interfaz de usuario puede reutilizar las mismas reglas del juego. Las
reglas del juego no saben, ni les importa, qué lenguaje humano se está utilizando.
Figura 25.1 Cualquier número de componentes de la interfaz de usuario puede reutilizar las reglas del juego
Supongamos también que el estado del juego se mantiene en alguna tienda persistente, tal vez en flash, o
tal vez en la nube, o tal vez solo en RAM. En cualquiera de esos casos, no queremos que las reglas del
juego conozcan los detalles. Entonces, nuevamente, crearemos una API que las reglas del juego puedan
usar para comunicarse con el componente de almacenamiento de datos.
No queremos que las reglas del juego sepan nada sobre los diferentes tipos de almacenamiento de
datos, por lo que las dependencias deben estar correctamente dirigidas siguiendo la Regla de dependencia,
como se muestra en la Figura 25.2.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 212 212
¿ARQUITECTURA LIMPIA?
Debería quedar claro que podríamos aplicar fácilmente el enfoque de arquitectura limpia en este
contexto,1 con todos los casos de uso, límites, entidades y estructuras de datos correspondientes.
Pero, ¿realmente hemos encontrado todos los límites arquitectónicos significativos?
Por ejemplo, el idioma no es el único eje de cambio para la interfaz de usuario. También podríamos
querer variar el mecanismo por el cual comunicamos el texto. Por ejemplo, podríamos querer usar
una ventana de shell normal, mensajes de texto o una aplicación de chat. Hay muchas posibilidades
diferentes.
Eso significa que existe un límite arquitectónico potencial definido por este eje de cambio.
Quizás deberíamos construir una API que cruce ese límite y aísle el lenguaje del mecanismo de
comunicación; esa idea se ilustra en la figura 25.3.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 213 213
sorpresas Los contornos discontinuos indican componentes abstractos que definen una API que
es implementada por los componentes por encima o por debajo de ellos. Por ejemplo, la API de
idiomas está implementada en inglés y español.
GameRules se comunica con Language a través de una API que GameRules define e implementa
Language . Language se comunica con TextDelivery usando una API que Language define pero
TextDelivery implementa. La API está definida y es propiedad del usuario, en lugar del
implementador.
En cada caso, la API definida por esas interfaces Boundary es propiedad del componente
ascendente.
Las variaciones, como inglés, SMS y CloudData, son proporcionadas por interfaces
polimórficas definidas en el componente API abstracto e implementadas por los
componentes concretos que las sirven. Por ejemplo, esperaríamos que las interfaces
polimórficas definidas en Idioma se implementaran en inglés y español.
Podemos simplificar este diagrama eliminando todas las variaciones y centrándonos solo en los
componentes de la API. La figura 25.4 muestra este diagrama.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 214 214
Observe que el diagrama está orientado en la figura 25.4 de modo que todas las flechas apunten hacia arriba.
Esto pone GameRules en la parte superior. Esta orientación tiene sentido porque GameRules es el componente
que contiene las políticas de más alto nivel.
Considere la dirección del flujo de información. Todas las entradas provienen del usuario a través del componente
TextDelivery en la parte inferior izquierda. Esa información se eleva a través del componente Idioma y se traduce
en comandos para GameRules. GameRules procesa la entrada del usuario y envía los datos apropiados a
DataStorage en la parte inferior derecha.
Luego, GameRules envía la salida de regreso a Idioma, que traduce la API nuevamente al idioma apropiado y
luego entrega ese idioma al usuario a través de
Entrega de texto.
Esta organización divide efectivamente el flujo de datos en dos flujos.2 El flujo de la izquierda se ocupa de la
comunicación con el usuario y el flujo de la derecha se ocupa de la persistencia de los datos. Ambos flujos se
encuentran en el top3 en GameRules, que es el último procesador de los datos que pasan por ambos flujos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 215 215
Por lo tanto, a medida que los sistemas se vuelven más complejos, la estructura de los componentes puede dividirse en
muchos de estos flujos.
Considere el componente GameRules para Hunt the Wumpus. Parte de las reglas del juego se
ocupan de la mecánica del mapa. Saben cómo están conectadas las cavernas y qué objetos se
encuentran en cada caverna. Saben cómo mover al jugador de caverna en caverna y cómo determinar
los eventos con los que debe lidiar el jugador.
Pero hay otro conjunto de políticas a un nivel aún más alto: políticas que conocen la salud del
jugador y el costo o beneficio de un evento en particular. Estas políticas podrían hacer que el jugador
pierda salud gradualmente o que gane salud al descubrir alimentos. La política de mecánica de nivel
inferior declararía eventos a esta política de nivel superior, como FoundFood o FellInPit. La política de
nivel superior administraría el estado del jugador (como se muestra en la Figura 25.6). Eventualmente,
esa política decidiría si el jugador gana o pierde.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 216 216
¿Es este un límite arquitectónico? ¿Necesitamos una API que separe MoveManagement de
PlayerManagement? Bueno, hagamos esto un poco más interesante y agreguemos microservicios.
Supongamos que tenemos una versión multijugador masiva de Hunt the Wumpus.
MoveManagement se maneja localmente dentro de la computadora del jugador,
pero PlayerManagement es manejado por un servidor. PlayerManagement ofrece una API de
microservicio para todos los componentes de MoveManagement conectados .
El diagrama de la Figura 25.7 representa este escenario de una manera un tanto abreviada.
Los elementos de la red son un poco más complejos de lo que se muestra, pero probablemente aún
puedas hacerte una idea. Existe un límite arquitectónico completo entre MoveManagement y
PlayerManagement en este caso.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 217 217
CONCLUSIÓN
¿Qué significa todo esto? ¿Por qué tomé este programa absurdamente simple, que podría
implementarse en 200 líneas de Kornshell, y lo extrapolé con todos estos límites arquitectónicos
locos?
Este ejemplo pretende mostrar que los límites arquitectónicos existen en todas partes.
Nosotros, como arquitectos, debemos tener cuidado de reconocer cuándo se necesitan.
También debemos ser conscientes de que tales límites, cuando se implementan por completo,
son costosos. Al mismo tiempo, debemos reconocer que cuando se ignoran dichos límites, es
muy costoso agregarlos más tarde, incluso en presencia de conjuntos de pruebas integrales y
disciplina de refactorización.
Así que ahí lo tienes. Oh arquitecto de software, debes ver el futuro. Debes adivinar,
inteligentemente. Debe sopesar los costos y determinar dónde se encuentran los límites
arquitectónicos, cuáles deben implementarse por completo y cuáles deben
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 218 218
Pero esta no es una decisión de una sola vez. No se decide simplemente al comienzo de un
proyecto qué límites implementar y cuáles ignorar. Más bien, observa. Prestas atención a
medida que el sistema evoluciona. Observa dónde pueden ser necesarios los límites y luego
observa atentamente el primer indicio de fricción porque esos límites no existen.
En ese momento, sopesa los costos de implementar esos límites frente al costo de ignorarlos,
y revisa esa decisión con frecuencia. Su objetivo es implementar los límites justo en el punto
de inflexión donde el costo de implementar se vuelve menor que el costo de ignorar.
1. Debería quedar igual de claro que no aplicaríamos el enfoque de arquitectura limpia a algo tan trivial como este
juego. Después de todo, es probable que todo el programa se pueda escribir en 200 líneas de código o menos.
En este caso, estamos usando un programa simple como proxy para un sistema mucho más grande con límites
arquitectónicos significativos.
2. Si está confundido por la dirección de las flechas, recuerde que apuntan en la dirección de
dependencias del código fuente, no en la dirección del flujo de datos.
3. En días pasados, habríamos llamado a ese componente superior la Transformación central. Ver Práctico
Guía para el diseño de sistemas estructurados, 2ª ed., Meilir Page-Jones, 1988.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 219 219
26
EL COMPONENTE PRINCIPAL
En cada sistema, hay al menos un componente que crea, coordina y supervisa a los
demás. A este componente lo llamo Main.
EL ÚLTIMO DETALLE
El componente principal es el último detalle: la política de nivel más bajo. Es el punto de
entrada inicial del sistema. Nada, excepto el sistema operativo, depende de ello. Su trabajo es
crear todas las Fábricas, Estrategias y otras instalaciones globales, y luego entregar el control
a las partes abstractas de alto nivel del sistema.
Es en este componente principal donde las dependencias deben ser inyectadas por un
marco de inyección de dependencia. Una vez que se inyectan en Main, Main debe distribuir esos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 220 220
Considere el siguiente componente principal de una versión reciente de Hunt the Wumpus.
Observe cómo carga todas las cadenas que no queremos que conozca el cuerpo principal del código.
"brillante",
"húmedo",
"seco",
"espeluznante",
"feo",
"brumoso",
"caliente",
"frío", "con
corrientes
de aire", "terrible"
};
};
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 221 221
"pasillo", "salón",
"extensión" };
Ahora aquí está la función principal . Observe cómo utiliza HtwFactory para crear el juego.
Pasa el nombre de la clase, htw.game.HuntTheWumpusFacade, porque esa clase es
incluso más sucia que Main. Esto evita que los cambios en esa clase hagan que Main se
vuelva a compilar o implementar.
HuntTheWumpus.Command c = juego.makeRestCommand();
Sistema.salida.println(">"); Comando de cadena = br.readLine(); if
(command.equalsIgnoreCase("e")) c = juego.makeMoveCommand(ESTE);
else if (command.equalsIgnoreCase("w")) c = game.makeMoveCommand(WEST);
más si (comando.equalsIgnoreCase("n"))
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 222 222
c = juego.hacerMovimientoComando(NORTE); else if
(command.equalsIgnoreCase("s")) c =
juego.makeMoveCommand(SUR); else if
(command.equalsIgnoreCase("r")) c = game.makeRestCommand();
else if (command.equalsIgnoreCase("sw")) c =
game.makeShootCommand(WEST); else if
(command.equalsIgnoreCase("se")) c =
game.makeShootCommand(EAST); else if
(command.equalsIgnoreCase("sn")) c =
game.makeShootCommand(NORTH); else if
(command.equalsIgnoreCase("ss")) c =
game.makeShootCommand(SUR); si no
(comando.equalsIgnoreCase("q"))
devolver;
c.ejecutar();
}
}
Tenga en cuenta también que main crea el flujo de entrada y contiene el ciclo principal del
juego, interpretando los comandos de entrada simples, pero luego difiere todo el procesamiento
a otros componentes de nivel superior.
cavernas.add(hacerNombre());
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 223 223
juego.addPitCavern(anyOther(playerCavern));
juego.addPitCavern(anyOther(playerCavern));
juego.addPitCavern(anyOther(playerCavern));
juego.setCarcaj(5);
}
El punto es que Main es un módulo sucio de bajo nivel en el círculo exterior de la arquitectura
limpia. Carga todo para el sistema de alto nivel y luego le entrega el control.
CONCLUSIÓN
Piense en Main como un complemento de la aplicación: un complemento que establece
las condiciones y configuraciones iniciales, recopila todos los recursos externos y luego
entrega el control a la política de alto nivel de la aplicación. Como es un complemento, es
posible tener muchos componentes principales , uno para cada configuración de su aplicación.
Por ejemplo, podría tener un complemento principal para desarrollo, otro para prueba y otro para
producción. También puede tener un complemento principal para cada país en el que implemente,
o cada jurisdicción, o cada cliente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 224 224
27
SERVICIOS: GRANDES Y PEQUEÑOS
Las "arquitecturas" orientadas a servicios y las "arquitecturas" de microservicios se han vuelto muy
populares últimamente. Las razones de su popularidad actual incluyen las siguientes:
• Los servicios parecen estar fuertemente desvinculados entre sí. Como veremos, esto es sólo
parcialmente cierto. • Los servicios parecen respaldar la independencia del desarrollo y la
implementación. Una vez más, como veremos, esto es sólo parcialmente cierto.
¿ARQUITECTURA DE SERVICIO?
Primero, consideremos la noción de que usar servicios, por su naturaleza, es una arquitectura.
Esto es evidentemente falso. La arquitectura de un sistema está definida por límites que
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 225 225
separe la política de alto nivel de los detalles de bajo nivel y siga la regla de dependencia.
Los servicios que simplemente separan los comportamientos de las aplicaciones son poco más que costosas
llamadas a funciones y no son necesariamente significativos desde el punto de vista arquitectónico.
Esto no quiere decir que todos los servicios deban ser arquitectónicamente significativos. A
menudo hay beneficios sustanciales en la creación de servicios que separan la funcionalidad
entre procesos y plataformas, ya sea que obedezcan la regla de dependencia o no. Es solo que los
servicios, en sí mismos, no definen una arquitectura.
Así es con los servicios. Después de todo, los servicios son solo llamadas de funciones a través de
los límites de procesos y/o plataformas. Algunos de esos servicios son arquitectónicamente
significativos y otros no. Nuestro interés, en este capítulo, está en el primero.
El signo de interrogación en el encabezado anterior indica que esta sección desafiará la ortodoxia
popular actual de la arquitectura de servicios. Abordemos los beneficios uno por uno.
Uno de los grandes supuestos beneficios de dividir un sistema en servicios es que los
servicios están fuertemente desacoplados entre sí. Después de todo, cada servicio se ejecuta
en un proceso diferente, o incluso en un procesador diferente; por lo tanto, esos servicios no tienen
acceso a las variables de los demás. Además, la interfaz de cada servicio debe estar bien definida.
Ciertamente hay algo de verdad en esto, pero no mucha verdad. Sí, los servicios están
desacoplados a nivel de variables individuales. Sin embargo, todavía pueden acoplarse mediante
recursos compartidos dentro de un procesador o en la red. Además, están fuertemente acoplados
por los datos que comparten.
Por ejemplo, si se agrega un nuevo campo a un registro de datos que se pasa entre servicios,
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 226 226
luego se debe cambiar cada servicio que opera en el nuevo campo. Los servicios también deben estar muy de
acuerdo sobre la interpretación de los datos en ese campo. Por tanto, esos servicios están fuertemente
acoplados al registro de datos y, por lo tanto, indirectamente acoplados entre sí.
En cuanto a que las interfaces estén bien definidas, eso es ciertamente cierto, pero no lo es menos para
las funciones. Las interfaces de servicio no son más formales, ni más rigurosas ni mejor definidas que las
interfaces de funciones. Claramente, entonces, este beneficio es algo así como una ilusión.
equipos
Hay algo de verdad en esta creencia, pero solo algo. En primer lugar, la historia ha demostrado que los
grandes sistemas empresariales pueden construirse a partir de monolitos y sistemas basados en componentes,
así como sistemas basados en servicios. Por lo tanto, los servicios no son la única opción para construir
sistemas escalables.
En segundo lugar, la falacia del desacoplamiento significa que los servicios no siempre se pueden desarrollar,
implementar y operar de forma independiente. En la medida en que estén acoplados por datos o
comportamiento, el desarrollo, la implementación y la operación deben coordinarse.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 227 227
Queríamos que nuestro sistema fuera escalable, por lo que elegimos construirlo a partir de
muchos microservicios pequeños. Subdividimos nuestro personal de desarrollo en muchos equipos
pequeños, cada uno de los cuales es responsable de desarrollar, mantener y operar una cantidad
correspondientemente pequeña de servicios.
El diagrama de la Figura 27.1 muestra cómo nuestros arquitectos ficticios organizaron los servicios
para implementar esta aplicación. El servicio TaxiUI se ocupa de los clientes, que utilizan dispositivos
móviles para pedir taxis. El servicio TaxiFinder examina los inventarios de los distintos TaxiSuppliers
y determina qué taxis son posibles candidatos para el usuario. Los deposita en un registro de datos a
corto plazo adjunto a ese usuario. El servicio TaxiSelector toma los criterios del usuario de costo,
tiempo, lujo, etc., y elige un taxi apropiado entre los candidatos. Entrega ese taxi al servicio
TaxiDispatcher , que ordena el taxi apropiado.
Ahora supongamos que este sistema ha estado en funcionamiento durante más de un año. Nuestro
equipo de desarrolladores ha estado felizmente desarrollando nuevas funciones mientras mantiene y
opera todos estos servicios.
La empresa instalará varios puntos de recogida de gatitos por toda la ciudad. Cuando se realiza
un pedido de gatitos, se seleccionará un taxi cercano para recoger un gatito en uno de esos puntos
de recogida y luego entregarlo en la dirección adecuada.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 228 228
Uno de los proveedores de taxis ha aceptado participar en este programa. Es probable que otros lo sigan.
Todavía otros pueden declinar.
Por supuesto, algunos conductores pueden ser alérgicos a los gatos, por lo que estos conductores nunca
deben seleccionarse para este servicio. Además, es indudable que algunos clientes tendrán alergias
similares, por lo que no se debe seleccionar un vehículo que se haya utilizado para entregar gatitos en
los últimos 3 días para clientes que declaren tales alergias.
Mira ese diagrama de servicios. ¿Cuántos de esos servicios tendrán que cambiar para implementar esta
función? Todos ellos. Claramente, el desarrollo y la implementación de la función de gatito deberán coordinarse
con mucho cuidado.
En otras palabras, todos los servicios están acoplados y no se pueden desarrollar, implementar ni mantener
de forma independiente.
Este es el problema con las preocupaciones transversales. Todo sistema de software debe enfrentar este
problema, ya sea que esté orientado a servicios o no. Las descomposiciones funcionales, del tipo que se
muestra en el diagrama de servicios de la figura 27.1, son muy vulnerables a las nuevas características que
atraviesan todos esos comportamientos funcionales.
OBJETOS AL RESCATE
¿Cómo habríamos resuelto este problema en una arquitectura basada en componentes? La consideración
cuidadosa de los principios de diseño de SOLID nos habría llevado a crear un conjunto de clases que podrían
extenderse polimórficamente para manejar nuevas características.
El diagrama de la figura 27.2 muestra la estrategia. Las clases en este diagrama corresponden
aproximadamente a los servicios que se muestran en la Figura 27.1. Sin embargo, tenga en cuenta los límites.
Tenga en cuenta también que las dependencias siguen la regla de dependencia.
Gran parte de la lógica de los servicios originales se conserva dentro de las clases base del modelo de
objetos. Sin embargo, esa parte de la lógica que era específica de los viajes se ha extraído en un componente
de viajes . La nueva función para gatitos se ha colocado en un componente Gatitos . Estos dos componentes
anulan las clases base abstractas en los componentes originales mediante un patrón como el método de
plantilla o la estrategia.
Tenga en cuenta nuevamente que los dos nuevos componentes, Rides y Kittens, siguen la
regla de dependencia. Tenga en cuenta también que las clases que implementan esas características
son creadas por fábricas bajo el control de la interfaz de usuario.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 229 229
Claramente, en este esquema, cuando se implementa la función Kitty, TaxiUI debe cambiar. Pero
nada más necesita ser cambiado. Más bien, se agrega un nuevo archivo jar, Gem o DLL al sistema
y se carga dinámicamente en tiempo de ejecución.
Por lo tanto, la función Kitty está desacoplada y se puede desarrollar y desplegar de forma independiente.
Figura 27.2 Uso de un enfoque orientado a objetos para tratar problemas transversales
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 230 230
La pregunta obvia es: ¿Podemos hacer eso para los servicios? Y la respuesta es, por supuesto: ¡Sí! Los
servicios no tienen por qué ser pequeños monolitos. En cambio, los servicios se pueden diseñar utilizando
los principios SOLID y se les puede dar una estructura de componentes para que se les puedan agregar
nuevos componentes sin cambiar los componentes existentes dentro del servicio.
Piense en un servicio en Java como un conjunto de clases abstractas en uno o más archivos jar. Piense en
cada característica nueva o extensión de característica como otro archivo jar que contiene clases que
amplían las clases abstractas en los primeros archivos jar. La implementación de una nueva función no se
convierte en una cuestión de volver a implementar los servicios, sino más bien en una cuestión de
simplemente agregar los nuevos archivos jar a las rutas de carga de esos servicios. En otras palabras,
agregar nuevas funciones se ajusta al principio abierto-cerrado.
El diagrama de servicio de la Figura 27.3 muestra la estructura. Los servicios siguen existiendo como
antes, pero cada uno tiene su propio diseño de componentes internos, lo que permite agregar nuevas
funciones como nuevas clases derivadas. Esas clases derivadas viven dentro de sus propios componentes.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 231 231
Figura 27.3 Cada servicio tiene su propio diseño de componentes internos, lo que permite agregar nuevas funciones
como nuevas clases derivadas
PREOCUPACIONES TRANSVERSALES
Lo que hemos aprendido es que los límites arquitectónicos no se encuentran entre los servicios.
Más bien, esos límites atraviesan los servicios, dividiéndolos en componentes.
Para hacer frente a las preocupaciones transversales a las que se enfrentan todos los sistemas
importantes, los servicios deben diseñarse con arquitecturas de componentes internos que sigan la
regla de dependencia, como se muestra en el diagrama de la figura 27.4. Esos servicios no definen
los límites arquitectónicos del sistema; en cambio, los componentes dentro de los servicios sí lo hacen.
Figura 27.4 Los servicios deben diseñarse con arquitecturas de componentes internos que sigan la regla de
dependencia
CONCLUSIÓN
A pesar de lo útiles que son los servicios para la escalabilidad y la capacidad de desarrollo de un
sistema, no son, en sí mismos, elementos arquitectónicamente significativos. La arquitectura de un
sistema se define por los límites trazados dentro de ese sistema y por las dependencias que cruzan
esos límites. Esa arquitectura no está definida por los mecanismos físicos por los cuales los
elementos se comunican y ejecutan.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 232 232
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 233 233
28
EL LÍMITE DE LA PRUEBA
Sí, así es: las pruebas son parte del sistema y participan en la arquitectura al igual que
cualquier otra parte del sistema. De alguna manera, esa participación es bastante normal. En
otras formas, puede ser bastante único.
Existe una gran confusión acerca de las pruebas. ¿Son parte del sistema? ¿Están separados del
sistema? ¿Qué tipos de pruebas hay? ¿Las pruebas unitarias y las pruebas de integración son cosas
diferentes? ¿Qué pasa con las pruebas de aceptación, las pruebas funcionales, las pruebas Cucumber,
las pruebas TDD, las pruebas BDD, las pruebas de componentes, etc.?
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 234 234
afortunadamente no es necesario. Desde un punto de vista arquitectónico, todas las pruebas son iguales.
Ya sean las diminutas pruebas creadas por TDD o las pruebas grandes de FitNesse, Cucumber, SpecFlow
o JBehave, son arquitectónicamente equivalentes.
Las pruebas, por su propia naturaleza, siguen la regla de dependencia; son muy detallados y concretos; y
siempre dependen hacia adentro del código que se está probando. De hecho, puede pensar en las pruebas
como el círculo más externo de la arquitectura. Nada dentro del sistema depende de las pruebas, y las
pruebas siempre dependen internamente de los componentes del sistema.
Las pruebas también se pueden implementar de forma independiente. De hecho, la mayoría de las veces se
implementan en sistemas de prueba, en lugar de sistemas de producción. Por lo tanto, incluso en los sistemas
donde la implementación independiente no es necesaria, las pruebas aún se implementarán de forma
independiente.
Las pruebas son el componente del sistema más aislado. No son necesarios para el funcionamiento del
sistema. Ningún usuario depende de ellos. Su papel es apoyar el desarrollo, no la operación. Y, sin
embargo, no son menos un componente del sistema que cualquier otro. De hecho, en muchos sentidos
representan el modelo que deben seguir todos los demás componentes del sistema.
El problema, por supuesto, es el acoplamiento. Las pruebas que están fuertemente acopladas al sistema
deben cambiar junto con el sistema. Incluso el cambio más trivial en un componente del sistema puede hacer
que muchas pruebas acopladas se rompan o requieran cambios.
Esta situación puede volverse aguda. Los cambios en los componentes comunes del sistema pueden hacer
que cientos, o incluso miles, de pruebas se rompan. Esto se conoce como el problema de las pruebas frágiles.
No es difícil ver cómo puede suceder esto. Imagine, por ejemplo, un conjunto de pruebas que utilizan la GUI
para verificar las reglas comerciales. Dichas pruebas pueden comenzar en la pantalla de inicio de sesión y
luego navegar a través de la estructura de la página hasta que puedan verificar un negocio en particular.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 235 235
normas. Cualquier cambio en la página de inicio de sesión, o en la estructura de navegación, puede hacer
que se rompa una enorme cantidad de pruebas.
Las pruebas frágiles a menudo tienen el efecto perverso de volver rígido el sistema. Cuando los
desarrolladores se dan cuenta de que los cambios simples en el sistema pueden causar fallas masivas en las
pruebas, es posible que se resistan a realizar esos cambios. Por ejemplo, imagine la conversación entre el equipo
de desarrollo y un equipo de marketing que solicita un cambio simple en la estructura de navegación de la página
que hará que se rompan 1000 pruebas.
La solución es diseñar para la capacidad de prueba. La primera regla del diseño de software, ya sea para la
capacidad de prueba o por cualquier otra razón, es siempre la misma: no dependa de cosas volátiles. Las GUI son
volátiles. Los conjuntos de pruebas que operan el sistema a través de la GUI deben ser frágiles. Por lo tanto, diseñe
el sistema y las pruebas para que las reglas comerciales puedan probarse sin usar la GUI.
LA API DE PRUEBA
La forma de lograr este objetivo es crear una API específica que las pruebas puedan usar para verificar todas las
reglas comerciales. Esta API debe tener superpoderes que permitan que las pruebas eviten las restricciones de
seguridad, eviten recursos costosos (como las bases de datos) y fuercen al sistema a estados comprobables
particulares. Esta API será un superconjunto del conjunto de interactores y adaptadores de interfaz que utiliza la
interfaz de usuario.
El propósito de la API de prueba es desacoplar las pruebas de la aplicación. Este desacoplamiento abarca más
que solo separar las pruebas de la interfaz de usuario: el objetivo es desacoplar la estructura de las pruebas de la
estructura de la aplicación.
ACOPLAMIENTO ESTRUCTURAL
El acoplamiento estructural es una de las formas más fuertes e insidiosas de acoplamiento de prueba.
Imagine un conjunto de pruebas que tiene una clase de prueba para cada clase de producción y un conjunto de
métodos de prueba para cada método de producción. Tal conjunto de pruebas está profundamente acoplado a la
estructura de la aplicación.
Cuando uno de esos métodos de producción o clases cambia, también debe cambiar una gran cantidad de
pruebas. En consecuencia, las pruebas son frágiles y hacen que el código de producción sea rígido.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 236 236
Esto permite que el código de producción se refactorice y evolucione de manera que no afecte
las pruebas. También permite que las pruebas se refactoricen y evolucionen de manera que no
afecten el código de producción.
Esta separación de la evolución es necesaria porque a medida que pasa el tiempo, las pruebas
tienden a ser cada vez más concretas y específicas. En cambio, el código de producción tiende
a volverse cada vez más abstracto y general. El fuerte acoplamiento estructural impide, o al menos
impide, esta evolución necesaria e impide que el código de producción sea tan general y flexible como
podría ser.
SEGURIDAD
Los superpoderes de la API de prueba podrían ser peligrosos si se implementaran en sistemas
de producción. Si esto es una preocupación, entonces la API de prueba y las partes peligrosas de su
implementación deben mantenerse en un componente separado e independiente.
CONCLUSIÓN
Las pruebas no están fuera del sistema; más bien, son partes del sistema que deben estar bien
diseñadas para que brinden los beneficios deseados de estabilidad y regresión.
Las pruebas que no están diseñadas como parte del sistema tienden a ser frágiles y difíciles de
mantener. Tales pruebas a menudo terminan en el piso de la sala de mantenimiento, descartadas
porque son demasiado difíciles de mantener.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 237 237
29
ARQUITECTURA INTEGRADA LIMPIA
Hace un tiempo leí un artículo titulado “La creciente importancia del software de mantenimiento
para el Departamento de Defensa”1 en el blog de Doug Schmidt. Doug hizo la siguiente afirmación:
Fue un momento de clarificación para mí. Doug mencionó dos términos que habría pensado que
eran obvios, pero tal vez no lo sean. El software es algo que puede tener una larga vida útil, pero
el firmware se volverá obsoleto a medida que evolucione el hardware. Si ha dedicado algún tiempo
al desarrollo de sistemas integrados, sabe que el hardware evoluciona y se mejora continuamente.
Al mismo tiempo, se añaden funciones a la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 238 238
Si bien el software no se desgasta, puede destruirse desde dentro debido a las dependencias
no administradas del firmware y el hardware.
No es raro que al software integrado se le niegue una vida potencialmente larga debido a que está infectado
con dependencias en el hardware.
Me gusta la definición de firmware de Doug, pero veamos qué otras definiciones existen. Encontré estas
alternativas:
• “El firmware se almacena en dispositivos de memoria no volátil, como ROM, EPROM o memoria flash”.
(https://en.wikipedia.org/wiki/Firmware)
• El firmware es “Software (programas o datos) que se ha escrito en la memoria de solo lectura (ROM)”.
(http://www.webopedia.com/TERM/F/firmware.html)
La declaración de Doug me hace darme cuenta de que estas definiciones aceptadas de firmware son
incorrectas o, al menos, obsoletas. El firmware no significa que el código viva en la ROM. No es firmware
debido a dónde está almacenado; más bien, es firmware debido a lo que depende y lo difícil que es cambiar
a medida que evoluciona el hardware. El hardware evoluciona (haga una pausa y busque evidencia en su
teléfono), por lo que debemos estructurar nuestro código integrado con esa realidad en mente.
No tengo nada en contra del firmware o de los ingenieros de firmware (se sabe que yo mismo escribo algo
de firmware). Pero lo que realmente necesitamos es menos firmware y más software.
En realidad, estoy decepcionado de que los ingenieros de firmware escriban tanto firmware.
He estado involucrado en muchos esfuerzos donde la línea entre el código del producto (el software) y el
código que interactúa con el hardware del producto (el firmware) es
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 239 239
borroso hasta el punto de la inexistencia. Por ejemplo, a fines de la década de 1990 tuve la
suerte de ayudar a rediseñar un subsistema de comunicaciones que estaba pasando de
multiplexación por división de tiempo (TDM) a voz sobre IP (VOIP). VOIP es cómo se hacen las
cosas ahora, pero TDM se consideró el estado del arte en las décadas de 1950 y 1960, y se
implementó ampliamente en las décadas de 1980 y 1990.
Siempre que teníamos una pregunta para el ingeniero de sistemas sobre cómo debería reaccionar
una llamada ante una situación dada, desaparecía y poco después salía con una respuesta muy
detallada. "¿De dónde sacó esa respuesta?" preguntamos. “Del código del producto actual”,
respondía. ¡El código heredado enredado era la especificación para el nuevo producto! La
implementación existente no tenía separación entre TDM y la lógica comercial de hacer llamadas.
Todo el producto dependía del hardware/tecnología de arriba a abajo y no se podía desenredar.
Todo el producto se había convertido esencialmente en firmware.
Considere otro ejemplo: los mensajes de comando llegan a este sistema a través del puerto serie.
Como era de esperar, hay un procesador/distribuidor de mensajes. El procesador de mensajes
conoce el formato de los mensajes, puede analizarlos y luego puede enviar el mensaje al
código que puede manejar la solicitud. Nada de esto es sorprendente, excepto que el
procesador/distribuidor de mensajes reside en el mismo archivo que el código que interactúa
con un hardware UART2. El procesador de mensajes está contaminado con detalles de UART. El
procesador de mensajes podría haber sido un software con una vida útil potencialmente larga, pero
en cambio es firmware. Al procesador de mensajes se le niega la oportunidad de convertirse en
software, ¡y eso no está bien!
PRUEBA DE APLICACIÓN-TITULACIÓN
¿Por qué tanto software embebido potencial se convierte en firmware? Parece que la mayor
parte del énfasis está en lograr que el código incrustado funcione, y no tanto énfasis en
estructurarlo para una larga vida útil. Kent Beck describe tres
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 240 240
actividades en la creación de software (el texto citado son las palabras de Kent y las cursivas son mi comentario):
1. “Primero haz que funcione”. Usted está fuera del negocio si no funciona.
2. “Entonces hazlo bien”. Refactorice el código para que usted y los demás puedan entenderlo.
y evolucionarlo a medida que cambien las necesidades o se entiendan mejor.
Gran parte del software de sistemas integrados que veo en la naturaleza parece haber sido escrito con "Haz que
funcione" en mente, y quizás también con una obsesión por el objetivo "Haz que sea rápido", logrado mediante la adición
de micro optimizaciones en cada oportunidad. . En The Mythical Man-Month, Fred Brooks sugiere que "planeemos tirar uno".
Kent y Fred están dando prácticamente el mismo consejo: aprenda lo que funciona y luego haga una mejor solución.
El software integrado no es especial cuando se trata de estos problemas. La mayoría de las aplicaciones no
incrustadas se construyen solo para funcionar, sin preocuparse por hacer que el código sea correcto para una vida útil
prolongada.
Hacer que una aplicación funcione es lo que llamo la prueba de actitud de la aplicación para un programador.
Los programadores, integrados o no, que solo se preocupan por hacer que su aplicación funcione, están perjudicando a
sus productos y empleadores. Hay mucho más en la programación que simplemente hacer que una aplicación funcione.
Como ejemplo de código producido al pasar la prueba App-titude, consulte estas funciones ubicadas en un archivo de un
pequeño sistema integrado:
ISR(TIMER1_vect) { ... }
ISR(INT2_vect) { ... } void
btn_Handler(void) { ... } float calc_RPM(void)
{ ... } static char Read_RawData(void)
{ ... } void Do_Average(void) { ... } void
Get_Next_Measurement(void) { ... } void
Zero_Sensor_1(void) { ... } void Zero_Sensor_2(void)
{ ... } void Dev_Control(char Activation) { ... } char
Load_FLASH_Setup(void) { ... } void
Save_FLASH_Setup(void) { ... } void Store_DataSet(void)
{ ... }
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 241 241
Esa lista de funciones está en el orden en que las encontré en el archivo fuente. Ahora los separaré y los
agruparé por preocupación:
ISR(TIMER1_vect) { ... }*
ISR(INT2_vect) { ... } void
uC_Sleep(void) { ... }
Las funciones que reaccionan al botón de encendido y apagado
presionan void btn_Handler(void) { ... } void Dev_Control(char
Activation) { ... }
Una función que puede obtener lecturas de entrada A/D del hardware
carácter estático Read_RawData (vacío) { ... }
• Funciones que almacenan valores en el almacenamiento persistente
Mirando algunos de los otros archivos en esta aplicación, encontré muchos impedimentos para entender el
código. También encontré una estructura de archivos que implicaba que la única forma de
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 242 242
pruebe si alguno de estos códigos está en el destino incrustado. Prácticamente cada bit de este código
sabe que está en una arquitectura de microprocesador especial, utilizando construcciones C "extendidas"3
que vinculan el código a una cadena de herramientas y un microprocesador en particular. No hay forma de
que este código tenga una vida útil prolongada a menos que el producto nunca necesite moverse a un
entorno de hardware diferente.
Esta aplicación funciona: el ingeniero pasó la prueba App-titude. Pero no se puede decir que la aplicación
tenga una arquitectura integrada limpia.
Sí, incrustado es especial. Los ingenieros integrados son especiales. Pero el desarrollo
integrado no es tan especial como para que los principios de este libro no sean aplicables a los
sistemas integrados.
Uno de los problemas incrustados especiales es el cuello de botella del hardware de destino. Cuando
el código incrustado está estructurado sin aplicar principios y prácticas de arquitectura limpia, a
menudo se enfrentará al escenario en el que puede probar su código solo en el objetivo. Si el destino
es el único lugar donde es posible realizar pruebas, el cuello de botella del hardware de destino lo ralentizará.
Capas
Las capas vienen en muchos sabores. Comencemos con tres capas, como se muestra en la Figura
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 243 243
29.1. En la parte inferior, está el hardware. Como nos advierte Doug, debido a los avances
tecnológicos y la ley de Moore, el hardware cambiará. Las piezas se vuelven obsoletas y las
piezas nuevas usan menos energía o proporcionan un mejor rendimiento o son más baratas.
Cualquiera que sea la razón, como ingeniero integrado, no quiero tener un trabajo más grande de
lo necesario cuando finalmente ocurra el inevitable cambio de hardware.
La separación entre el hardware y el resto del sistema es un hecho, al menos una vez que se
define el hardware (Figura 29.2). Aquí es donde a menudo comienzan los problemas cuando
intenta pasar la prueba App-titude. No hay nada que impida que el conocimiento del hardware
contamine todo el código. Si no tiene cuidado sobre dónde coloca las cosas y qué puede saber un
módulo sobre otro módulo, será muy difícil cambiar el código. No me refiero solo a cuándo cambia
el hardware, sino a cuándo el usuario solicita un cambio o cuándo se debe corregir un error.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 244 244
Figura 29.2 El hardware debe estar separado del resto del sistema
La mezcla de software y firmware es un antipatrón. El código que muestre este patrón anti
resistirá los cambios. Además, los cambios serán peligrosos y, a menudo, tendrán consecuencias
no deseadas. Se necesitarán pruebas de regresión completas de todo el sistema para cambios
menores. Si no ha creado pruebas instrumentadas externamente, espere aburrirse con las
pruebas manuales, y luego puede esperar nuevos informes de errores.
El hardware es un detalle
La línea entre el software y el firmware normalmente no está tan bien definida como la línea
entre el código y el hardware, como se muestra en la figura 29.3.
Figura 29.3 La línea entre software y firmware es un poco más borrosa que la línea entre código y
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 245 245
hardware
Uno de sus trabajos como desarrollador de software integrado es reafirmar esa línea.
El nombre del límite entre el software y el firmware es la capa de abstracción de
hardware (HAL) (Figura 29.4). Esta no es una idea nueva: ha estado en las PC desde
los días anteriores a Windows.
La HAL existe para el software que se encuentra sobre ella y su API debe adaptarse a las
necesidades de ese software. Como ejemplo, el firmware puede almacenar bytes y
matrices de bytes en la memoria flash. Por el contrario, la aplicación necesita almacenar y
leer pares de nombre/valor en algún mecanismo de persistencia. El software no debe
preocuparse de que los pares de nombre/valor se almacenen en la memoria flash, un disco
giratorio, la nube o la memoria central. La HAL proporciona un servicio y no le revela al
software cómo lo hace. La implementación flash es un detalle que debe ocultarse del
software.
Como otro ejemplo, un LED está vinculado a un bit GPIO. El firmware podría
proporcionar acceso a los bits GPIO, donde HAL podría proporcionar Led_TurnOn(5).
Esa es una capa de abstracción de hardware de bastante bajo nivel. Consideremos
elevar el nivel de abstracción desde una perspectiva de hardware a la perspectiva de
software/producto. ¿Qué indica el LED? Supongamos que indicaba batería baja. En algún
nivel, el firmware (o un paquete de soporte de placa) podría proporcionar Led_TurnOn(5),
mientras que HAL proporciona Indicate_LowBattery(). Puede ver los servicios de expresión
de HAL que necesita la aplicación. También puede ver que las capas pueden contener
capas. Es más un patrón fractal repetitivo que un conjunto limitado de capas predefinidas. El GPIO
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 246 246
El procesador es un detalle
Cuando su aplicación incrustada utiliza una cadena de herramientas especializada, a menudo proporcionará
4
archivos de encabezado para <i>ayudarle </i>.Estos compiladores a menudo se toman libertades con el
lenguaje C y agregan nuevas palabras clave para acceder a las funciones de su procesador. El código se
verá como C, pero ya no es C.
A veces, los compiladores de C proporcionados por el proveedor proporcionan lo que parecen variables
globales para dar acceso directo a los registros del procesador, puertos de E/S, temporizadores de reloj, bits
de E/S, controladores de interrupción y otras funciones del procesador. Es útil tener acceso a estas cosas
fácilmente, pero tenga en cuenta que cualquiera de su código que usa estas funciones útiles ya no es C. No se
compilará para otro procesador, o tal vez incluso con un compilador diferente para el mismo procesador.
Odiaría pensar que el proveedor de herramientas y silicio está siendo cínico, vinculando su producto al
compilador. Démosle al proveedor el beneficio de la duda asumiendo que realmente está tratando de ayudar.
Pero ahora depende de ti usar esa ayuda de una manera que no te haga daño en el futuro. Deberá limitar qué
archivos pueden conocer las extensiones C.
Veamos este archivo de encabezado diseñado para la familia de DSP ACME, ya sabes, los que usa Wile E.
Coyote:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 247 247
#elif definido(_ACME_A42)
typedef unsigned long typedef Uint_32;
unsigned int typedef unsigned Uint_16;
char Uint_8;
#terminara si
En lugar de usar acmetypes.h, debe intentar seguir una ruta más estandarizada y
utilice stdint.h. Pero, ¿qué sucede si el compilador de destino no proporciona stdint.h? Puedes
escribir este archivo de encabezado. El stdint.h que escribe para las compilaciones de destino usa acmetypes.h
para compilaciones de destino como esta:
#ifndef_STDINT_H_
#definir _STDINT_H_
#include <tiposacme.h>
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 248 248
#terminara si
Hacer que su software y firmware incorporados usen stdint.h ayuda a mantener su código limpio y portátil.
Ciertamente, todo el software debe ser independiente del procesador, pero no todo el firmware puede serlo. El
siguiente fragmento de código aprovecha las extensiones especiales de C que le dan a su código acceso a los
periféricos del microcontrolador.
Es probable que su producto use este microcontrolador para que pueda usar sus periféricos integrados. Esta
función genera una línea que dice "hola" al puerto de salida en serie. (Este ejemplo se basa en código real de la
naturaleza).
vacío decir_hola() {
IE = 0b11000000;
SBUF0 = (0x68);
mientras (TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x69);
mientras (TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0a);
mientras (TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0d);
mientras (TI_0 == 0);
TI_0 = 0;
IE = 0b11010000;
}
Hay muchos problemas con esta pequeña función. Una cosa que podría llamarte la atención es la presencia de
0b11000000. Esta notación binaria es genial; C puede hacer eso?
Lamentablemente no. Algunos otros problemas se relacionan con este código directamente usando las extensiones
C personalizadas:
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 249 249
TI_0: Interrupción de vacío de búfer de transmisión serie. Leer un 1 indica que el búfer
está vacío.
Las variables en mayúsculas en realidad acceden a los periféricos integrados del microcontrolador. Si desea
controlar las interrupciones y los caracteres de salida, debe usar estos periféricos. Sí, esto es conveniente, pero
no es C.
Una arquitectura integrada limpia usaría estos registros de acceso de dispositivos directamente en muy
pocos lugares y los limitaría totalmente al firmware. Todo lo que sabe acerca de estos registros se convierte en
firmware y, en consecuencia, está vinculado al silicio. Vincular el código al procesador lo perjudicará cuando
desee que el código funcione antes de tener un hardware estable. También le hará daño cuando mueva su
aplicación integrada a un nuevo procesador.
Si usa un microcontrolador como este, su firmware podría aislar estas funciones de bajo nivel con algún tipo
de capa de abstracción de procesador (PAL). El firmware por encima de PAL podría probarse fuera del
objetivo, haciéndolo un poco menos firme.
Un HAL es necesario, pero ¿es suficiente? En los sistemas integrados sin sistema operativo, una HAL
puede ser todo lo que necesita para evitar que su código se vuelva demasiado adicto al entorno operativo.
Pero, ¿qué pasa con los sistemas integrados que utilizan un sistema operativo en tiempo real (RTOS) o
alguna versión integrada de Linux o Windows?
Para darle a su código incrustado una buena oportunidad de una larga vida, debe tratar el sistema
operativo como un detalle y protegerlo contra las dependencias del sistema operativo.
El software accede a los servicios del entorno operativo a través del sistema operativo. El sistema operativo es
una capa que separa el software del firmware (Figura 29.5). Usar un sistema operativo directamente puede
causar problemas. Por ejemplo, ¿qué sucede si otra empresa compra su proveedor de RTOS y las regalías
aumentan o la calidad disminuye? ¿Qué pasa si sus necesidades cambian y su RTOS no tiene las capacidades
que necesita ahora?
Tendrás que cambiar mucho código. Estos no serán solo cambios sintácticos simples debido a la API del
nuevo sistema operativo, sino que probablemente tendrán que adaptarse semánticamente a las diferentes
capacidades y primitivas del nuevo sistema operativo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 250 250
Una arquitectura integrada limpia aísla el software del sistema operativo, a través de una
capa de abstracción del sistema operativo (OSAL) (Figura 29.6). En algunos casos,
implementar esta capa puede ser tan simple como cambiar el nombre de una función. En
otros casos, podría implicar envolver varias funciones juntas.
Si alguna vez movió su software de un RTOS a otro, sabe que es doloroso. Si su software
dependiera directamente de un OSAL en lugar del sistema operativo, en gran medida
estaría escribiendo un nuevo OSAL que es compatible con el antiguo OSAL. ¿Qué preferiría
hacer: modificar un montón de código existente complejo o escribir código nuevo para una
interfaz y un comportamiento definidos? Esta no es una pregunta capciosa. Elijo este último.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 251 251
Es posible que empiece a preocuparse por el exceso de código ahora. Realmente, sin embargo, la capa
se convierte en el lugar donde se aísla gran parte de la duplicación en torno al uso de un sistema operativo.
Esta duplicación no tiene que imponer una gran sobrecarga. Si define un OSAL, también puede alentar a
sus aplicaciones a tener una estructura común. Puede proporcionar mecanismos de paso de mensajes, en
lugar de tener cada subproceso a mano su modelo de concurrencia.
OSAL puede ayudar a proporcionar puntos de prueba para que el valioso código de la aplicación en la capa
de software pueda probarse fuera del objetivo y fuera del sistema operativo. El software de una arquitectura
integrada limpia se puede probar fuera del sistema operativo de destino. Un OSAL exitoso proporciona esa
costura o conjunto de puntos de sustitución que facilitan las pruebas fuera del objetivo.
PROGRAMACIÓN A INTERFACES Y
SUSTITUCIÓN
Además de agregar una HAL y potencialmente una OSAL dentro de cada una de las capas principales (software,
sistema operativo, firmware y hardware), puede y debe aplicar los principios descritos a lo largo de este libro.
Estos principios fomentan la separación de preocupaciones, la programación de interfaces y la sustitución.
Una regla general básica es usar archivos de encabezado como definiciones de interfaz. Sin embargo, cuando
lo haga, debe tener cuidado con lo que se incluye en el archivo de encabezado. Limite el contenido del archivo
de encabezado a las declaraciones de funciones, así como a las constantes y los nombres de estructuras que
necesita la función.
No abarrote los archivos de encabezado de la interfaz con estructuras de datos, constantes y definiciones de
tipo que solo necesita la implementación. No es solo una cuestión de desorden: ese desorden conducirá a
dependencias no deseadas. Limite la visibilidad de los detalles de implementación. Espere que los detalles de
implementación cambien. Cuantos menos lugares donde el código conozca los detalles, menos lugares donde
el código tendrá que ser rastreado y modificado.
Una arquitectura integrada limpia es comprobable dentro de las capas porque los módulos interactúan
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 252 252
a través de interfaces. Cada interfaz proporciona ese punto de unión o sustitución que facilita las
pruebas fuera del objetivo.
Un uso de la sustituibilidad que a menudo se pasa por alto se relaciona con la forma en que los programas
integrados C y C++ manejan diferentes objetivos o sistemas operativos. Hay una tendencia a utilizar la
compilación condicional para activar y desactivar segmentos de código. Recuerdo un caso especialmente
problemático en el que la declaración #ifdef BOARD_V2 se mencionó varios miles de veces en una
aplicación de telecomunicaciones.
Esta repetición de código viola el principio Don't Repeat Yourself (DRY) .5 Si veo #ifdef BOARD_V2 una
vez, no es realmente un problema. Seis mil veces es un problema extremo. La compilación condicional que
identifica el tipo de hardware de destino a menudo se repite en los sistemas integrados. Pero, ¿Qué más
podemos hacer?
¿Qué pasa si hay una capa de abstracción de hardware? El tipo de hardware se convertiría en un detalle
oculto bajo el HAL. Si la HAL proporciona un conjunto de interfaces, en lugar de usar la compilación
condicional, podríamos usar el enlazador o alguna forma de enlace de tiempo de ejecución para conectar
el software al hardware.
CONCLUSIÓN
Las personas que desarrollan software integrado tienen mucho que aprender de las experiencias fuera del
software integrado. Si usted es un desarrollador integrado que ha leído este libro, encontrará una gran
cantidad de sabiduría en el desarrollo de software en las palabras y las ideas.
Dejar que todo el código se convierta en firmware no es bueno para la salud a largo plazo de su producto.
Ser capaz de probar solo en el hardware de destino no es bueno para la salud a largo plazo de su
producto. Una arquitectura integrada limpia es buena para la salud a largo plazo de su producto.
1. https://insights.sei.cmu.edu/sei_blog/2011/08/the-creciente-importancia-del-software-sostenible
para-thedod.html
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 253 253
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 254 254
NOSOTROS
DETALLES
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 255 255
30
LA BASE DE DATOS ES UN DETALLE
Desde un punto de vista arquitectónico, la base de datos no es una entidad, es un detalle que
no alcanza el nivel de un elemento arquitectónico. Su relación con la arquitectura de un
sistema de software es como la relación de un picaporte con la arquitectura de su hogar.
Me doy cuenta de que estas son palabras de pelea. Créeme, he tenido la pelea. Así que
déjame ser claro: no estoy hablando del modelo de datos. La estructura que le da a los datos
dentro de su aplicación es muy importante para la arquitectura de su sistema. Pero la base de
datos no es el modelo de datos. La base de datos es una pieza de software. La base de datos
es una utilidad que proporciona acceso a los datos. Desde el punto de vista de la arquitectura,
esa utilidad es irrelevante porque es un detalle de bajo nivel, un mecanismo. Y un buen arquitecto
no permite que los mecanismos de bajo nivel contaminen la arquitectura del sistema.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 256 256
Edgar Codd definió los principios de las bases de datos relacionales en 1970. A mediados de la década de 1980,
el modelo relacional había crecido hasta convertirse en la forma dominante de almacenamiento de datos. Había
una buena razón para esta popularidad: el modelo relacional es elegante, disciplinado y robusto. Es una excelente
tecnología de acceso y almacenamiento de datos.
Pero no importa cuán brillante, útil y matemáticamente sólida sea una tecnología, sigue siendo solo una tecnología.
Y eso significa que es un detalle.
Si bien las tablas relacionales pueden ser convenientes para ciertas formas de acceso a los datos, no hay nada
arquitectónicamente significativo en organizar los datos en filas dentro de las tablas. Los casos de uso de su
aplicación no deben saber ni preocuparse por tales asuntos.
De hecho, el conocimiento de la estructura tabular de los datos debe restringirse a las funciones de utilidad
de nivel más bajo en los círculos exteriores de la arquitectura.
Muchos marcos de acceso a datos permiten que las filas y tablas de la base de datos pasen por el sistema
como objetos. Permitir esto es un error arquitectónico. Combina los casos de uso, las reglas comerciales y, en
algunos casos, incluso la interfaz de usuario con la estructura relacional de los datos.
¿Por qué los sistemas de software y las empresas de software están dominados por sistemas de bases de datos?
¿Qué explica la preeminencia de Oracle, MySQL y SQL Server? En una palabra: discos.
El disco magnético giratorio fue el pilar del almacenamiento de datos durante cinco décadas.
Varias generaciones de programadores no han conocido otra forma de almacenamiento de datos. La tecnología
de discos ha pasado de enormes pilas de platos masivos de 48 pulgadas de diámetro que pesaban miles de libras
y tenían una capacidad de 20 megabytes, a círculos delgados individuales, de 3 pulgadas de diámetro, que
pesaban solo unos gramos y tenían una capacidad de un terabyte o más. Ha sido un viaje salvaje. Y a lo largo de
ese viaje, los programadores se han visto afectados por un rasgo fatal de la tecnología de discos: los discos son
lentos.
En un disco, los datos se almacenan en pistas circulares. Esas pistas se dividen en sectores que contienen una
cantidad conveniente de bytes, a menudo 4K. Cada plato puede tener cientos de pistas y puede haber una docena
de platos. Si desea leer un byte en particular
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 257 257
fuera del disco, debe mover la cabeza a la pista adecuada, esperar a que el disco gire al sector adecuado, leer
todos los 4K de ese sector en la RAM y luego indexar en ese búfer de RAM para obtener el byte que desea. Y todo
eso lleva tiempo, milisegundos de tiempo.
Puede que los milisegundos no parezcan mucho, pero un milisegundo es un millón de veces más largo que el
tiempo de ciclo de la mayoría de los procesadores. Si esos datos no estuvieran en un disco, se podría acceder
a ellos en nanosegundos, en lugar de milisegundos.
Para mitigar el retraso de tiempo impuesto por los discos, necesita índices, cachés y esquemas de
consulta optimizados; y necesita algún tipo de medio regular para representar los datos para que estos índices,
cachés y esquemas de consulta sepan con qué están trabajando. En resumen, necesita un sistema de acceso y
gestión de datos. A lo largo de los años, estos sistemas se han dividido en dos tipos distintos: sistemas de archivos
y sistemas de administración de bases de datos relacionales (RDBMS).
Los sistemas de archivos están basados en documentos. Proporcionan una forma natural y conveniente de
almacenar documentos completos. Funcionan bien cuando necesita guardar y recuperar un conjunto de
documentos por nombre, pero no ofrecen mucha ayuda cuando busca el contenido de esos documentos. Es fácil
encontrar un archivo llamado login.c, pero es difícil y lento encontrar todos los archivos .c que tienen una variable
llamada x .
Los sistemas de bases de datos se basan en el contenido. Proporcionan una forma natural y conveniente de
encontrar registros en función de su contenido. Son muy buenos para asociar varios registros en función de una
parte del contenido que todos comparten. Desafortunadamente, son bastante deficientes para almacenar y
recuperar documentos opacos.
Ambos sistemas organizan los datos en el disco para que puedan almacenarse y recuperarse de la manera más
eficiente posible, dadas sus necesidades de acceso particulares. Cada uno tiene su propio esquema para indexar
y organizar los datos. Además, cada uno trae eventualmente los datos relevantes a la RAM, donde se pueden
manipular rápidamente.
¿Y SI NO HAY DISCO?
A pesar de lo frecuentes que alguna vez fueron los discos, ahora son una especie en extinción. Pronto habrán
seguido el camino de las unidades de cinta, las unidades de disquete y los CD. Están siendo reemplazados
por RAM.
Hágase esta pregunta: cuando todos los discos se hayan ido y todos sus datos estén almacenados en
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 258 258
RAM, ¿cómo organizará esos datos? ¿Lo organizará en tablas y accederá a él con SQL? ¿Lo organizará
en archivos y accederá a él a través de un directorio?
Por supuesto que no. Lo organizará en listas vinculadas, árboles, tablas hash, pilas, colas o cualquiera
de las otras innumerables estructuras de datos, y accederá a él mediante punteros o referencias,
porque eso es lo que hacen los programadores.
De hecho, si piensas detenidamente en este tema, te darás cuenta de que esto es lo que ya haces.
Aunque los datos se guardan en una base de datos o en un sistema de archivos, los lee en la RAM
y luego los reorganiza, para su propia conveniencia, en listas, conjuntos, pilas, colas, árboles o
cualquier estructura de datos que se le antoje. Es muy poco probable que dejes los datos en forma de
archivos o tablas.
DETALLES
Esta realidad es la razón por la que digo que la base de datos es un detalle. Es solo un mecanismo que
usamos para mover los datos de un lado a otro entre la superficie del disco y la RAM. La base de datos
no es más que un gran cubo de bits donde almacenamos nuestros datos a largo plazo. Pero rara vez
usamos los datos en esa forma.
Por lo tanto, desde un punto de vista arquitectónico, no debería importarnos la forma que toman los
datos mientras están en la superficie de un disco magnético giratorio. De hecho, no debemos
reconocer que el disco existe en absoluto.
¿No es el rendimiento una preocupación arquitectónica? Por supuesto que lo es, pero cuando se
trata de almacenamiento de datos, es una preocupación que puede encapsularse por completo y
separarse de las reglas comerciales. Sí, necesitamos que los datos entren y salgan del almacén de
datos rápidamente, pero eso es una preocupación de bajo nivel. Podemos abordar esa preocupación
con mecanismos de acceso a datos de bajo nivel. No tiene nada que ver con la arquitectura general de
nuestros sistemas.
ANÉCDOTA
A fines de la década de 1980, dirigí un equipo de ingenieros de software en una empresa nueva que
intentaba construir y comercializar un sistema de gestión de red que medía la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 259 259
integridad de las comunicaciones de las líneas de telecomunicaciones T1. El sistema recuperó datos
de los dispositivos en los extremos de esas líneas y luego ejecutó una serie de algoritmos predictivos
para detectar e informar problemas.
Contratamos a un gerente de marketing para esta puesta en marcha, un tipo agradable y bien
informado. Pero inmediatamente me dijo que teníamos que tener una base de datos relacional en el
sistema. No era una opción y no era un problema de ingeniería, era un problema de marketing.
Esto no tuvo sentido para mí. ¿Por qué demonios querría reorganizar mis listas y árboles
vinculados en un montón de filas y tablas a las que se accede a través de SQL? ¿Por qué introduciría
todos los gastos generales y gastos de un RDBMS masivo cuando un simple sistema de archivos de
acceso aleatorio era más que suficiente? Así que luché contra él, con uñas y dientes.
Teníamos un ingeniero de hardware en esta empresa que retomó el canto RDBMS. Se convenció
de que nuestro sistema de software necesitaba un RDBMS por razones técnicas. Tenía reuniones
a mis espaldas con los ejecutivos de la empresa, dibujaba figuras de palitos en la pizarra de una
casa balanceándose sobre un poste, y les preguntaba a los ejecutivos: “¿Construirían una casa
sobre un poste?” Su mensaje implícito fue que un RDBMS que mantiene sus tablas en archivos
de acceso aleatorio era de alguna manera más confiable que los archivos de acceso aleatorio que
estábamos usando.
Luché contra él. Luché contra el tipo de marketing. Me aferré a mis principios de ingeniería frente a
una ignorancia increíble. Luché, y luché, y luché.
Al final, el desarrollador de hardware fue ascendido por encima de mi cabeza para convertirse
en el administrador de software. Al final, pusieron un RDBMS en ese pobre sistema. Y, al final, ellos
tenían toda la razón y yo estaba equivocado.
No por razones de ingeniería, fíjate: tenía razón en eso. Tenía razón al luchar contra la instalación
de un RDBMS en el núcleo arquitectónico del sistema. La razón por la que me equivoqué fue
porque nuestros clientes esperaban que tuviéramos una base de datos relacional.
No sabían qué harían con él. No tenían ninguna forma realista de usar los datos relacionales en
nuestro sistema. Pero no importó: nuestros clientes esperaban un RDBMS. Se había convertido en
una casilla de verificación que todos los compradores de software tenían en su lista. No había una
lógica de ingeniería: la racionalidad había
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 260 260
Nada que ver con eso. Era una necesidad irracional, externa y completamente infundada, pero no menos
real.
¿De dónde vino esa necesidad? Se originó a partir de las campañas de marketing altamente efectivas
empleadas por los proveedores de bases de datos en ese momento. Habían logrado convencer a
ejecutivos de alto nivel de que sus "activos de datos" corporativos necesitaban protección y que los
sistemas de bases de datos que ofrecían eran los medios ideales para brindar esa protección.
Hoy vemos el mismo tipo de campañas de marketing. La palabra “empresa” y la noción de “Arquitectura
Orientada a Servicios” tienen mucho más que ver con el marketing que con la realidad.
¿Qué debería haber hecho en ese escenario de hace mucho tiempo? Debería haber instalado un RDBMS
en el costado del sistema y proporcionarle un canal de acceso a datos estrecho y seguro, mientras
mantenía los archivos de acceso aleatorio en el núcleo del sistema. ¿Qué hice ? Renuncié y me convertí
en consultor.
CONCLUSIÓN
La estructura organizativa de los datos, el modelo de datos, es arquitectónicamente significativo.
Las tecnologías y los sistemas que mueven datos dentro y fuera de una superficie magnética giratoria no
lo son. Los sistemas de bases de datos relacionales que obligan a que los datos se organicen en tablas y
se acceda a ellos con SQL tienen mucho más que ver con lo segundo que con lo primero.
Los datos son significativos. La base de datos es un detalle.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 261 261
31
LA WEB ES UN DETALLE
En realidad, la web no cambió nada. O, al menos, no debería haberlo hecho. La web es solo la
última de una serie de oscilaciones por las que ha pasado nuestra industria desde la década de
1960. Estas oscilaciones van y vienen entre colocar toda la energía de la computadora en los
servidores centrales y apagar toda la energía de la computadora en las terminales.
Hemos visto varias de estas oscilaciones solo en la última década desde que la web se hizo
prominente. Al principio pensamos que todo el poder de la computadora estaría en las granjas
de servidores y que los navegadores serían estúpidos. Luego empezamos a poner applets en
los navegadores. Pero eso no nos gustó, así que volvimos a mover el contenido dinámico a los servidores.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 262 262
Pero eso no nos gustó, así que inventamos la Web 2.0 y trasladamos una gran cantidad de procesamiento al
navegador con Ajax y JavaScript. Llegamos tan lejos como para crear enormes aplicaciones escritas para ejecutarse
en los navegadores. Y ahora todos estamos entusiasmados con la posibilidad de volver a colocar ese JavaScript en
el servidor con Node.
(Suspiro.)
Y así va la historia. Parece que no podemos averiguar dónde queremos la potencia de la computadora. Vamos y
venimos entre centralizarlo y distribuirlo. Y me imagino que esas oscilaciones continuarán por algún tiempo más.
Cuando lo miras en el ámbito general de la historia de TI, la web no cambió nada en absoluto. La web fue
simplemente una de las muchas oscilaciones en una lucha que comenzó antes de que la mayoría de nosotros naciera y
continuará mucho después de que la mayoría de nosotros nos hayamos jubilado.
Sin embargo, como arquitectos, tenemos que mirar a largo plazo. Esas oscilaciones son solo problemas a corto
plazo que queremos alejar del núcleo central de nuestras reglas comerciales.
Déjame contarte la historia de la empresa Q. La empresa Q creó un sistema de finanzas personales muy popular.
Era una aplicación de escritorio con una GUI muy útil. Me encantó usarlo.
Luego vino la web. En su próximo lanzamiento, la empresa Q cambió la GUI para que se viera y se comportara
como un navegador. ¡Estaba estupefacto! ¿Qué genio del marketing decidió que el software de finanzas personales,
que se ejecuta en una computadora de escritorio, debería tener la apariencia de un navegador web?
Por supuesto, odiaba la nueva interfaz. Aparentemente, todos los demás también lo hicieron, porque después de
algunos lanzamientos, la compañía Q eliminó gradualmente la sensación de navegador y convirtió su sistema de
finanzas personales nuevamente en una GUI de escritorio normal.
Ahora imagina que eres un arquitecto de software en Q. Imagina que algo de marketing
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 263 263
el genio convence a la alta dirección de que toda la interfaz de usuario tiene que cambiar para
parecerse más a la web. ¿A qué te dedicas? O, mejor dicho, ¿qué debería haber hecho antes de
este punto para proteger su aplicación de ese genio del marketing?
No soy un experto en el software dentro de ese dispositivo, así que no sé si ese cambio causó
dificultades significativas para los programadores de las aplicaciones que se ejecutan en el
teléfono de la empresa A. Espero que los arquitectos de A y los arquitectos de las aplicaciones
mantengan su interfaz de usuario y sus reglas comerciales aisladas entre sí, porque siempre hay
genios del marketing esperando para aprovechar el siguiente acoplamiento que cree.
EL RESULTADO
El resultado es simplemente este: la GUI es un detalle. La web es una GUI. Así que la web es
un detalle. Y, como arquitecto, desea colocar detalles como ese detrás de límites que los
mantengan separados de la lógica comercial principal.
Piénselo de esta manera: la WEB es un dispositivo IO. En la década de 1960, aprendimos el valor
de escribir aplicaciones que fueran independientes del dispositivo. La motivación de esa
independencia no ha cambiado. La web no es una excepción a esa regla.
¿O es eso? Se puede argumentar que una GUI, como la web, es tan única y rica que es absurdo
buscar una arquitectura independiente del dispositivo. Cuando piensa en las complejidades de la
validación de JavaScript o las llamadas AJAX de arrastrar y soltar, o cualquiera de la gran cantidad
de otros widgets y dispositivos que puede colocar en una página web, es fácil argumentar que la
independencia del dispositivo no es práctica.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 264 264
"hablador" en formas que son bastante específicas para el tipo de GUI que tiene. El baile entre
un navegador y una aplicación web es diferente del baile entre una GUI de escritorio y su
aplicación. Tratar de abstraer ese baile, de la misma forma en que se abstraen los dispositivos de
UNIX, parece poco posible.
Pero se puede abstraer otro límite entre la interfaz de usuario y la aplicación . La lógica
empresarial se puede considerar como un conjunto de casos de uso, cada uno de los cuales realiza
alguna función en nombre de un usuario. Cada caso de uso se puede describir en función de los datos
de entrada, el procesamiento realizado y los datos de salida.
En algún punto del baile entre la interfaz de usuario y la aplicación, se puede decir que los datos de
entrada están completos, lo que permite ejecutar el caso de uso. Al finalizar, los datos resultantes se
pueden retroalimentar en el baile entre la interfaz de usuario y la aplicación.
Los datos de entrada completos y los datos de salida resultantes pueden colocarse en
estructuras de datos y usarse como valores de entrada y valores de salida para un proceso que
ejecuta el caso de uso. Con este enfoque, podemos considerar que cada caso de uso opera el
dispositivo IO de la interfaz de usuario de manera independiente del dispositivo.
CONCLUSIÓN
Este tipo de abstracción no es fácil, y probablemente tomará varias iteraciones para hacerlo bien.
Pero es posible. Y dado que el mundo está lleno de genios del marketing, no es difícil argumentar
que a menudo es muy necesario.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 265 265
32
LOS MARCOS SON DETALLES
Los marcos se han vuelto bastante populares. En términos generales, esto es algo bueno.
Existen muchos marcos que son gratuitos, potentes y útiles.
Sin embargo, los marcos no son arquitecturas, aunque algunos intentan serlo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 266 266
Los autores del marco conocen sus propios problemas y los problemas de sus compañeros
de trabajo y amigos. Y escriben sus marcos para resolver esos problemas, no los tuyos.
Por supuesto, es probable que sus problemas se superpongan bastante con esos otros
problemas. Si este no fuera el caso, los marcos no serían tan populares. En la medida en
que exista tal superposición, los marcos pueden ser muy útiles.
MATRIMONIO ASIMÉTRICO
La relación entre usted y el autor del marco es extraordinariamente asimétrica.
Debe hacer un gran compromiso con el marco, pero el autor del marco no se
compromete con usted en absoluto.
Piense en este punto cuidadosamente. Cuando usa un marco, lee la documentación que
proporciona el autor de ese marco. En esa documentación, el autor y otros usuarios de ese
marco le aconsejan sobre cómo integrar su software con el marco. Por lo general, esto
significa envolver su arquitectura en torno a ese marco. El autor recomienda derivar de las
clases base del marco e importar las funciones del marco a sus objetos comerciales. El autor
lo insta a acoplar su aplicación al marco lo más estrechamente posible.
Es más, el autor quiere que te acoples al armazón, porque una vez acoplado de
esta forma, es muy difícil que se separe. Nada se siente más validador para un autor de
marco que un grupo de usuarios dispuestos a derivar inextricablemente de las clases base
del autor.
En efecto, el autor le está pidiendo que se case con el marco, que haga un gran
compromiso a largo plazo con ese marco. Y sin embargo, bajo ninguna circunstancia el
autor asumirá un compromiso correspondiente con usted. Es un matrimonio unidireccional.
Usted asume todo el riesgo y la carga; el autor del marco no asume nada en absoluto.
LOS RIESGOS
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 267 267
¿Cuáles son los riesgos? Aquí hay algunos para que los consideres.
• La arquitectura del marco a menudo no es muy limpia. Los frameworks tienden a violar la Regla
de Dependencia. Le piden que herede su código en sus objetos comerciales: ¡sus Entidades!
Quieren que su marco se acople a ese círculo más interno. Una vez dentro, ese marco no vuelve a
salir. El anillo de bodas está en tu dedo; y se va a quedar ahí. • El marco puede ayudarlo con algunas
características iniciales de su aplicación.
Sin embargo, a medida que su producto madure, es posible que supere las instalaciones del marco.
Si te has puesto ese anillo de bodas, encontrarás que el marco te pelea cada vez más a medida
que pasa el tiempo. • El marco puede evolucionar en una dirección que no encuentre útil. Es
posible que se quede atascado actualizando a nuevas versiones que no lo ayuden. Incluso puede
encontrar características antiguas, que usó, desapareciendo o cambiando de manera que le resulte
difícil mantenerse al día.
LA SOLUCIÓN
¿Cuál es la solución?
Si el marco quiere que derive sus objetos comerciales de sus clases base, ¡diga que no! En su lugar,
obtenga proxies y conserve esos proxies en componentes que sean complementos para sus reglas
comerciales.
No permita que los marcos entren en su código central. En su lugar, intégrelos en componentes que
se conectan a su código central, siguiendo la regla de dependencia.
Por ejemplo, tal vez te guste Spring. Spring es un buen marco de inyección de dependencia.
Tal vez use Spring para conectar automáticamente sus dependencias. Eso está bien, pero no debe
esparcir anotaciones @autowired en todos sus objetos comerciales. Sus objetos de negocio no
deben conocer Spring.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 268 268
AHORA TE PRONUNCIAR…
Hay algunos marcos con los que simplemente debes casarte. Si está utilizando C++, por
ejemplo, probablemente tendrá que casarse con STL, es difícil de evitar. Si está utilizando
Java, es casi seguro que tendrá que casarse con la biblioteca estándar.
Eso es normal, pero aún debe ser una decisión. Debe comprender que cuando une un marco
con su aplicación, se quedará con ese marco durante el resto del ciclo de vida de esa
aplicación. Para bien o para mal, en la enfermedad y en la salud, en la riqueza o en la pobreza,
renunciando a todos los demás, estarás usando ese marco.
Este no es un compromiso que deba asumirse a la ligera.
CONCLUSIÓN
Cuando se enfrente a un marco, trate de no casarse con él de inmediato. Vea si no hay
formas de salir con él por un tiempo antes de dar el paso. Mantenga el marco detrás de un
límite arquitectónico si es posible, durante el mayor tiempo posible. Tal vez puedas encontrar
una manera de obtener la leche sin comprar la vaca.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 269 269
33
ESTUDIO DE CASO : VENTA DE VÍDEO
EL PRODUCTO
Para este estudio de caso, he elegido un producto con el que estoy íntimamente
familiarizado: el software para un sitio web que vende videos. Por supuesto, recuerda a
cleancoders.com, el sitio donde vendo mis videos tutoriales de software.
La idea básica es trivial. Tenemos un lote de videos que queremos vender. Los vendemos, en la
web, tanto a particulares como a empresas. Las personas pueden pagar un precio para transmitir
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 270 270
los videos, y otro precio más alto para descargar esos videos y poseerlos permanentemente.
Las licencias comerciales son solo de transmisión y se compran en lotes que permiten descuentos por
cantidad.
Los individuos suelen actuar como espectadores y compradores. Las empresas, por el contrario,
a menudo tienen personas que compran los videos que otras personas verán.
Los autores de videos deben proporcionar sus archivos de video, descripciones escritas y archivos
auxiliares con exámenes, problemas, soluciones, código fuente y otros materiales.
Los administradores deben agregar nuevas series de videos, agregar y eliminar videos de la serie y
establecer precios para varias licencias.
Nuestro primer paso para determinar la arquitectura inicial del sistema es identificar los actores y los
casos de uso.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 271 271
Los cuatro actores principales son evidentes. De acuerdo con el Principio de Responsabilidad
Única, estos cuatro actores serán las cuatro fuentes principales de cambio para el sistema. Cada
vez que se agrega alguna característica nueva, o se cambia alguna característica existente, se dará
ese paso para servir a uno de estos actores. Por lo tanto, queremos dividir el sistema de manera que
un cambio en un actor no afecte a ninguno de los otros actores.
Los casos de uso que se muestran en la Figura 33.1 no son una lista completa. Por ejemplo, no
encontrará casos de uso de inicio o cierre de sesión. La razón de esta omisión es simplemente
manejar el tamaño del problema en este libro. Si tuviera que incluir todos los diferentes casos de uso, entonces
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 272 272
Tenga en cuenta los casos de uso discontinuos en el centro de la Figura 33.1. Son casos de uso abstractos1 .
Un caso de uso abstracto es aquel que establece una política general que se desarrollará en otro caso de
uso. Como puede ver, los casos de uso Ver catálogo como espectador y Ver catálogo como comprador se
heredan del caso de uso abstracto Ver catálogo .
Por un lado, no era estrictamente necesario para mí crear esa abstracción. Podría haber dejado el caso
de uso abstracto fuera del diagrama sin comprometer ninguna de las características del producto en general.
Por otro lado, estos dos casos de uso son tan similares que pensé que sería prudente reconocer la similitud y
encontrar una manera de unificarla al principio del análisis.
ARQUITECTURA DE COMPONENTES
Ahora que conocemos los actores y los casos de uso, podemos crear una arquitectura de componentes
preliminar (Figura 33.2).
Las líneas dobles en el dibujo representan límites arquitectónicos como de costumbre. Puede ver la partición
típica de vistas, presentadores, interactuadores y controladores. También puede ver que he dividido cada una
de esas categorías por sus actores correspondientes.
Cada uno de los componentes de la Figura 33.2 representa un archivo .jar potencial o un archivo .dll .
Cada uno de esos componentes contendrá las vistas, los presentadores, los interactuadores y los
controladores que se le han asignado.
Tenga en cuenta los componentes especiales para la vista de catálogo y el presentador de catálogo.
Así es como traté el caso de uso abstracto de View Catalog . Supongo que esas vistas y presentadores
se codificarán en clases abstractas dentro de esos componentes, y que los componentes heredados
contendrán clases de vista y presentador que heredarán de esas clases abstractas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 273 273
¿Realmente dividiría el sistema en todos estos componentes y los entregaría como archivos .jar
o .dll ? Si y no. Ciertamente rompería el entorno de compilación y construcción de esta manera,
para poder construir entregas independientes como esa. También me reservaría el derecho de
combinar todos esos entregables en un número menor de entregables si fuera necesario. Por ejemplo,
dada la partición de la figura 33.2, sería fácil combinarlos en cinco archivos .jar : uno para vistas,
presentadores, interactuadores, controladores y utilidades, respectivamente. Luego, podría implementar
de forma independiente los componentes que tienen más probabilidades de cambiar de forma
independiente entre sí.
Otra posible agrupación sería juntar las vistas y los presentadores en el mismo archivo .jar y colocar los
interactuadores, controladores y utilidades en su propio archivo .jar . Otro agrupamiento aún más
primitivo sería crear dos archivos .jar , con vistas y presentadores en un archivo, y todo lo demás en el
otro.
Mantener estas opciones abiertas nos permitirá adaptar la forma en que implementamos el sistema
en función de cómo cambia el sistema con el tiempo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 274 274
GESTIÓN DE DEPENDENCIA
El flujo de control en la Figura 33.2 procede de derecha a izquierda. La entrada ocurre en los
controladores, y esa entrada es procesada en un resultado por los interactuadores. Luego, los
presentadores dan formato a los resultados y las vistas muestran esas presentaciones.
Observe que no todas las flechas fluyen de derecha a izquierda. De hecho, la mayoría de ellos
apuntan de izquierda a derecha. Esto se debe a que la arquitectura sigue la regla de dependencia.
Todas las dependencias cruzan las líneas de límite en una dirección y siempre apuntan hacia los
componentes que contienen la política de nivel superior.
Observe también que las relaciones de uso (flechas abiertas) apuntan con el flujo de control y que
las relaciones de herencia (flechas cerradas) apuntan contra el flujo de control. Esto describe
nuestro uso del principio abierto-cerrado para garantizar que las dependencias fluyan en la dirección
correcta y que los cambios en los detalles de bajo nivel no se propaguen hacia arriba para afectar
las políticas de alto nivel.
CONCLUSIÓN
El diagrama de arquitectura de la figura 33.2 incluye dos dimensiones de separación. El primero es
la separación de actores basada en el Principio de Responsabilidad Única; la segunda es la regla
de dependencia. El objetivo de ambos es separar los componentes que cambian por diferentes
razones y a diferentes velocidades. Los diferentes motivos corresponden a los actores; las diferentes
tasas corresponden a los diferentes niveles de póliza.
Una vez que haya estructurado el código de esta manera, puede mezclar y combinar cómo desea
implementar el sistema. Puede agrupar los componentes en entregables implementables de
cualquier manera que tenga sentido y cambiar fácilmente esa agrupación cuando cambien las
condiciones.
1. Esta es mi propia notación para casos de uso "abstractos". Habría sido más estándar usar un
estereotipo UML como <<abstracto>>, pero adherirse a tales estándares no me parece muy útil hoy
en día.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 275 275
34
EL CAPÍTULO PERDIDO
Todos los consejos que ha leído hasta ahora sin duda lo ayudarán a diseñar un mejor
software, compuesto de clases y componentes con límites bien definidos,
responsabilidades claras y dependencias controladas. Pero resulta que el diablo está en
los detalles de implementación, y es muy fácil caer en el último obstáculo si no piensas en
eso también.
Imaginemos que estamos construyendo una librería en línea, y uno de los casos de
uso que nos han pedido que implementemos es que los clientes puedan ver el estado de
sus pedidos. Aunque este es un ejemplo de Java, los principios se aplican igualmente a
otros lenguajes de programación. Dejemos la arquitectura limpia a un lado por un momento
y veamos una serie de enfoques para el diseño y la organización del código.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 276 276
En esta típica arquitectura en capas, tenemos una capa para el código web, una capa para
nuestra "lógica comercial" y una capa para la persistencia. En otras palabras, el código se corta
horizontalmente en capas, que se utilizan como una forma de agrupar tipos similares de cosas.
En una "arquitectura en capas estricta", las capas deben depender solo de la siguiente capa
inferior adyacente. En Java, las capas normalmente se implementan como paquetes. Como
puede ver en la Figura 34.1, todas las dependencias entre capas (paquetes) apuntan hacia abajo.
En este ejemplo, tenemos los siguientes tipos de Java:
• OrdersController: un controlador web, algo así como un controlador Spring MVC, que
maneja las solicitudes de la web. • OrdersService: una interfaz que define la "lógica
comercial" relacionada con los pedidos. • OrdersServiceImpl: La implementación del servicio
de pedidos.1 • OrdersRepository: Una interfaz que define cómo obtenemos acceso a pedidos
persistentes
información.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 277 277
En “Presentation Domain Data Layering”,2 Martin Fowler dice que adoptar una arquitectura
en capas de este tipo es una buena manera de comenzar. Él no está solo. Muchos de los
libros, tutoriales, cursos de capacitación y código de muestra que encontrará también lo
guiarán por el camino de la creación de una arquitectura en capas. Es una forma muy
rápida de poner en marcha algo sin una gran cantidad de complejidad. El problema, como
señala Martin, es que una vez que su software crece en escala y complejidad, pronto se
dará cuenta de que tener tres cubos grandes de código no es suficiente y tendrá que
pensar en modularizar más.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 278 278
Otro problema es que, como ya ha dicho el tío Bob, una arquitectura en capas no grita
nada sobre el dominio empresarial. Ponga el código para dos arquitecturas en capas, de
dos dominios comerciales muy diferentes, uno al lado del otro y es probable que se vean
inquietantemente similares: web, servicios y repositorios. También hay otro gran problema con
las arquitecturas en capas, pero lo abordaremos más adelante.
Con este enfoque, como se muestra en la Figura 34.2, tenemos las mismas interfaces y clases
que antes, pero todas se ubican en un solo paquete Java en lugar de dividirse entre tres paquetes.
Esta es una refactorización muy simple del estilo "paquete por capa", pero la organización de
nivel superior del código ahora grita algo sobre el dominio comercial. Ahora podemos ver que
este código base tiene algo que ver con los pedidos en lugar de la web, los servicios y los
repositorios. Otro beneficio es que es potencialmente más fácil encontrar todo el código que
necesita modificar en caso de que cambie el caso de uso "ver pedidos". Todo se encuentra en
un solo paquete de Java en lugar de estar distribuido.3
A menudo veo que los equipos de desarrollo de software se dan cuenta de que tienen
problemas con las capas horizontales ("paquete por capa") y cambian a capas verticales
("paquete por característica"). En mi opinión, ambos son subóptimos. Si ha leído este libro hasta
ahora, es posible que esté pensando que podemos hacerlo mucho mejor, y tiene razón.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 279 279
PUERTOS Y ADAPTADORES
Como ha dicho el tío Bob, los enfoques como "puertos y adaptadores", la "arquitectura
hexagonal", "límites, controladores, entidades", etc. tienen como objetivo crear
arquitecturas en las que el código centrado en el negocio o el dominio sea independiente y
esté separado del código técnico. detalles de implementación como marcos y bases de datos.
Para resumir, a menudo verá tales bases de código compuestas por un "interior" (dominio) y
un "exterior" (infraestructura), como se sugiere en la Figura 34.3.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 280 280
La región "interior" contiene todos los conceptos de dominio, mientras que la región
"exterior" contiene las interacciones con el mundo exterior (por ejemplo, interfaces de
usuario, bases de datos, integraciones de terceros). La regla principal aquí es que el
“afuera” depende del “adentro”, nunca al revés. La figura 34.4 muestra una versión de cómo
podría implementarse el caso de uso "ver pedidos".
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 281 281
También vale la pena señalar que esta es una versión simplificada de cómo se vería el
diagrama de clases UML, porque le faltan cosas como interactores y objetos para ordenar
los datos a través de los límites de dependencia.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 282 282
sobre cómo organizar el código. Así que voy a presentar otra opción aquí, a la que llamo
"paquete por componente". Para brindarle algunos antecedentes, he pasado la mayor parte de mi
carrera creando software empresarial, principalmente en Java, en varios dominios comerciales
diferentes. Esos sistemas de software también han variado enormemente. Un gran número se ha
basado en la web, pero otros han sido cliente-servidor4 , distribuidos, basados más.
en mensajes
Aunque las
o algo
tecnologías diferían, el tema común era que la arquitectura de la mayoría de estos sistemas de
software se basaba en una arquitectura tradicional en capas.
Ya mencioné un par de razones por las que las arquitecturas en capas deberían considerarse
malas, pero esa no es toda la historia. El propósito de una arquitectura en capas es separar el
código que tiene el mismo tipo de función. Las cosas web están separadas de la lógica empresarial,
que a su vez está separada del acceso a los datos. Como vimos en el diagrama de clases de UML,
desde una perspectiva de implementación, una capa suele equivaler a un paquete de Java. Desde la
perspectiva de la accesibilidad del código, para que OrdersController pueda tener una dependencia
de la interfaz de OrdersService , la interfaz de OrdersService debe marcarse como pública, porque
están en paquetes diferentes.
Del mismo modo, la interfaz OrdersRepository debe marcarse como pública para que la clase
OrdersServiceImpl pueda verla fuera del paquete del repositorio .
En una arquitectura en capas estricta, las flechas de dependencia siempre deben apuntar
hacia abajo, y las capas dependen solo de la siguiente capa inferior adyacente. Esto vuelve a la
creación de un gráfico de dependencias acíclicas agradable, limpio, que se logra mediante la
introducción de algunas reglas sobre cómo los elementos en una base de código deben depender
entre sí. El gran problema aquí es que podemos hacer trampa introduciendo algunas dependencias
no deseadas, y aún así crear un buen gráfico de dependencia acíclica.
Suponga que contrata a alguien nuevo que se une a su equipo y le da al recién llegado
otro caso de uso relacionado con pedidos para implementar. Dado que la persona es nueva, quiere
causar una gran impresión e implementar este caso de uso lo más rápido posible. Después de
sentarse con una taza de café durante unos minutos, el recién llegado descubre una clase
OrdersController existente , por lo que decide que allí debe ir el código para la nueva página web
relacionada con los pedidos. Pero necesita algunos datos de pedidos de la base de datos. El recién
llegado tiene una epifanía: “Oh, también hay una interfaz de OrdersRepository ya construida.
Simplemente puedo inyectar la dependencia de la implementación en mi controlador. ¡Perfecto!"
Después de unos minutos más de pirateo, la página web está funcionando. Pero el diagrama UML
resultante se parece a la figura 34.5.
Las flechas de dependencia aún apuntan hacia abajo, pero OrdersController ahora también
omite OrdersService para algunos casos de uso. esta organización es
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 283 283
a menudo se denomina arquitectura en capas relajada, ya que las capas pueden saltar alrededor
de sus vecinos adyacentes. En algunas situaciones, este es el resultado previsto, si intenta seguir
el patrón CQRS5, por ejemplo. En muchos otros casos, pasar por alto la capa de lógica empresarial
no es deseable, especialmente si esa lógica empresarial es responsable de garantizar el acceso
autorizado a registros individuales, por ejemplo.
Si bien el nuevo caso de uso funciona, quizás no esté implementado de la manera que
esperábamos. Veo que esto sucede mucho con los equipos que visito como consultor, y
generalmente se revela cuando los equipos comienzan a visualizar cómo se ve realmente su base
de código, a menudo por primera vez.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 284 284
Lo que necesitamos aquí es una guía, un principio arquitectónico, que diga algo así como: "Los
controladores web nunca deben acceder a los repositorios directamente". La cuestión, por supuesto, es
la aplicación. Muchos equipos que he conocido simplemente dicen: "Hacemos cumplir este principio a
través de una buena disciplina y revisiones de código, porque confiamos en nuestros desarrolladores". Es
genial escuchar esta confianza, pero todos sabemos lo que sucede cuando los presupuestos y los plazos
comienzan a acercarse cada vez más.
Un número mucho menor de equipos me dice que utilizan herramientas de análisis estático (p.
ej., NDepend, Structure101, Checkstyle) para verificar y hacer cumplir automáticamente las infracciones de
la arquitectura en el momento de la construcción. Es posible que haya visto tales reglas usted mismo; por
lo general, se manifiestan como expresiones regulares o cadenas de caracteres comodín que indican "los
tipos en el paquete **/web no deben acceder a los tipos en **/datos"; y se ejecutan después del paso de
compilación.
Este enfoque es un poco crudo, pero puede funcionar, informando violaciones de los principios de
arquitectura que ha definido como equipo y (espera) fallando en la compilación.
El problema con ambos enfoques es que son falibles y el ciclo de retroalimentación es más largo de lo
que debería ser. Si no se controla, esta práctica puede convertir una base de código en una "gran bola
de barro" .6 Personalmente, me gustaría usar el compilador para reforzar mi arquitectura si es posible.
Esto nos lleva a la opción “paquete por componente”. Es un enfoque híbrido de todo lo que hemos
visto hasta ahora, con el objetivo de agrupar todas las responsabilidades relacionadas con un solo
componente de granularidad gruesa en un solo paquete de Java. Se trata de tener una visión centrada
en el servicio de un sistema de software, que es algo que también estamos viendo con las arquitecturas
de microservicio. De la misma manera que los puertos y adaptadores tratan la web como un mecanismo
de entrega más, el "paquete por componente" mantiene la interfaz de usuario separada de estos
componentes de granularidad gruesa. La figura 34.6 muestra cómo podría verse el caso de uso "ver
pedidos".
En esencia, este enfoque agrupa la "lógica comercial" y el código de persistencia en una sola cosa, a la
que llamo "componente". El tío Bob presentó su definición de "componente" anteriormente en el libro,
diciendo:
Los componentes son las unidades de implementación. Son las entidades más pequeñas que se
pueden implementar como parte de un sistema. En Java, son archivos jar.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 285 285
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 286 286
preocupación ortogonal.
Un beneficio clave del enfoque de "paquete por componente" es que si está escribiendo un código que
necesita hacer algo con los pedidos, solo hay un lugar a donde ir: el OrdersComponent. Dentro del
componente, aún se mantiene la separación de preocupaciones, por lo que la lógica comercial está
separada de la persistencia de los datos, pero ese es un detalle de implementación del componente
que los consumidores no necesitan conocer. Esto es similar a lo que podría terminar si adoptara una
Arquitectura Orientada a Servicios o microservicios, un OrdersService separado que encapsula todo
lo relacionado con el manejo de pedidos. La diferencia clave es el modo de desacoplamiento. Puede
pensar en componentes bien definidos en una aplicación monolítica como un trampolín hacia una
arquitectura de microservicios.
Algo que veo regularmente es un uso demasiado liberal del modificador de acceso público en
lenguajes como Java. Es casi como si nosotros, como desarrolladores, usáramos instintivamente la
palabra clave pública sin pensar. Está en nuestra memoria muscular. Si no me cree, eche un vistazo
a los ejemplos de código de libros, tutoriales y marcos de trabajo de código abierto en GitHub. Esta
tendencia es evidente, independientemente del estilo arquitectónico que pretenda adoptar una base
de código: capas horizontales, capas verticales, puertos y adaptadores, o cualquier otra cosa. Marcar
todos sus tipos como públicos significa que no está aprovechando las ventajas que ofrece su lenguaje
de programación con respecto a la encapsulación. En algunos casos, literalmente no hay nada que
impida que alguien escriba algún código para instanciar una clase de implementación concreta
directamente, violando el estilo de arquitectura previsto.
ORGANIZACIÓN VERSUS
ENCAPSULACIÓN
Mirando este problema de otra manera, si hace públicos todos los tipos en su aplicación Java,
los paquetes son simplemente un mecanismo de organización (una agrupación, como
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 287 287
carpetas), en lugar de usarse para la encapsulación. Dado que los tipos públicos se pueden
usar desde cualquier lugar en una base de código, puede ignorar los paquetes de manera
efectiva porque brindan muy poco valor real. El resultado neto es que si ignora los paquetes
(porque no proporcionan ningún medio de encapsulación y ocultación), en realidad no importa
qué estilo arquitectónico esté aspirando a crear. Si volvemos a mirar los diagramas UML de
ejemplo, los paquetes de Java se vuelven un detalle irrelevante si todos los tipos están marcados
como públicos. En esencia, los cuatro enfoques arquitectónicos presentados anteriormente en
este capítulo son exactamente iguales cuando abusamos de esta designación (Figura 34.7).
Mire de cerca las flechas entre cada uno de los tipos en la figura 34.7: todos son idénticos,
independientemente del enfoque arquitectónico que intente adoptar.
Conceptualmente los enfoques son muy diferentes, pero sintácticamente son idénticos.
Además, podría argumentar que cuando hace públicos todos los tipos, lo que realmente tiene
son solo cuatro formas de describir una arquitectura tradicional en capas horizontales. Este es
un buen truco y, por supuesto, nadie haría públicos todos sus tipos de Java. Excepto cuando lo
hacen. Y lo he visto.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 288 288
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 289 289
Figura 34.8 Los tipos sombreados son donde el modificador de acceso se puede hacer más restrictivo
Para que quede absolutamente claro, lo que he descrito aquí se relaciona con una
aplicación monolítica, donde todo el código reside en un solo árbol de código fuente. Si está
creando una aplicación de este tipo (y mucha gente lo está haciendo), sin duda lo alentaría a
apoyarse en el compilador para hacer cumplir sus principios arquitectónicos, en lugar de confiar en
la autodisciplina y las herramientas posteriores a la compilación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 290 290
Otra opción es desacoplar sus dependencias en el nivel del código fuente, dividiendo el código
en diferentes árboles de código fuente. Si tomamos el ejemplo de puertos y adaptadores, podríamos
tener tres árboles de código fuente:
• El código fuente para el negocio y el dominio (es decir, todo lo que es independiente de las opciones de
tecnología y marco): OrdersService, OrdersServiceImpl y
Pedidos
• El código fuente de la web: OrdersController
Los últimos dos árboles de código fuente tienen una dependencia en tiempo de compilación del
código comercial y de dominio, que en sí mismo no sabe nada sobre la web o el código de persistencia
de datos. Desde una perspectiva de implementación, puede hacer esto configurando módulos o
proyectos separados en su herramienta de compilación (por ejemplo, Maven, Gradle, MSBuild).
Idealmente, repetiría este patrón, teniendo un árbol de código fuente separado para todos y cada uno
de los componentes de su aplicación. Sin embargo, esta es en gran medida una solución idealista,
porque existen problemas de rendimiento, complejidad y mantenimiento del mundo real asociados con
la división del código fuente de esta manera.
Un enfoque más simple que algunas personas siguen para el código de sus puertos y adaptadores es
tener solo dos árboles de código fuente:
Esto se corresponde muy bien con el diagrama (Figura 34.9) que mucha gente usa para resumir la
arquitectura de puertos y adaptadores, y hay una dependencia en tiempo de compilación del
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 291 291
infraestructura al dominio.
Este enfoque para organizar el código fuente también funcionará, pero tenga en cuenta la
posible compensación. Es lo que yo llamo el “antipatrón Périphérique de puertos y adaptadores”.
La ciudad de París, Francia, tiene una carretera de circunvalación llamada Boulevard
Périphérique, que le permite circunnavegar París sin entrar en las complejidades de la ciudad.
Tener todo su código de infraestructura en un solo árbol de código fuente significa que es
potencialmente posible que el código de infraestructura en un área de su aplicación (por ejemplo,
un controlador web) llame directamente al código en otra área de su aplicación (por ejemplo, un
repositorio de base de datos) , sin navegar por el dominio. Esto es especialmente cierto si olvidó
aplicar los modificadores de acceso apropiados a ese código.
El objetivo de este capítulo es resaltar que sus mejores intenciones de diseño pueden destruirse
en un instante si no considera las complejidades de la implementación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 292 292
estrategia. Piense en cómo asignar su diseño deseado a estructuras de código, cómo organizar
ese código y qué modos de desacoplamiento aplicar durante el tiempo de ejecución y el tiempo
de compilación. Deje las opciones abiertas cuando corresponda, pero sea pragmático y tenga
en cuenta el tamaño de su equipo, su nivel de habilidad y la complejidad de la solución junto
con sus limitaciones de tiempo y presupuesto. Piense también en usar su compilador para
ayudarlo a hacer cumplir su estilo arquitectónico elegido y tenga cuidado con el acoplamiento
en otras áreas, como los modelos de datos. El diablo está en los detalles de implementación.
1. Podría decirse que esta es una manera horrible de nombrar una clase, pero como veremos más adelante, tal vez no
asunto.
2. https://martinfowler.com/bliki/PresentationDomainDataLayering.html.
3. Este beneficio es mucho menos relevante con las instalaciones de navegación de los IDE modernos, pero parece
ha habido un renacimiento volviendo a los editores de texto livianos, por razones que claramente soy demasiado viejo
para entender.
4. Mi primer trabajo después de graduarme de la universidad en 1996 fue construir un escritorio cliente-servidor
aplicaciones con una tecnología llamada PowerBuilder, un 4GL superproductivo que sobresalió en la creación de
aplicaciones basadas en bases de datos. Un par de años más tarde, estaba construyendo aplicaciones cliente-
servidor con Java, donde tuvimos que construir nuestra propia conectividad de base de datos (esto era anterior a JDBC) y
nuestros propios juegos de herramientas GUI sobre AWT. ¡Eso es “progreso” para usted!
5. En el patrón Command Query Responsibility Segregation , tiene patrones separados para actualizar y leer datos.
6. http://www.laputan.org/mud/ 7. Ver https://www.structurizr.com/help/c4 para más información.
8. En Java, por ejemplo, aunque tendemos a pensar que los paquetes son jerárquicos, no es posible crear
restricciones de acceso basadas en una relación de paquete y subpaquete. Cualquier jerarquía que cree está en
el nombre de esos paquetes y la estructura de directorios en el disco, únicamente.
9. A menos que hagas trampa y uses el mecanismo de reflexión de Java, ¡pero por favor no hagas eso!
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 293 293
VII
Apéndice
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 294 294
A
ARQUITECTURA ARQUEOLOGÍA
Para descubrir los principios de la buena arquitectura, hagamos un viaje de 45 años a través
de algunos de los proyectos en los que he trabajado desde 1970. Algunos de estos
proyectos son interesantes desde el punto de vista arquitectónico. Otros son interesantes
por las lecciones aprendidas y por cómo alimentaron proyectos posteriores.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 295 295
Local 705 de Teamsters Union para proporcionar un sistema de contabilidad. La computadora que ASC
eligió para implementar este sistema fue una GE Datanet 30, como se muestra en la Figura A.1.
Como puede ver en la imagen, esta era una máquina enorme1. Llenó una habitación, y la habitación
necesitaba controles ambientales estrictos.
Esta computadora fue construida en los días anteriores a los circuitos integrados. Fue construido a
partir de transistores discretos. Incluso había algunos tubos de vacío en él (aunque solo en los
amplificadores de sentido de las unidades de cinta).
Según los estándares actuales, la máquina era enorme, lenta, pequeña y primitiva. Tenía 16K × 18 bits de
núcleo, con un tiempo de ciclo de alrededor de 7 microsegundos.2 Llenó una gran sala con control
ambiental. Tenía unidades de cinta magnética de 7 pistas y una unidad de disco con una capacidad de 20
megabytes más o menos.
Ese disco era un monstruo. Puedes verlo en la imagen de la Figura A.2, pero eso no te da la
escala de la bestia. La parte superior de ese gabinete estaba sobre mi cabeza. Los platos tenían 36
pulgadas de diámetro y 3/8 de pulgada de espesor. Uno de los platos se muestra en la Figura A.3.
Ahora cuenta los platos en esa primera imagen. Hay más de una docena. Cada uno tenía su propio
brazo de búsqueda individual que era accionado por actuadores neumáticos. Podrías ver esas cabezas
de búsqueda moverse a través de los platos. El tiempo de búsqueda fue probablemente sobre
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 296 296
Cuando se encendía esta bestia, sonaba como un motor a reacción. El piso retumbaría y se
sacudiría hasta que se acelerara.3
El gran reclamo a la fama del Datanet 30 fue su capacidad para controlar una gran cantidad de
terminales asíncronos a una velocidad relativamente alta. Eso es exactamente lo que ASC necesitaba.
ASC tenía su sede en Lake Bluff, Illinois, 30 millas al norte de Chicago. La oficina del Local 705
estaba en el centro de Chicago. El sindicato quería que una docena más o menos de sus empleados
de ingreso de datos usaran terminales CRT4 (Figura A.4) para ingresar datos en el sistema.
Imprimirían informes en teletipos ASR35 (Figura A.5).
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 297 297
Figura A.3 Un plato de ese disco: 3/8 de pulgada de espesor, 36 pulgadas de diámetro
Los terminales CRT funcionaban a 30 caracteres por segundo. Esta era una tasa bastante buena para
fines de la década de 1960 porque los módems eran relativamente poco sofisticados en esos días.
ASC arrendó aproximadamente una docena de líneas telefónicas dedicadas y el doble de módems de
300 baudios de la compañía telefónica para conectar el Datanet 30 a estos terminales.
Estas computadoras no venían con sistemas operativos. Ni siquiera venían con sistemas de archivos. Lo
que obtuviste fue un ensamblador.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 298 298
El sistema original fue escrito en ensamblador por un consultor que de alguna manera
logró meter todo en 16K.
Como puede imaginar, esa gran Datanet 30 era una máquina costosa de operar y mantener. El
consultor de software que mantuvo el software en funcionamiento también era costoso.
Además, las minicomputadoras se estaban volviendo populares y eran mucho más baratas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 299 299
En 1971, cuando tenía 18 años, ASC nos contrató a mí ya dos de mis amigos geek para reemplazar todo el sistema
de contabilidad sindical por uno que estaba basado en una minicomputadora Varian 620/f (Figura A.6). La
computadora era barata. Éramos baratos. Así que parecía un buen negocio para ASC.
La máquina Varian tenía un bus de 16 bits y una memoria de 32K * 16 núcleos. Tenía un tiempo de ciclo de aproximadamente
1 microsegundo. Era mucho más poderoso que el Datanet 30. Usaba la exitosa tecnología de disco 2314 de IBM, lo que
nos permitía almacenar 30 megabytes en platos que tenían solo 14 pulgadas de diámetro y no podían explotar a través de
paredes de bloques de concreto.
Por supuesto, todavía no teníamos sistema operativo. Sin sistema de archivos. Sin lenguaje de alto nivel.
Todo lo que teníamos era un ensamblador. Pero nos las arreglamos.
El orfanato de minicomputadoras
Los programas se intercambiarían en el área de superposición, se ejecutarían lo suficiente para llenar la salida
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 300 300
Por supuesto, cuando su interfaz de usuario se ejecuta a 30 caracteres por segundo, sus programas pasan
mucho tiempo esperando. Tuvimos mucho tiempo para intercambiar los programas dentro y fuera del disco
para mantener todas las terminales funcionando lo más rápido posible. Nadie se quejó nunca de los
problemas de tiempo de respuesta.
Escribimos un supervisor preventivo que gestionaba las interrupciones y las E/S. Escribimos las aplicaciones;
escribimos los controladores de disco, los controladores de terminal, los controladores de cinta y todo lo
demás en ese sistema. No había un solo bit en ese sistema que no escribimos. Aunque fue una lucha que
involucró demasiadas semanas de 80 horas, pusimos a la bestia en funcionamiento en cuestión de 8 o 9
meses.
La arquitectura del sistema era simple (Figura A.7). Cuando se iniciaba una aplicación, generaba
resultados hasta que su búfer de terminal en particular estaba lleno. Luego, el supervisor cambiaría la
aplicación e intercambiaría una nueva aplicación. El supervisor continuaría filtrando el contenido del búfer
del terminal a 30 cps hasta que estuviera casi vacío. Luego, volvería a cambiar la aplicación para llenar el
búfer nuevamente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 301 301
Este límite era dependencia normal, es decir, dependencias señaladas con el flujo de control. Las
aplicaciones tenían dependencias en tiempo de compilación con el supervisor y el flujo de control
pasaba de las aplicaciones al supervisor. El límite impedía que las aplicaciones supieran a qué tipo de
dispositivo se dirigía la salida
a.
El segundo límite era la dependencia invertida. El supervisor podía iniciar las aplicaciones,
pero no tenía dependencias de tiempo de compilación sobre ellas. El flujo de control pasó del
supervisor a las aplicaciones. La interfaz polimórfica que invirtió la dependencia fue simplemente esta:
cada aplicación se inició saltando exactamente a la misma dirección de memoria dentro del área de
superposición. El límite impidió que el supervisor supiera nada sobre las aplicaciones que no fuera el
punto de partida.
RECORTE LÁSER
En 1973, me uní a una empresa en Chicago llamada Teradyne Applied Systems (TAS).
Esta era una división de Teradyne Inc., que tenía su sede en Boston. Nuestro producto era un
sistema que utilizaba láseres de potencia relativamente alta para recortar componentes
electrónicos con tolerancias muy finas.
En aquellos días, los fabricantes serigrafiarían los componentes electrónicos sobre sustratos
cerámicos. Esos sustratos eran del orden de 1 pulgada cuadrada. Los componentes eran
típicamente resistencias, dispositivos que resisten el flujo de corriente.
Nuestro sistema colocaría el sustrato cerámico en un arnés que tenía sondas que hacían contacto
con las resistencias. El sistema mediría la resistencia de las resistencias y luego usaría un láser
para quemar partes de la resistencia, haciéndola más y más delgada hasta alcanzar el valor de
resistencia deseado dentro de una décima de un porcentaje más o menos.
Vendimos estos sistemas a los fabricantes. También utilizamos algunos sistemas internos para
recortar lotes relativamente pequeños para pequeños fabricantes.
La computadora era una M365. Esto fue en los días en que muchas empresas construyeron sus
propias computadoras: Teradyne construyó el M365 y lo suministró a todas sus divisiones. los
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 302 302
M365 era una versión mejorada de una PDP-8, una minicomputadora popular de la época.
El M365 controlaba la mesa de posicionamiento, que movía los sustratos cerámicos debajo
de las sondas. Controlaba el sistema de medición y el láser. El láser se colocó utilizando espejos
XY que podían girar bajo el control del programa. La computadora también podría controlar la
configuración de potencia del láser.
El entorno de desarrollo del M365 era relativamente primitivo. No había disco. El almacenamiento
masivo estaba en cartuchos de cinta que parecían viejos casetes de cinta de audio de 8 pistas.
Las cintas y las unidades fueron hechas por Tri-Data.
Al igual que los casetes de audio de 8 pistas de la época, la cinta estaba orientada en bucle. La
unidad movía la cinta en una sola dirección: ¡no había rebobinado! Si quería colocar la cinta al
principio, tenía que enviarla hacia adelante hasta su "punto de carga".
La cinta se movió a una velocidad de aproximadamente 1 pie por segundo. Por lo tanto, si el bucle
de cinta tuviera 25 pies de largo, podría tomar hasta 25 segundos enviarlo al punto de carga. Por
esta razón, Tridata fabricó cartuchos en varias longitudes, desde 10 pies hasta 100 pies.
El M365 tenía un botón en el frente que cargaba la memoria con un pequeño programa de
arranque y lo ejecutaba. Este programa leería el primer bloque de datos de la cinta y lo
ejecutaría. Por lo general, este bloque contenía un cargador que cargaba el sistema operativo
que vivía en el resto de la cinta.
La consola era una CRT ASCII con fósforos verdes, de 72 caracteres de ancho5 por 24 líneas.
Los caracteres estaban todos en mayúsculas.
Para editar un programa, cargaría el Editor ED-402 y luego insertaría la cinta que contenía su
código fuente. Leería un bloque de cinta de ese código fuente en la memoria y se mostraría en
la pantalla. El bloque de cinta puede contener 50 líneas de código. Haría sus ediciones moviendo
el cursor por la pantalla y escribiendo de una manera similar a vi. Cuando haya terminado, escribirá
ese bloque en una cinta diferente y leerá el siguiente bloque de la cinta de origen. Seguiste
haciendo esto hasta que terminaste.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 303 303
No hubo desplazamiento hacia atrás a los bloques anteriores. Editaste tu programa en línea recta,
de principio a fin. Volver al principio te obligaba a terminar de copiar el código fuente en la cinta de salida y
luego comenzar una nueva sesión de edición en esa cinta. Quizás no sea sorprendente, dadas estas
limitaciones, imprimimos nuestros programas en papel, marcamos todas las ediciones a mano con tinta roja
y luego editamos nuestros programas bloque por bloque consultando nuestras marcas en la lista.
Una vez que se editó el programa, volvimos al sistema operativo e invocamos al ensamblador. El
ensamblador leyó la cinta del código fuente y escribió una cinta binaria, al mismo tiempo que producía una
lista en nuestra impresora de línea de productos de datos.
Las cintas no eran 100% confiables, así que siempre escribimos dos cintas al mismo tiempo.
De esa forma, al menos uno de ellos tenía una alta probabilidad de estar libre de errores.
Nuestro programa tenía aproximadamente 20,000 líneas de código y tomó casi 30 minutos compilarlo. Las
probabilidades de que obtuviéramos un error de lectura de cinta durante ese tiempo eran aproximadamente
de 1 en 10. Si el ensamblador obtenía un error de cinta, sonaba la campana en la consola y luego
comenzaba a imprimir una serie de errores en la impresora. Se podía escuchar esta campana enloquecedora
por todo el laboratorio. También podía escuchar las maldiciones del pobre programador que acababa de
enterarse de que la compilación de 30 minutos necesitaba comenzar de nuevo.
La arquitectura del programa era la típica de aquellos días. Existía un Programa Operativo Maestro,
apropiadamente llamado “el MOP”. Su trabajo consistía en gestionar las funciones básicas de E/S y
proporcionar los rudimentos de un "shell" de consola. Muchas de las divisiones de Teradyne compartían el
código fuente de MOP, pero cada una lo había bifurcado para sus propios usos.
En consecuencia, nos enviábamos actualizaciones del código fuente en forma de listados marcados que
luego integrábamos manualmente (y con mucho cuidado).
Una capa de utilidad de propósito especial controlaba el hardware de medición, las tablas de posicionamiento
y el láser. El límite entre esta capa y el MOP fue confuso en el mejor de los casos. Mientras que la capa de
servicios públicos se llamaba MOP, MOP se había modificado específicamente para esa capa y, a menudo,
se le devolvía la llamada. De hecho, realmente no pensamos en estos dos como capas separadas. Para
nosotros, era solo un código que agregamos al MOP de una manera altamente acoplada.
Luego vino la capa de aislamiento. Esta capa proporcionó una interfaz de máquina virtual para los programas
de aplicación, que se escribieron en un lenguaje controlado por datos (DSL) específico de dominio
completamente diferente. El lenguaje tenía operaciones para mover el láser, mover la mesa, hacer cortes,
hacer medidas, etc. Nuestros clientes escribirían sus programas de aplicación de corte por láser en este
lenguaje, y el
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 304 304
Los trabajos de recorte pueden cargarse desde la cinta y ejecutarse por el sistema. Esencialmente, nuestro
sistema era un sistema operativo para aplicaciones de recorte.
El sistema fue escrito en ensamblador M365 y compilado en una única unidad de compilación que
producía código binario absoluto.
Los límites en esta aplicación eran suaves en el mejor de los casos. Incluso el límite entre el código del
sistema y las aplicaciones escritas en el DSL no se cumplió bien. Había acoplamientos por todas partes.
OMC mantuvo una enorme instalación en Waukegan, Illinois, para crear piezas de aluminio fundido
a presión para todos los motores y productos de la empresa. El aluminio se fundía en hornos enormes y luego
se transportaba en grandes baldes a docenas y docenas de máquinas de fundición de aluminio operadas
individualmente. Cada máquina tenía un operador humano responsable de configurar los moldes, hacer
funcionar la máquina y extraer las piezas recién fundidas. A estos operadores se les pagaba en función de la
cantidad de piezas que producían.
Me contrataron para trabajar en un proyecto de automatización del taller. OMC había comprado un IBM System/
7, que era la respuesta de IBM a la minicomputadora. Vincularon esta computadora a todas las máquinas de
fundición a presión en el piso, para que pudiéramos contar y cronometrar los ciclos de cada máquina. Nuestro
papel era recopilar toda esa información y presentarla en 3270 pantallas verdes.
El lenguaje era ensamblador. Y, de nuevo, cada fragmento de código que se ejecutó en este
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 305 305
la computadora era el código que escribimos. No había sistema operativo, ni bibliotecas de subrutinas, ni
framework. Era solo código en bruto.
También era un código en tiempo real controlado por interrupciones. Cada vez que una máquina de fundición
a presión realizaba un ciclo, teníamos que actualizar un lote de estadísticas y enviar mensajes a un gran IBM
370 en el cielo, ejecutando un programa CICS-COBOL que presentaba esas estadísticas en las pantallas verdes.
Odiaba este trabajo. Oh, chico, lo hice. ¡Oh, el trabajo fue divertido! Pero la cultura… Baste decir que me
obligaron a llevar corbata.
Oh, lo intenté. Realmente lo hice. Pero claramente estaba descontento trabajando allí, y mis colegas lo sabían.
Lo sabían porque no podía recordar las fechas críticas ni lograr levantarme lo suficientemente temprano para
asistir a reuniones importantes. Este fue el único trabajo de programación del que me despidieron, y me lo
merecía.
Desde un punto de vista arquitectónico, no hay mucho que aprender aquí excepto por una cosa. El
System/7 tenía una instrucción muy interesante llamada set program interrupt (SPI). Esto le permitió activar
una interrupción del procesador, lo que le permitió manejar cualquier otra interrupción de menor prioridad en
cola. Hoy en día, en Java llamamos a esto
Hilo.rendimiento().
4-TELÉFONO
En octubre de 1976, después de haber sido despedido de OMC, regresé a una división diferente de
Teradyne, una división en la que permanecería durante 12 años. El producto en el que trabajé se llamaba 4-
TEL. Su propósito era probar todas las líneas telefónicas en un área de servicio telefónico, todas las noches,
y generar un informe de todas las líneas que requerían reparación. También permitió que el personal de
pruebas telefónicas probara líneas telefónicas específicas en detalle.
Este sistema comenzó su vida con el mismo tipo de arquitectura que el sistema Laser Trim. Era una
aplicación monolítica escrita en lenguaje ensamblador sin límites significativos. Pero cuando me uní a la
empresa, eso estaba a punto de cambiar.
El sistema fue utilizado por probadores ubicados en un centro de servicio (SC). Un centro de servicio
cubría muchas oficinas centrales (CO), cada una de las cuales podía manejar hasta 10.000 líneas
telefónicas. El hardware de marcación y medición tenía que estar ubicado dentro de la CO.
Ahí es donde se colocaron las computadoras M365. Llamamos a esas computadoras los probadores
de línea de la oficina central (COLT). Se colocó otro M365 en el SC; era
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 306 306
llamada computadora del área de servicio (SAC). El SAC tenía varios módems que podía usar
para marcar los COLT y comunicarse a 300 baudios (30 cps).
El problema con esta configuración es que 30 cps es muy lento. A los evaluadores no les
gustó ver a los personajes pasar por la pantalla, especialmente porque solo estaban
interesados en unos pocos datos clave. Además, en aquellos días, la memoria central del
M365 era costosa y el programa era grande.
La solución fue separar la parte del software que marcaba y medía líneas de la parte que
analizaba los resultados e imprimía los informes. Este último se trasladaría al SAC y el primero
se quedaría en los COLT. Esto permitiría que el COLT fuera una máquina más pequeña, con
mucha menos memoria, y agilizaría mucho la respuesta en el terminal, ya que los reportes se
generarían en el SAC.
El resultado fue notablemente exitoso. Las actualizaciones de pantalla fueron muy rápidas
(una vez que se marcó el COLT apropiado) y la huella de memoria de los COLT se redujo mucho.
El límite estaba muy limpio y muy desacoplado. Se intercambiaron paquetes de datos muy cortos
entre SAC y COLT. Estos paquetes eran una forma muy simple de DSL y representaban
comandos primitivos como "MARCAR XXXX" o "MEDIR".
El M365 se cargó desde la cinta. Esas unidades de cinta eran costosas y no muy confiables,
especialmente en el entorno industrial de una oficina central telefónica. Además, el M365 era una
máquina costosa en relación con el resto de la electrónica dentro del COLT. Así que nos
embarcamos en un proyecto para reemplazar el M365 con una microcomputadora basada en un
procesador de 8085 µ.
La nueva computadora estaba compuesta por una placa de procesador que contenía el 8085,
una placa de RAM que contenía 32K de RAM y tres placas de ROM que contenían 12K de
memoria de solo lectura cada una. Todas estas placas encajan en el mismo chasis que el
hardware de medición, eliminando así el voluminoso chasis adicional que albergaba el M365.
Las tarjetas ROM contenían 12 chips Intel 2708 EPROM (Memoria de sólo lectura programable
y borrable).6 La figura A.8 muestra un ejemplo de dicho chip. Cargamos esos chips con
software insertándolos en dispositivos especiales llamados quemadores PROM que
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 307 307
fueron impulsados por nuestro entorno de desarrollo. Los chips podrían borrarse exponiéndolos
a luz ultravioleta de alta intensidad.7
Nuestro entorno de desarrollo tenía 64 K de RAM y no tenía ROM, por lo que pudimos descargar
rápidamente nuestros binarios compilados en la RAM y probarlos.
Una vez que hicimos funcionar el programa, pasamos a usar las EPROM. Quemamos 30 chips y los
insertamos en las ranuras correctas de las tres placas ROM. Cada chip estaba etiquetado para que
pudiéramos saber qué chip entró en qué ranura.
El programa 30K era un solo binario de 30K de largo. Para quemar los chips, simplemente
dividimos esa imagen binaria en 30 segmentos diferentes de 1K y grabamos cada segmento en el chip
debidamente etiquetado.
Esto funcionó muy bien y comenzamos a producir en masa el hardware y a implementar el sistema en
el campo.
Pero el software es suave.8 Es necesario agregar funciones. Los errores necesitaban ser corregidos. Y
a medida que crecía la base instalada, la logística de actualizar el software quemando 30 chips por
instalación y hacer que el personal de servicio de campo reemplazara los 30 chips en cada sitio se
convirtió en una pesadilla.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 308 308
Había todo tipo de problemas. A veces, las fichas estaban mal etiquetadas o las etiquetas se caían. A
veces, el ingeniero de servicio de campo reemplazaba por error el chip equivocado. A veces, el ingeniero de
servicio de campo sin darse cuenta rompía un pin de uno de los nuevos chips. En consecuencia, los ingenieros de
campo tuvieron que llevar extras de los 30 chips con ellos.
¿Por qué tuvimos que cambiar los 30 chips? Cada vez que añadíamos o eliminamos código de nuestro
ejecutable de 30K, cambiaban las direcciones en las que se cargaba cada instrucción. También cambió las
direcciones de las subrutinas y funciones que llamamos.
Así que todos los chips se vieron afectados, sin importar cuán trivial sea el cambio.
Un día, mi jefe vino a mí y me pidió que solucionara ese problema. Dijo que necesitábamos una manera
de hacer un cambio en el firmware sin reemplazar los 30 chips cada vez. Hicimos una lluvia de ideas sobre este
tema por un tiempo y luego nos embarcamos en el proyecto de "Vectorización". Me tomó tres meses.
La idea era maravillosamente simple. Dividimos el programa de 30K en 32 archivos fuente compilables de forma
independiente, cada uno de menos de 1K. Al comienzo de cada archivo fuente, le dijimos al compilador en qué
dirección cargar el programa resultante (por ejemplo, ORG C400 para el chip que se insertaría en la posición C4).
También al comienzo de cada archivo fuente, creamos una estructura de datos simple y de tamaño fijo
que contenía todas las direcciones de todas las subrutinas en ese chip. Esta estructura de datos tenía una longitud
de 40 bytes, por lo que no podía contener más de 20 direcciones. Esto significaba que ningún chip podía tener más
de 20 subrutinas.
A continuación, creamos un área especial en la RAM conocida como vectores. Contenía 32 tablas de 40 bytes,
exactamente suficiente RAM para contener los punteros al comienzo de cada chip.
Finalmente, cambiamos cada llamada a cada subrutina en cada chip en una llamada indirecta a través del vector
RAM apropiado.
Cuando nuestro procesador arrancaba, escaneaba cada chip y cargaba la tabla de vectores al comienzo de cada
chip en los vectores de RAM. Entonces saltaría al programa principal.
Esto funcionó muy bien. Ahora, cuando arreglamos un error o añadimos una función, simplemente podíamos
volver a compilar uno o dos chips y enviar solo esos chips a los ingenieros de servicio de campo.
Hicimos que los chips se desplegaran de forma independiente. Habíamos inventado el envío polimórfico.
Habíamos inventado objetos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 309 309
Esta era una arquitectura de complemento, literalmente. Conectamos esos chips. Eventualmente
lo diseñamos para que se pudiera instalar una función en nuestros productos conectando el chip con
esa función en uno de los zócalos abiertos para chips. El control del menú aparecería automáticamente
y el enlace a la aplicación principal se produciría automáticamente.
Por supuesto, no conocíamos los principios orientados a objetos en ese momento, y no sabíamos nada
acerca de separar la interfaz de usuario de las reglas comerciales. Pero los rudimentos estaban allí, y eran
muy poderosos.
Un beneficio secundario inesperado del enfoque fue que pudimos parchear el firmware a través de una
conexión de acceso telefónico. Si encontramos un error en el firmware, podríamos marcar nuestros
dispositivos y usar el programa de monitoreo integrado para alterar el vector de RAM para que la subrutina
defectuosa apunte a un poco de RAM vacía. Luego ingresaríamos la subrutina reparada en esa área de
RAM, escribiéndola en código de máquina, en hexadecimal.
Esta fue una gran ayuda para nuestra operación de servicio de campo y para nuestros clientes. Si
tenían un problema, no necesitaban que les enviáramos chips nuevos y que programáramos una
llamada de servicio de campo urgente. Se podría parchear el sistema y se podría instalar un nuevo chip
en la próxima visita de mantenimiento programada regularmente.
DETERMINACIÓN DE ENVÍO
Una de las bases económicas de este sistema se basaba en la correcta asignación de los artesanos de
reparación. Las embarcaciones de reparación se separaron, según las reglas del sindicato, en tres
categorías: oficina central, cable y caída. Los artesanos de CO solucionaron problemas dentro de la oficina
central. Los artesanos del cable solucionaron problemas en la planta de cable que conectaba el CO con el
cliente. Los artesanos de la caída solucionaron los problemas dentro de las instalaciones del cliente y en
las líneas que conectan el cable externo a esas instalaciones (la "caída").
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 310 310
problema y determinar qué tipo de artesano despachar. Esto ahorró mucho dinero a las compañías
telefónicas porque los despachos incorrectos significaban demoras para el cliente y viajes perdidos para los
artesanos.
El código que hizo esta determinación de envío fue diseñado y escrito por alguien que era muy
brillante, pero un terrible comunicador. El proceso de escribir el código se ha descrito como "Tres semanas
de mirar al techo y dos días de código saliendo por todos los orificios de su cuerpo, después de lo cual renunció".
Nadie entendió este código. Cada vez que intentamos agregar una característica o corregir un defecto, lo
rompimos de alguna manera. Y dado que en este código se basaba uno de los principales beneficios
económicos de nuestro sistema, cada nuevo defecto era profundamente vergonzoso para la empresa.
Al final, nuestra gerencia simplemente nos dijo que bloqueáramos ese código y nunca lo modificáramos. Ese
código se volvió oficialmente rígido.
ARQUITECTURA
El sistema fue escrito en 1976 en ensamblador M365. Era un programa único y monolítico de
aproximadamente 60.000 líneas. El sistema operativo era un conmutador de tareas no preventivo de
cosecha propia basado en sondeos. Lo llamamos MPS para sistema de multiprocesamiento. La
computadora M365 no tenía una pila incorporada, por lo que las variables específicas de la tarea se
mantuvieron en un área especial de la memoria y se intercambiaron en cada cambio de contexto. Las
variables compartidas se gestionaban con candados y semáforos. Los problemas de reingreso y las
condiciones de carrera fueron problemas constantes.
No hubo aislamiento de la lógica de control del dispositivo, o la lógica de la interfaz de usuario, de las reglas
comerciales del sistema. Por ejemplo, el código de control del módem se puede encontrar manchado en la
mayor parte de las reglas comerciales y el código de la interfaz de usuario. No hubo ningún intento de reunirlo
en un módulo o abstraer la interfaz. Los módems estaban controlados, a nivel de bits, por un código que estaba
disperso por todo el sistema.
Lo mismo ocurría con la interfaz de usuario del terminal. Los mensajes y el código de control de formato no
estaban aislados. Se extendieron a lo largo y ancho a lo largo de la base de código de 60,000 líneas.
Los módulos de módem que estábamos usando estaban diseñados para montarse en placas de PC.
Compramos esas unidades a un tercero y las integramos con otros circuitos en un
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 311 311
placa que encaja en nuestro backplane personalizado. Estas unidades eran caras. Entonces,
después de algunos años, decidimos diseñar nuestros propios módems. Nosotros, en el grupo de
software, rogamos al diseñador de hardware que usara los mismos formatos de bits para controlar el
nuevo módem. Explicamos que el código de control del módem estaba manchado por todas partes y
que nuestro sistema tendría que lidiar con ambos tipos de módems en el futuro. Entonces, suplicamos
y engatusamos: "Por favor, haga que el nuevo módem se vea como el antiguo desde el punto de
vista del control del software".
Pero cuando recibimos el nuevo módem, la estructura de control era completamente diferente.
No era solo un poco diferente. Era total y completamente diferente.
¿Qué íbamos a hacer? No estábamos simplemente reemplazando todos los módems antiguos
por módems nuevos. En cambio, estábamos mezclando módems antiguos y nuevos en nuestros
sistemas. El software necesitaba poder manejar ambos tipos de módems al mismo tiempo.
¿Estábamos condenados a rodear cada lugar del código que manipulaba los módems con
banderas y casos especiales? ¡Había cientos de esos lugares!
Una subrutina en particular escribía datos en el bus de comunicación en serie que se usaba para
controlar todos nuestros dispositivos, incluidos nuestros módems. Modificamos esa subrutina para
reconocer los patrones de bits que eran específicos del módem anterior y traducirlos a los patrones
de bits que necesita el nuevo módem.
Esto no fue sencillo. Los comandos al módem consistían en secuencias de escrituras en diferentes
direcciones de E/S en el bus serie. Nuestro truco tuvo que interpretar estos comandos, en
secuencia, y traducirlos a una secuencia diferente utilizando diferentes direcciones de E/S, tiempos
y posiciones de bits.
Lo hicimos funcionar, pero fue el peor truco imaginable. Fue debido a este fiasco que aprendí el valor
de aislar el hardware de las reglas comerciales y de abstraer las interfaces.
Para cuando llegó la década de 1980, la idea de producir su propia minicomputadora y su propia
arquitectura de computadora estaba comenzando a pasar de moda. Había muchas microcomputadoras
en el mercado, y hacer que funcionaran era más barato y
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 312 312
más estándar que seguir confiando en las arquitecturas informáticas propietarias de finales de la década
de 1960. Eso, sumado a la horrible arquitectura del software SAC, indujo a nuestra gerencia técnica a
iniciar una re-arquitectura completa del sistema SAC.
El nuevo sistema debía escribirse en C utilizando un sistema operativo UNIX en disco, ejecutándose
en una microcomputadora Intel 8086. Nuestros muchachos de hardware comenzaron a trabajar en el
nuevo hardware de la computadora, y se encargó la reescritura a un grupo selecto de desarrolladores
de software, "The Tiger Team".
No los aburriré con los detalles del fiasco inicial. Baste decir que el primer Tiger Team fracasó por
completo después de gastar dos o tres años-hombre en un proyecto de software que nunca entregó
nada.
Uno o dos años más tarde, probablemente en 1982, el proceso se inició de nuevo. El objetivo era el
rediseño total y completo del SAC en C y UNIX en nuestro propio hardware 80286, de nuevo diseño e
impresionantemente potente. Llamamos a esa computadora "Pensamiento Profundo".
Tomó años, luego más años, y luego aún más años. No sé cuándo finalmente se implementó el primer
SAC basado en UNIX; Creo que ya había dejado la empresa para entonces (1988). De hecho, no estoy
del todo seguro de que alguna vez se haya implementado.
¿Por qué la demora? En resumen, es muy difícil para un equipo de rediseño ponerse al día con un
gran personal de programadores que mantienen activamente el sistema anterior. He aquí sólo un
ejemplo de las dificultades que encontraron.
EUROPA
Casi al mismo tiempo que se estaba rediseñando el SAC en C, la empresa comenzó a expandir las
ventas a Europa. No podían esperar a que se terminara el software rediseñado, así que, por supuesto,
implementaron los antiguos sistemas M365 en Europa.
El problema era que los sistemas telefónicos en Europa eran muy diferentes a los sistemas
telefónicos en los Estados Unidos. La organización del oficio y de las burocracias también era
diferente. Entonces, uno de nuestros mejores programadores fue enviado al Reino Unido para liderar un
equipo de desarrolladores del Reino Unido para modificar el software SAC para hacer frente a todos
estos problemas europeos.
Por supuesto, no se hizo ningún intento serio de integrar estos cambios en el software basado en
EE.UU. Esto fue mucho antes de que las redes hicieran factible la transmisión de grandes bases
de código a través del océano. Estos desarrolladores del Reino Unido simplemente bifurcaron el
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 313 313
Esto, por supuesto, causó dificultades. Se encontraron errores en ambos lados del Atlántico que necesitaban
reparación en el otro lado. Pero los módulos habían cambiado significativamente, por lo que era muy difícil
determinar si la solución hecha en los Estados Unidos funcionaría en el Reino Unido.
Después de algunos años de acidez estomacal y la instalación de una línea de alto rendimiento que
conectaba las oficinas de EE. UU. y el Reino Unido, se hizo un intento serio de volver a integrar estas dos
bifurcaciones, haciendo que las diferencias fueran una cuestión de configuración. Este esfuerzo fracasó la
primera, segunda y tercera vez que se intentó. Las dos bases de código, aunque notablemente similares, todavía
eran demasiado diferentes para reintegrarse, especialmente en el entorno de mercado que cambiaba rápidamente
y que existía en ese momento.
Mientras tanto, el “Tiger Team”, tratando de reescribir todo en C y UNIX, se dio cuenta de que también tenía
que lidiar con esta dicotomía entre Europa y EE. UU. Y, por supuesto, eso no hizo nada para acelerar su
progreso.
CONCLUSIÓN SAC
Hay muchas otras historias que podría contarles sobre este sistema, pero es demasiado deprimente
para mí continuar. Baste decir que muchas de las duras lecciones de mi vida de software las aprendí mientras
estaba inmerso en el horrible código ensamblador del SAC.
LENGUAJE C
El hardware de la computadora 8085 que usamos en el proyecto 4-Tel Micro nos brindó una plataforma
informática de costo relativamente bajo para muchos proyectos diferentes que podrían integrarse en
entornos industriales. Podíamos cargarlo con 32K de RAM y otros 32K de ROM, y teníamos un esquema
extremadamente flexible y poderoso para controlar periféricos. Lo que no teníamos era un lenguaje flexible y
conveniente para programar la máquina. El ensamblador 8085 simplemente no era divertido para escribir
código.
Además de eso, el ensamblador que estábamos usando fue escrito por nuestros propios programadores. Se
ejecutó en nuestras computadoras M365, utilizando el sistema operativo de cinta de cartucho descrito en la
sección "Recorte láser".
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 314 314
Como el destino lo tendría, nuestro ingeniero de hardware principal convenció a nuestro CEO de que
necesitábamos una computadora real . En realidad, no sabía qué haría con él, pero tenía mucha influencia
política. Así que compramos un PDP-11/60.
Yo, un humilde desarrollador de software en ese momento, estaba extasiado. Sabía exactamente lo que
quería hacer con esa computadora. Estaba decidido a que esta iba a ser mi máquina.
Cuando llegaron los manuales, muchos meses antes de la entrega de la máquina, los llevé a casa y los devoré.
En el momento en que me entregaron la computadora, sabía cómo operar tanto el hardware como el software a un
nivel íntimo, al menos, tan íntimo como el estudio en el hogar puede hacerlo.
Ayudé a redactar la orden de compra. En particular, especifiqué el almacenamiento en disco que tendría la nueva
computadora. Decidí que deberíamos comprar dos unidades de disco que pudieran albergar paquetes de discos
extraíbles de 25 megabytes cada uno.9
¡Cincuenta megas! ¡El número parecía infinito! Recuerdo caminar por los pasillos de la oficina, tarde en la noche,
cacareando como la Malvada Bruja del Oeste: “¡Cincuenta megabytes! ¡Jajajajajajajajaja!”
Le pedí al gerente de instalaciones que construyera una pequeña habitación que albergaría seis terminales VT100.
Lo decoré con imágenes del espacio. Nuestros desarrolladores de software usarían esta sala para escribir y
compilar código.
Cuando llegó la máquina, pasé varios días configurándola, cableando todas las terminales y haciendo que todo
funcionara. Fue un gozo, un trabajo de amor.
Compramos ensambladores estándar para el 8085 de Boston Systems Office y traducimos el código 4-Tel Micro a
esa sintaxis. Creamos un sistema de compilación cruzada que nos permitió descargar archivos binarios compilados
desde el PDP-11 a nuestros entornos de desarrollo 8085 y grabadoras de ROM. Y, Bob es tu tío, todo funcionó
como un campeón.
C
Pero eso nos dejó con el problema de seguir usando el ensamblador 8085. Esa no era una situación con
la que estaba feliz. Escuché que existía este lenguaje "nuevo" que se usaba mucho en Bell Labs. Lo llamaron
"C". Así que compré una copia de The C Programming Language de Kernighan y Ritchie. Como los manuales
de PDP-11 algunos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 315 315
Me quedé asombrado por la elegancia simple de este idioma. No sacrificó nada del poder
del lenguaje ensamblador y proporcionó acceso a ese poder con una sintaxis mucho más
conveniente. me vendieron
JEFE
Nuestra plataforma 8085 no tenía sistema operativo. Mi experiencia con el sistema MPS del
M365 y los primitivos mecanismos de interrupción del IBM System 7 me convencieron de que
necesitábamos un conmutador de tareas simple para el 8085. Así que concebí BOSS: Sistema
Operativo Básico y Programador.10
En otras palabras, como dije antes, era un simple cambio de tareas no preventivo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 316 316
Este software se convirtió en la base de una gran cantidad de proyectos en los años siguientes.
Pero uno de los primeros fue el pCCU.
pCCU
Los últimos años de la década de 1970 y principios de la de 1980 fueron una época tumultuosa para las compañías telefónicas.
Durante el siglo anterior, la conexión entre la oficina central de conmutación y el teléfono del
cliente había sido un par de cables de cobre. Estos cables se agruparon en cables que se
extendieron en una gran red por todo el campo. A veces se transportaban en postes y, a veces, se
enterraban bajo tierra.
Nuestro producto 4-Tel probó cables de cobre, no conexiones digitales. Todavía había muchos
cables de cobre en un entorno digital, pero eran mucho más cortos que antes y estaban
ubicados cerca de los teléfonos de los clientes. La señal se transportaría digitalmente desde la
oficina central hasta un punto de distribución local, donde se volvería a convertir en una señal
analógica y se distribuiría al cliente a través de cables de cobre estándar. Esto significaba que
nuestro dispositivo de medición debía ubicarse donde comenzaban los cables de cobre, pero nuestro
dispositivo de marcación debía permanecer en la oficina central. El problema era que todos nuestros
COLT incorporaban marcación y medición en el mismo dispositivo. (¡Podríamos habernos ahorrado
una fortuna si hubiéramos reconocido ese límite arquitectónico obvio unos años antes!)
Así concebimos una nueva arquitectura de producto: la CCU/CMU (la unidad de control COLT y la
unidad de medida COLT). La CCU estaría ubicada en la oficina central de conmutación y manejaría
la marcación de las líneas telefónicas a probar. La CMU estaría ubicada en los puntos de distribución
locales y mediría los cables de cobre que conducían al teléfono del cliente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 317 317
El problema era que por cada CCU había muchas CMU. La información sobre qué CMU se debe
utilizar para cada número de teléfono estaba en manos del propio conmutador digital. Por lo tanto, la
CCU tuvo que interrogar al interruptor digital para determinar con qué CMU comunicarse y controlar.
Prometimos a las compañías telefónicas que tendríamos esta nueva arquitectura funcionando
a tiempo para su transición. Sabíamos que faltaban meses, si no años, así que no nos sentimos
apurados. También sabíamos que llevaría varios años-hombre desarrollar este nuevo hardware y
software de CCU/CMU.
Con el paso del tiempo, descubrimos que siempre había asuntos urgentes que requerían que
pospusiéramos el desarrollo de la arquitectura CCU/CMU. Nos sentimos seguros con esta decisión
porque las compañías telefónicas retrasaban constantemente la implementación de conmutadores
digitales. Al mirar sus cronogramas, nos sentimos seguros de que teníamos mucho tiempo, por lo que
constantemente retrasamos nuestro desarrollo.
Luego llegó el día en que mi jefe me llamó a su oficina y me dijo: “Uno de nuestros clientes
implementará un conmutador digital el próximo mes. Tenemos que tener una CCU/ CMU en
funcionamiento para entonces”.
¡Estaba horrorizado! ¿Cómo podríamos hacer años-hombre de desarrollo en un mes? Pero mi jefe
tenía un plan...
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 318 318
Este fue el primer producto escrito en C y usando BOSS que se implementó en un cliente. Me
tomó alrededor de una semana para desarrollar. No hay un significado arquitectónico profundo
en esta historia, pero es un buen prefacio para el próximo proyecto.
DLU/DRU
A principios de la década de 1980, uno de nuestros clientes era una compañía telefónica de Texas.
Tenían grandes áreas geográficas que cubrir. De hecho, las áreas eran tan grandes que una sola
área de servicio requería varias oficinas diferentes desde las cuales despachar a los artesanos.
Esas oficinas tenían artesanos de prueba que necesitaban terminales en nuestro SAC.
Podría pensar que este era un problema simple de resolver, pero recuerde que esta historia tiene
lugar a principios de la década de 1980. Los terminales remotos no eran muy comunes. Para colmo,
el hardware del SAC presumía que todos los terminales eran locales. Nuestras terminales en realidad
se encontraban en un bus serial patentado de alta velocidad.
Había módems de alta velocidad disponibles, pero eran muy caros y necesitaban funcionar con
conexiones permanentes "acondicionadas". La calidad de acceso telefónico definitivamente no era lo
suficientemente buena.
DLU/DRU significaba "Display Local Unit" y "Display Remote Unit". La DLU era una placa de
computadora que se conectaba al chasis de la computadora SAC y pretendía ser una placa
administradora de terminales. Sin embargo, en lugar de controlar el bus serie para terminales locales,
tomó el flujo de caracteres y lo multiplexó a través de un solo enlace de módem acondicionado de 9600
bps.
La DRU era una caja colocada en la ubicación remota del cliente. Se conectó al otro extremo del
enlace de 9600 bps y tenía el hardware para controlar los terminales en nuestro bus serial patentado.
Demultiplexó los caracteres recibidos del enlace de 9600 bps y los envió a las terminales locales
apropiadas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 319 319
Extraño, ¿no? Tuvimos que diseñar una solución que hoy en día es tan omnipresente que ni siquiera
pensamos en ella. Pero en aquel entonces…
Incluso tuvimos que inventar nuestro propio protocolo de comunicaciones porque, en aquellos días, los
protocolos de comunicaciones estándar no eran shareware de código abierto. De hecho, esto fue mucho
antes de que tuviéramos algún tipo de conexión a Internet.
ARQUITECTURA
La arquitectura de este sistema era muy simple, pero hay algunas peculiaridades interesantes que
quiero resaltar. Primero, ambas unidades usaban nuestra tecnología 8085, y ambas estaban escritas en C
y usaban BOSS. Pero ahí es donde terminó la similitud.
Éramos dos en el proyecto. Yo era el líder del proyecto y Mike Carew era mi colaborador más cercano.
Asumí el diseño y codificación de la DLU; Mike hizo la DRU.
La arquitectura de la DLU se basó en un modelo de flujo de datos. Cada tarea hizo un trabajo pequeño y
enfocado, y luego pasó su salida a la siguiente tarea en línea, usando una cola.
Piense en un modelo de tuberías y filtros en UNIX. La arquitectura era compleja. Una tarea podría alimentar
una cola que muchas otras atenderían. Otras tareas alimentarían una cola que solo atendería una tarea.
Piensa en una línea de montaje. Cada posición en la línea de ensamblaje tiene un trabajo único, simple y
altamente enfocado para realizar. Luego, el producto se mueve a la siguiente posición en la línea.
A veces, la línea de montaje se divide en muchas líneas. A veces, esas líneas se fusionan en una sola
línea. Ese fue el DLU.
La DRU de Mike utilizó un esquema notablemente diferente. Creó una tarea por terminal y simplemente
hizo todo el trabajo para esa terminal en esa tarea. Sin colas. Sin flujo de datos.
Solo muchas tareas grandes idénticas, cada una administrando su propia terminal.
Esto es lo opuesto a una línea de montaje. En este caso, la analogía son muchos constructores
expertos, cada uno de los cuales construye un producto completo.
En ese momento pensé que mi arquitectura era superior. Mike, por supuesto, pensó que el suyo era mejor.
Tuvimos muchas discusiones entretenidas sobre esto. Al final, por supuesto, ambos funcionaron bastante
bien. Y me quedé con la conclusión de que las arquitecturas de software pueden ser muy diferentes, pero
igualmente efectivas.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 320 320
VRS
A medida que avanzaba la década de 1980, aparecieron tecnologías cada vez más nuevas.
Una de esas tecnologías era el control informático de la voz.
Una de las características del sistema 4-Tel era la capacidad del artesano para localizar una falla
en un cable. El procedimiento fue el siguiente:
• El probador, en la oficina central, usaría nuestro sistema para determinar la distancia aproximada,
en pies, hasta la falla. Esto sería exacto dentro del 20% más o menos. El probador enviaría a un
técnico de reparación de cables a un punto de acceso apropiado cerca de esa posición. • El técnico
de reparación de cables, al llegar, llamaría al probador y le pediría que comenzara el proceso de
localización de fallas. El probador invocaría la función de ubicación de fallas del sistema 4-Tel. El
sistema comenzaría midiendo las características electrónicas de esa línea defectuosa, e imprimiría
mensajes en la pantalla solicitando que se realicen ciertas operaciones, como abrir el cable o
cortocircuitarlo. • El probador le diría al artesano qué operaciones desea el sistema, y el artesano le
diría al probador cuándo se completó la operación. Luego, el probador le diría al sistema que la
operación se completó y el sistema continuaría con la prueba.
• Después de dos o tres interacciones de este tipo, el sistema calcularía una nueva distancia a la
falla. El artesano del cable luego conduciría hasta ese lugar y comenzaría el proceso nuevamente.
Imagínese cuánto mejor sería eso si los artesanos del cable, en el poste o parados en un
pedestal, pudieran operar el sistema por sí mismos. Y eso es exactamente lo que las nuevas
tecnologías de voz nos permitieron hacer. Los artesanos del cable podían llamar directamente a
nuestro sistema, dirigir el sistema con tonos y escuchar los resultados que se les leían con una voz
agradable.
EL NOMBRE
La empresa realizó un pequeño concurso para seleccionar un nombre para el nuevo sistema.
Uno de los nombres más creativos sugeridos fue SAM CARP. Esto significaba "otra manifestación
más de la avaricia capitalista que reprime al proletariado". No hace falta decir que eso no fue
seleccionado.
Otro fue el Sistema de prueba interactivo Teradyne. Ese tampoco fue seleccionado.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 321 321
Otro más fue la red de acceso de prueba del área de servicio. Eso tampoco fue seleccionado.
ARQUITECTURA
No trabajé en este sistema, pero me enteré de lo que sucedió. La historia que les voy a contar es de
segunda mano, pero en su mayor parte creo que es correcta.
Estos fueron los días embriagadores de las microcomputadoras, los sistemas operativos UNIX, las
bases de datos C y SQL. Estábamos decididos a usarlos todos.
De los muchos proveedores de bases de datos que existen, finalmente elegimos UNIFY. UNIFY era
un sistema de base de datos que funcionaba con UNIX, lo cual era perfecto para nosotros.
UNIFY también admitió una nueva tecnología llamada Embedded SQL. Esta tecnología nos permitió
incrustar comandos SQL, como cadenas, directamente en nuestro código C. Y así lo hicimos, en todas
partes.
Quiero decir, fue genial que pudieras poner tu SQL directamente en tu código, en cualquier
lugar que quisieras. ¿Y adónde queríamos? ¡En todas partes! Y entonces hubo SQL manchado en
todo el cuerpo de ese código.
Por supuesto, en esos días SQL no era un estándar sólido. Hubo muchas peculiaridades especiales
específicas del proveedor. Por lo tanto, las llamadas a la API especial de SQL y UNIFY también se
difuminaron en todo el código.
¡Esto funcionó muy bien! El sistema fue un éxito. Los artesanos lo usaban y a las compañías
telefónicas les encantaba. La vida era todo sonrisas.
Vaya. Vaya.
Así que decidimos cambiar a SyBase. ¿O fue Ingreso? no recuerdo Baste decir que tuvimos que buscar
en todo ese código C, encontrar todas las llamadas de API especiales y SQL incrustadas, y reemplazarlas
con los gestos correspondientes para el nuevo proveedor.
Después de tres meses de esfuerzo más o menos, nos dimos por vencidos. No pudimos hacerlo
funcionar. Estábamos tan acoplados a UNIFY que no había ninguna esperanza seria de reestructurar el
código a ningún costo práctico.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 322 322
Por lo tanto, contratamos a un tercero para que nos mantuviera UNIFY, según un contrato de
mantenimiento. Y, por supuesto, las tasas de mantenimiento aumentaron año tras año tras año.
CONCLUSIÓN VRS
Esta es una de las formas en que aprendí que las bases de datos son detalles que deben aislarse
del propósito comercial general del sistema. Esta es también una de las razones por las que no me
gusta acoplarme fuertemente a sistemas de software de terceros.
LA RECEPCIONISTA ELECTRÓNICA
En 1983, nuestra empresa se encontraba en la confluencia de los sistemas
informáticos, los sistemas de telecomunicaciones y los sistemas de voz. Nuestro CEO pensó que esta
podría ser una posición fértil desde la cual desarrollar nuevos productos. Para abordar este objetivo,
encargó a un equipo de tres (que me incluía a mí) que concibiera, diseñara e implementara un nuevo
producto para la empresa.
La idea era sencilla. Cuando llamaba a una empresa, ER respondía y le preguntaba con quién quería
hablar. Usaría tonos para deletrear el nombre de esa persona, y luego ER lo conectaría. Los usuarios de
ER podían marcar y, mediante simples comandos de tonos, decirle en qué número de teléfono se podía
contactar a la persona deseada, en cualquier parte del mundo. De hecho, el sistema podría enumerar varios
números alternativos.
Cuando llamó a ER y marcó RMART (mi código), ER llamaría al primer número en mi lista. Si no respondía
y no me identificaba, llamaría al siguiente número, y al siguiente. Si aún no me localizaban, ER grabaría un
mensaje de la persona que llamó.
Luego, ER trataría periódicamente de encontrarme para entregar ese mensaje, y cualquier otro mensaje
que me dejara cualquier otra persona.
Este fue el primer sistema de correo de voz de la historia, y nosotros11 teníamos la patente.
Construimos todo el hardware para este sistema: la placa de computadora, la placa de memoria, las
placas de voz/telecomunicaciones, todo. La placa principal de la computadora era Deep Thought, el
procesador Intel 80286 que mencioné anteriormente.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 323 323
Cada uno de los tableros de voz admitía una línea telefónica. Consistían en una interfaz telefónica, un
codificador/descodificador de voz, algo de memoria y una microcomputadora Intel 80186.
El software para la placa de la computadora principal se escribió en C. El sistema operativo era MP/
M-86, uno de los primeros sistemas operativos de disco multiprocesador impulsado por línea de
comandos. MP/M era el UNIX de los pobres.
El software de las tarjetas de voz estaba escrito en ensamblador y no tenía sistema operativo. La
comunicación entre Deep Thought y los tableros de voz se produjo a través de la memoria compartida.
La arquitectura de este sistema se denominaría hoy orientada a servicios. Cada línea telefónica
fue monitoreada por un proceso de escucha que se ejecutaba bajo MP/M. Cuando entraba una llamada,
se iniciaba un proceso de manejo inicial y se le pasaba la llamada. A medida que la llamada avanzaba
de un estado a otro, el proceso del controlador apropiado se iniciaría y tomaría el control.
Los mensajes se pasaban entre estos servicios a través de archivos de disco. El servicio actualmente
en ejecución determinaría cuál debería ser el próximo servicio; escribiría la información de estado
necesaria en un archivo de disco; emitiría la línea de comando para iniciar ese servicio; y luego saldría.
Esta fue la primera vez que construí un sistema como este. De hecho, esta fue la primera vez que fui el
arquitecto principal de un producto completo. Todo lo que tenía que ver con el software era mío, y funcionó
como un campeón.
No diría que la arquitectura de este sistema era “limpia” en el sentido de este libro; no era una arquitectura
de "complemento". Sin embargo, definitivamente mostró signos de verdaderos límites. Los servicios se
podían implementar de forma independiente y vivían dentro de su propio dominio de responsabilidad. Había
procesos de alto nivel y procesos de bajo nivel, y muchas de las dependencias funcionaban en la dirección
correcta.
HA FALLECIDO
Desafortunadamente, la comercialización de este producto no fue muy bien. Teradyne era una empresa
que vendía equipos de prueba. No entendíamos cómo entrar en el mercado de equipos de oficina.
Después de repetidos intentos durante dos años, nuestro CEO se dio por vencido y, lamentablemente,
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 324 324
abandonó la solicitud de patente. La patente fue recogida por la empresa que presentó la solicitud tres
meses después de que nosotros la solicitamos; por lo tanto, entregamos todo el mercado de correo de voz
y desvío de llamadas electrónicas.
¡Ay!
Por otro lado, no me puedes culpar por esas molestas máquinas que ahora plagan nuestra existencia.
ER había fallado como producto, pero todavía teníamos todo este hardware y software que podíamos
usar para mejorar nuestras líneas de productos existentes. Además, nuestro éxito de marketing con
VRS nos convenció de que debíamos ofrecer un sistema de respuesta de voz para interactuar con los
artesanos del teléfono que no dependiera de nuestros sistemas de prueba.
Así nació CDS, el Sistema de Despacho de Artesanías. CDS era esencialmente ER, pero se
enfocaba específicamente en el dominio muy limitado de administrar el despliegue de reparadores
telefónicos en el campo.
Cuando se descubrió un problema en una línea telefónica, se creó un ticket de problema en el centro de
servicio. Los tickets de problemas se guardaban en un sistema automatizado. Cuando un reparador en el
campo terminaba un trabajo, llamaba al centro de servicio para la próxima tarea.
El operador del centro de servicio obtendría el siguiente ticket de problema y se lo leería al reparador.
Nos dispusimos a automatizar ese proceso. Nuestro objetivo era que el reparador en el campo llamara a
CDS y solicitara la siguiente tarea. CDS consultaba el sistema de tickets de problemas y leía los resultados.
CDS mantendría un registro de qué reparador fue asignado a qué informe de problemas e informaría al
sistema de informes de problemas sobre el estado de la reparación.
Había bastantes características interesantes de este sistema que tenían que ver con la
interacción con el sistema de tickets de problemas, el sistema de gestión de la planta y cualquier
sistema de prueba automatizado.
La experiencia con la arquitectura orientada a servicios de ER me hizo querer probar la misma idea de
manera más agresiva. La máquina de estado para un ticket de problema estaba mucho más involucrada
que la máquina de estado para manejar una llamada con ER. Me dispuse a crear lo que ahora se llamaría
una arquitectura de microservicios.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 325 325
Cada transición de estado de cualquier llamada, por insignificante que fuera, provocaba que el sistema
iniciara un nuevo servicio. De hecho, la máquina de estado se externalizó en un archivo de texto que el
sistema leyó. Cada evento que llegaba al sistema desde una línea telefónica se convertía en una transición
en esa máquina de estados finitos. El proceso existente iniciaría un nuevo proceso dictado por la máquina
de estado para manejar ese evento; entonces el proceso existente saldría o esperaría en una cola.
Esta máquina de estado externalizada nos permitió cambiar el flujo de la aplicación sin cambiar ningún
código (el Principio Abierto-Cerrado). Podríamos agregar fácilmente un nuevo servicio, independientemente
de cualquiera de los demás, y conectarlo al flujo modificando el archivo de texto que contenía la máquina
de estado. Incluso podríamos hacer esto mientras el sistema estaba funcionando. En otras palabras,
teníamos hot-swapping y un BPEL (Lenguaje de Ejecución de Procesos de Negocios) efectivo.
El antiguo enfoque de ER de usar archivos de disco para comunicarse entre servicios era demasiado
lento para este cambio mucho más rápido de servicios, por lo que inventamos un mecanismo de memoria
compartida que llamamos 3DBB.12 El 3DBB permitía acceder a los datos por nombre; los nombres que
usamos fueron nombres asignados a cada instancia de máquina de estado.
El 3DBB era excelente para almacenar cadenas y constantes, pero no podía usarse para almacenar
estructuras de datos complejas. La razón de esto es técnica pero fácil de entender. Cada proceso
en MP/M vivía en su propia partición de memoria. Los punteros a datos en una partición de memoria
no tenían significado en otra partición de memoria. Como consecuencia, los datos en el 3DBB no
podían contener punteros. Las cadenas estaban bien, pero los árboles, las listas enlazadas o cualquier
estructura de datos con punteros no funcionaban.
Los tickets de problemas en el sistema de tickets de problemas provienen de muchas fuentes diferentes.
Algunos estaban automatizados y otros eran manuales. Las entradas manuales fueron creadas por
operadores que hablaban con los clientes sobre sus problemas. A medida que los clientes describían sus
problemas, los operadores ingresaban sus quejas y observaciones en un flujo de texto estructurado. Se
veía algo como esto:
Entiendes la idea. El personaje / comenzó un nuevo tema. Después de la barra oblicua había un
código, y después del código había parámetros. Había miles de códigos y un ticket de problema individual
podía tener docenas de ellos en la descripción. Peor,
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 326 326
Nuestro problema era decodificar estas cadenas semi-libres, interpretar y corregir cualquier error, y luego
convertirlas en salida de voz para poder leerlas al reparador, arriba de un poste, escuchando con un
auricular. Esto requería, entre otras cosas, una técnica muy flexible de análisis y representación de datos.
Esa representación de datos tenía que pasar a través de 3DBB, que solo podía manejar cadenas.
Y así, en un avión, volando entre las visitas de los clientes, inventé un esquema que llamé FLD: Field
Labeled Data. Hoy en día llamaríamos a esto XML o JSON. El formato era diferente, pero la idea era la
misma. Los FLD eran árboles binarios que asociaban nombres con datos en una jerarquía recursiva. Los
FLD se pueden consultar mediante una API simple y se pueden traducir hacia y desde un formato de
cadena conveniente que sea ideal para 3DBB.
Entonces, los microservicios se comunican a través del análogo de memoria compartida de sockets
usando un análogo XML, en 1985.
COMUNICACIONES CLARAS
En 1988, un grupo de empleados de Teradyne dejó la empresa para formar una startup llamada Clear
Communications. Me uní a ellos unos meses después. Nuestra misión era construir el software para un
sistema que monitorearía la calidad de las comunicaciones de las líneas T1, las líneas digitales que
transportaban comunicaciones de larga distancia en todo el país. La visión era un monitor enorme con un
mapa de los Estados Unidos atravesado por líneas T1 que parpadeaban en rojo si se degradaban.
Recuerde, las interfaces gráficas de usuario eran completamente nuevas en 1988. Apple Macintosh tenía
solo cinco años. Windows era una broma en ese entonces. Pero Sun Microsystems estaba construyendo
Sparcstations que tenían GUI X-Windows creíbles. Así que optamos por Sun, y por lo tanto con C y UNIX.
Esto fue una puesta en marcha. Trabajábamos de 70 a 80 horas por semana. Tuvimos la visión. Teníamos
la motivación. Teníamos la voluntad. Teníamos la energía. Teníamos la experiencia. Teníamos equidad.
Teníamos sueños de ser millonarios. Estábamos llenos de mierda.
El código C se derramó por todos los orificios de nuestros cuerpos. Lo golpeamos aquí, y
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 327 327
lo empujó allí. Construimos enormes castillos en el aire. Teníamos procesos, colas de mensajes y
arquitecturas grandiosas y superlativas. Escribimos una pila completa de comunicaciones ISO de siete
capas desde cero, hasta la capa de enlace de datos.
Escribimos código GUI. ¡CÓDIGO PEGAJOSO! ¡DIOS MÍO! Escribimos código GOOOOOEY.
Personalmente escribí una función C de 3000 líneas llamada gi(); su nombre significa Intérprete Gráfico.
Fue una obra maestra de goo. No fue la única cosa pegajosa que escribí en Clear, pero fue la más infame.
¿Arquitectura? ¿Estás bromeando? Esto fue una puesta en marcha. No teníamos tiempo para
la arquitectura. ¡Solo código, maldita sea! Código para sus propias vidas!
Así que codificamos. Y codificamos. Y codificamos. Pero, después de tres años, lo que fallamos fue vender.
Oh, tuvimos una instalación o dos. Pero el mercado no estaba particularmente interesado en nuestra gran
visión, y nuestros financistas de capital de riesgo estaban bastante hartos.
Odiaba mi vida en este momento. Vi todo mi esfuerzo y mis sueños derrumbarse. Tuve conflictos en
el trabajo, conflictos en casa por el trabajo y conflictos conmigo mismo.
LA PUESTA EN MARCHA
Dos años antes de esa llamada telefónica, sucedieron dos cosas importantes.
Primero, logré establecer una conexión uucp con una empresa cercana que tenía una conexión uucp
con otra instalación que estaba conectada a Internet. Estas conexiones eran de acceso telefónico, por
supuesto. Nuestra Sparcstation principal (la que está en mi escritorio) usaba un módem de 1200 bps para
llamar a nuestro host uucp dos veces al día. Esto nos dio el correo electrónico y Netnews (una de las
primeras redes sociales donde la gente discutía temas interesantes).
En segundo lugar, Sun lanzó un compilador de C++. Me había interesado en C++ y OO desde 1983,
pero los compiladores eran difíciles de conseguir. Entonces, cuando se presentó la oportunidad, cambié
de idioma de inmediato. Dejé atrás las funciones C de 3000 líneas y comencé a escribir código C++ en
Clear. Y aprendí…
Leo libros. Por supuesto, leí El lenguaje de programación C++ y El manual de referencia de C++ anotado
(The ARM) de Bjarne Stroustrup. Leí el hermoso libro de Rebecca Wirfs Brock sobre diseño orientado a la
responsabilidad: Designing Object Oriented
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 328 328
Software. Leí OOA y OOD y OOP de Peter Coad. Leí Smalltalk-80 de Adele Goldberg. Leí
Estilos y modismos de programación avanzados en C++ de James O.
Coplién. Pero quizás lo más importante de todo es que leí Diseño orientado a objetos con
aplicaciones de Grady Booch.
¡Que nombre! Grady Booch. ¿Cómo podría alguien olvidar un nombre como ese? Además,
¡era el científico jefe de una empresa llamada Rational! ¡Cómo quería ser un científico jefe! Y
entonces leí su libro. Y aprendí, y aprendí, y aprendí…
Fue en uno de esos debates donde se sentaron las bases de los principios SOLID.
Y todo ese debate, y tal vez incluso algo de sentido, me llamó la atención...
TÍO BOB
Uno de los ingenieros de Clear era un joven llamado Billy Vogel. Billy les puso apodos a todos.
Me llamó tío Bob. Sospecho que, a pesar de que mi nombre es Bob, estaba haciendo una
referencia improvisada a JR "Bob" Dobbs (ver https://en.wikipedia.org/wiki/File:Bobdobbs.png).
Al principio lo toleraba. Pero a medida que pasaban los meses, su incesante parloteo de "Tío
Bob,... Tío Bob", en el contexto de las presiones y decepciones de la puesta en marcha,
comenzó a agotarse bastante.
LA LLAMADA TELEFÓNICA
Era un reclutador. Obtuvo mi nombre como alguien que conocía C++ y diseño orientado a
objetos. No estoy seguro de cómo, pero sospecho que tuvo algo que ver con mi presencia
en Netnews.
Dijo que tenía una oportunidad en Silicon Valley, en una empresa llamada Rational. Ellos
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 329 329
La sangre se drenó de mi cara. Sabía lo que era esto. No sé cómo lo supe, pero lo supe. Esta
era la compañía de Grady Booch . ¡Vi ante mí la oportunidad de unir fuerzas con Grady Booch!
ROSA
Me uní a Rational, como programador por contrato, en 1990. Estaba trabajando en el producto
ROSE. Esta era una herramienta que permitía a los programadores dibujar diagramas de
Booch, los diagramas sobre los que Grady había escrito en Análisis y diseño orientados a objetos
con aplicaciones (la Figura A.9 muestra un ejemplo).
ROSE tenía una arquitectura, una verdadera arquitectura. Se construyó en capas verdaderas y las
dependencias entre capas se controlaron adecuadamente. La arquitectura lo hizo liberable,
desarrollable y de despliegue independiente.
Oh, no fue perfecto. Había muchas cosas que todavía no entendíamos sobre los principios
arquitectónicos. Por ejemplo, no creamos una verdadera estructura de complemento.
También caímos en una de las modas más desafortunadas del día: usamos una base de datos
llamada orientada a objetos.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 330 330
Pero, en general, la experiencia fue excelente. Pasé un hermoso año y medio trabajando con
el equipo de Rational en ROSE. Esta fue una de las experiencias intelectualmente más
estimulantes de mi vida profesional.
Una cosa me molestó. Era perverso, pero era cierto. Nadie me llamaba "tío Bob".
Descubrí que me lo perdí. Así que cometí el error de poner "Tío Bob" en mi correo electrónico
y firmas de Netnews. Y el nombre se quedó. Con el tiempo me di cuenta de que era una
marca bastante buena.
ROSE era una gigantesca aplicación C++. Estaba compuesto por capas, con una regla
de dependencia estrictamente aplicada. Esa regla no es la regla que he descrito en este libro.
No apuntamos nuestras dependencias hacia políticas de alto nivel. Más bien, apuntamos
nuestras dependencias en la dirección más tradicional del control de flujo. La GUI apuntaba
a la representación, que apuntaba a las reglas de manipulación, que apuntaban a la base de
datos. Al final, fue este fracaso en dirigir nuestras dependencias hacia la política lo que
contribuyó a la eventual desaparición del producto.
Las bases de datos orientadas a objetos eran una idea relativamente nueva, y el mundo
OO estaba lleno de implicaciones. Todo programador orientado a objetos quería tener una
base de datos orientada a objetos en su sistema. La idea era relativamente simple y
profundamente idealista. La base de datos almacena objetos, no tablas. Se suponía que la
base de datos se parecería a la RAM. Cuando accedías a un objeto, simplemente aparecía
en la memoria. Si ese objeto apunta a otro objeto, el otro objeto aparecerá en la memoria tan
pronto como acceda a él. Fue como magia.
Esa base de datos fue probablemente nuestro mayor error práctico. Queríamos la magia,
pero lo que obtuvimos fue un marco de terceros grande, lento, intrusivo y costoso que hizo
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 331 331
nuestras vidas son un infierno al impedir nuestro progreso en casi todos los niveles.
Esa base de datos no fue el único error que cometimos. El mayor error, de hecho, fue el exceso de arquitectura.
Había muchas más capas de las que he descrito aquí, y cada una tenía su propia marca de sobrecarga de
comunicaciones. Esto sirvió para reducir significativamente la productividad del equipo.
De hecho, después de muchos años de trabajo, inmensas luchas y dos lanzamientos tibios, toda la herramienta
fue desechada y reemplazada por una linda aplicación escrita por un pequeño equipo en Wisconsin.
Y así aprendí que las grandes arquitecturas a veces conducen a grandes fracasos.
La arquitectura debe ser lo suficientemente flexible para adaptarse al tamaño del problema.
La arquitectura para la empresa, cuando todo lo que realmente necesita es una linda herramienta de escritorio,
es una receta para el fracaso.
Uno de mis primeros clientes de consultoría fue Educational Testing Service (ETS). Estaba bajo contrato con
la Junta de Registro del Consejo Nacional de Arquitectos (NCARB) para realizar los exámenes de registro para
los nuevos candidatos a arquitectos.
Cualquiera que desee ser un arquitecto registrado (del tipo que diseña edificios) en los Estados Unidos o
Canadá debe aprobar el examen de registro. Este examen implicó que el candidato resolviera una serie de
problemas arquitectónicos relacionados con el diseño de edificios.
Se le puede dar al candidato un conjunto de requisitos para una biblioteca pública, un restaurante o
una iglesia, y luego pedirle que dibuje los diagramas arquitectónicos apropiados.
Los resultados se recopilarían y guardarían hasta el momento en que un grupo de arquitectos senior
pudiera reunirse como jurado para calificar las presentaciones. Estas reuniones eran eventos grandes y
costosos y eran fuente de mucha ambigüedad y demora.
NCARB quería automatizar el proceso haciendo que los candidatos tomaran los exámenes usando una
computadora y luego que otra computadora hiciera la evaluación y la calificación.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 332 332
NCARB le pidió a ETS que desarrollara ese software y ETS me contrató para reunir un equipo de
desarrolladores para producir el producto.
ETS había dividido el problema en 18 viñetas de prueba individuales. Cada uno requeriría una
aplicación GUI similar a CAD que el candidato usaría para expresar su solución. Una aplicación de
puntuación separada tomaría las soluciones y produciría
puntuaciones.
Mi socio, Jim Newkirk, y yo nos dimos cuenta de que estas 36 aplicaciones tenían grandes similitudes.
Las 18 aplicaciones GUI usaban gestos y mecanismos similares. Las 18 aplicaciones de puntuación
utilizaron todas las mismas técnicas matemáticas. Teniendo en cuenta estos elementos compartidos, Jim
y yo estábamos decididos a desarrollar un marco reutilizable para las 36 aplicaciones. De hecho, vendimos
esta idea a ETS diciendo que pasaríamos mucho tiempo trabajando en la primera aplicación, pero luego
el resto aparecería cada pocas semanas.
En este punto, deberías estar palmeándote la cara o golpeándote la cabeza con este libro. Aquellos de
ustedes que tienen la edad suficiente pueden recordar la promesa de "reutilización" de OO. Todos
estábamos convencidos, en aquel entonces, de que si solo escribía un buen código limpio y orientado a
objetos en C ++, naturalmente produciría montones, montones de código reutilizable.
Así que nos dispusimos a escribir la primera aplicación, que fue la más complicada del lote. Se llamaba
Viñeta Grande.
Los dos trabajamos a tiempo completo en Vignette Grande con miras a crear un marco reutilizable.
Nos tomó un año. A finales de ese año teníamos 45 000 líneas de código de marco y 6000 líneas de
código de aplicación. Entregamos este producto a ETS y nos contrataron para escribir las otras 17
aplicaciones rápidamente.
Así que Jim y yo reclutamos un equipo de otros tres desarrolladores y comenzamos a trabajar en las
siguientes viñetas.
Pero algo salió mal. Descubrimos que el marco reutilizable que habíamos creado no era particularmente
reutilizable. No encajaba bien en las nuevas aplicaciones que se estaban escribiendo. Hubo fricciones
sutiles que simplemente no funcionaron.
Esto fue profundamente desalentador, pero creíamos que sabíamos qué hacer al respecto. Fuimos a
ETS y les dijimos que habría un retraso, que el marco de 45,000 líneas necesitaba ser reescrito, o al
menos reajustado. Les dijimos que llevaría un tiempo más hacerlo.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 333 333
No necesito decirte que ETS no estaba particularmente contento con esta noticia.
Así que empezamos de nuevo. Dejamos a un lado el viejo marco y comenzamos a escribir cuatro
nuevas viñetas simultáneamente. Tomaríamos prestadas ideas y código del marco anterior, pero los
volveríamos a trabajar para que encajen en los cuatro sin modificaciones. Este esfuerzo tomó otro año.
Produjo otro marco de 45.000 líneas, más cuatro viñetas que tenían entre 3000 y 6000 líneas cada una.
No hace falta decir que la relación entre las aplicaciones GUI y el marco siguió la regla de dependencia.
Las viñetas eran complementos del marco. Toda la política de GUI de alto nivel estaba en el marco. El
código de la viñeta era solo pegamento.
La relación entre las aplicaciones de puntuación y el marco fue un poco más compleja. La política de
puntuación de alto nivel estaba en la viñeta. El marco de puntuación se conectó a la viñeta de puntuación.
Por supuesto, ambas aplicaciones eran aplicaciones C++ vinculadas estáticamente, por lo que la noción
de complemento no estaba en nuestras mentes. Y, sin embargo, la forma en que se ejecutaban las
dependencias era coherente con la regla de dependencia.
Habiendo entregado esas cuatro aplicaciones, comenzamos con las siguientes cuatro. Y esta vez
empezaron a salir por la parte de atrás cada pocas semanas, tal como habíamos previsto. El retraso nos
había costado casi un año en nuestro cronograma, por lo que contratamos a otro programador para
acelerar el proceso.
Cumplimos nuestras fechas y nuestros compromisos. Nuestro cliente estaba feliz. Éramos felices.
La vida era buena.
Pero aprendimos una buena lección: no puede crear un marco reutilizable hasta que primero haga un
marco utilizable. Los marcos reutilizables requieren que los construyas junto con varias aplicaciones de
reutilización.
CONCLUSIÓN
Como decía al principio, este apéndice tiene algo de autobiográfico. Llegué a los puntos culminantes
de los proyectos que sentí que tuvieron un impacto arquitectónico. Y, por supuesto, mencioné algunos
episodios que no eran exactamente relevantes para el contenido técnico de este libro, pero que sin
embargo eran significativos.
Por supuesto, esta era una historia parcial. Hubo muchos otros proyectos en los que trabajé.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 334 334
a lo largo de las décadas. También detuve esta historia a propósito a principios de la década de
1990, porque tengo otro libro para escribir sobre los eventos de fines de la década de 1990.
Mi esperanza es que hayas disfrutado de este pequeño viaje por mi carril de la memoria; y que pudiste
aprender algunas cosas en el camino.
1. Una de las historias que escuchamos sobre la máquina en particular en ASC fue que se envió en un
gran camión semirremolque junto con un menaje de muebles. En el camino, el camión golpeó un puente a gran
velocidad. La computadora estaba bien, pero se deslizó hacia adelante y aplastó los muebles en astillas.
3. Imagina la masa de ese disco. ¡Imagina la energía cinética! Un día entramos y vimos poco
virutas de metal que caen del botón del gabinete. Llamamos al hombre de mantenimiento. Nos aconsejó que apagáramos
la unidad. Cuando vino a repararlo, dijo que uno de los cojinetes se había desgastado. Luego nos contó historias sobre
cómo estos discos, si no se reparan, podrían soltarse de sus amarres, atravesar paredes de bloques de hormigón e
incrustarse en los automóviles en el estacionamiento.
5. El número mágico 72 provino de las tarjetas perforadas de Hollerith, que contenían 80 caracteres cada una. Los
últimos 8 caracteres estaban "reservados" para los números de secuencia en caso de que se le cayera la baraja.
7. Tenían una pequeña ventana de plástico transparente que te permitía ver el chip de silicona en el interior y permitía
UV para borrar los datos.
8. Sí, sé que cuando el software se graba en la ROM, se llama firmware, pero incluso el firmware es
realmente todavía suave.
9. RKO7.
11. Nuestra empresa tenía la patente. Nuestro contrato de trabajo dejaba claro que todo lo que inventábamos pertenecía a
nuestra empresa. Mi jefe me dijo: “Nos lo vendiste por un dólar y no te pagamos ese dólar”.
12. Tablero negro tridimensional. Si nació en la década de 1950, es probable que obtenga esta referencia:
Llovizna, llovizna, llovizna, zángano.
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 335 335
ÍNDICE
Números
sistema de memoria compartida 3DBB, proyecto de arqueología Craft Dispatch System, 363
4-TEL, proyectos de arqueología
JEFE, 351–352
Lenguaje C, 349–351
DLU/DRU, 354–356
descripción general de,
339–344 pCCU, 352–354
SAC (computadora del área de servicio), 344–349
VRS, 357–359
8085 computadora, proyectos arqueológicos
4-TEL, 341
JEFE, 351
lenguaje C y, 349–351
DLU/DRU, 356
8086 Microcomputadora Intel, proyecto de arqueología SAC, 347–348
A
Clases abstractas
conclusión, 132
Principio de inversión de dependencia y, 87
restos en Zona de inutilidad, 129–130 colocando
política de alto nivel, 126–128 servicios en Java
como conjunto de, 246
Componentes abstractos, 125–126
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 336 336
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 337 337
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 338 338
Arquitectura, gritando
sobre la web, 197–198
conclusión, 199 marcos
como herramientas, no formas de vida, 198
descripción general de, 195–196 propósito de, 197
arquitecturas comprobables, 198 tema, 196–197
B
Clases base, marcos, 293
Arquitectura del sistema BCE, 202
Beck, Kent, 258-261
Comportamiento (función)
la arquitectura soporta el sistema, 137, 148
Matriz de Eisenhower de importancia frente a urgencia, 16–17
lucha por la antigüedad de la arquitectura sobre la función, 18
mantener abiertas las opciones, 140–142 como valor del software,
14 valor de la función frente a la arquitectura, 15–16 Binarios ,
reubicación, 99–100 Booch, Grady introducción a, 366 trabajando
para, 367 trabajando en el producto ROSE, 368–369 Proyecto de
arqueología BOSS (Sistema Operativo Básico y Programador), 351–
352 en proyecto de arqueología DLU/DRU, 356 Límites
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 339 339
Anatomía de límites
cruce de límites, 176
conclusión, 181
componentes de implementación, 178–
179 monolito temido, 176–178 procesos
locales, 179–180 servicios, 180–181
subprocesos, 179 cruce de límites en
arquitectura limpia, 206 escenario de
arquitectura limpia, 207–208 creación
apropiado, 176 Regla de dependencia
para datos entrantes, 207 Ciclo de ruptura,
Principio de dependencias acíclicas, 117–
118 Gerentes de negocios Matriz de
importancia versus urgencia de Eisenhower, 17 Preferencia por
función versus arquitectura, 15–16 Reglas comerciales
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 340 340
conclusión, 194
creación de entidades, 190–
191 desacoplamiento de la interfaz
de usuario, 287–289 desacoplamiento
de capas, 152–153 casos de uso de
desacoplamiento, 153 diseño para
verificación, 251 en el juego de aventuras Hunt the
Wumpus, 222–223 capacidad de desarrollo independiente,
47 mantenerse cerca de los datos , 67 conexión, 170–
173 declaraciones de política cálculo, 184 modelos de
solicitud/respuesta y, 193–194 en proyecto de arqueología
SAC, 346–347 separación de componentes con líneas
de límite, 165–169 comprensión, 189–190 casos de uso
para, 191– 193, 204
C
herencia del
lenguaje C++ en, 40
aprendizaje, 366
casarse con el marco STL en, 293
polimorfismo en, 42
Aplicación ROSE, 369–370
debilitamiento de la encapsulación, 36–37
lenguaje C
proyecto de arqueología BOSS usando, 351–352
proyecto de arqueología DLU/DRU utilizando, 356
encapsulación en, 34–36 herencia en, 38–40
polimorfismo en, 40–42 rediseño de SAC en, 347–
348
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 341 341
CCU/CMU (unidad de control COLT/unidad de medición COLT), proyecto de arqueología pCCU, 353–354
Características de la
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 342 342
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 343 343
de oficina central)
desacoplamiento, 155–157
Idiomas compilados, 96
Los
reubicables, 99–100
Cohesión de componentes
Acoplamiento de componentes
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 344 344
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 345 345
Sistema de Despacho de Artesanías. Ver CDS (Craft Dispatch System), proyecto de arqueología Datos
comerciales críticos, 190–191 Reglas comerciales críticas, 190–193 Inquietudes transversales Diseño de
servicios para tratar, 247 Enfoque orientado a objetos, 244–245 Flujos de datos cruzados, 226 CRP
(Principio de reutilización común) que influye en la composición de los componentes, 118 descripción
general de, 107–108 diagrama de tensión, 108–110 terminales CRT (tubo de rayos catódicos), proyecto
de arqueología de contabilidad de la Unión, 328–329 ruptura de ciclos, 117–118 efecto de gráfico de
dependencia , 115–117 eliminando la dependencia, 113–115 problemas de compilación semanales, 112–
113
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 346 346
Almacenamiento
de datos en el proyecto de arqueología Laser Trim,
335 predominio de los sistemas de bases de datos debido a los discos,
279–280 en el proyecto de arqueología Union Accounting, 327–328
Arquitectura
anécdota, 281–283
conclusión, 283 detalles,
281 si no hubiera discos,
280–281 descripción general de, 277–
278 rendimiento, 281 bases de datos
relacionales, 278 por qué los sistemas de
bases de datos son tan frecuentes, 279–
280
Arquitectura del sistema DCI, 202
Deadlocks, de variables mutables, 52
Desacoplamiento
como falacia de servicios, 240–241
implementación independiente, 154, 241
desarrollo independiente, 153–154, 241 ejemplo del
problema del gatito, 242–243 capas, 151–152
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 347 347
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 348 348
Entidades, 204
marcos y controladores, 205
marcos que tienden a violar, 293 en el
juego de aventuras Hunt the Wumpus, 223
adaptadores de interfaz, 205 enfoque orientado a
objetos para preocupaciones transversales, 244–245 los
servicios pueden seguir, 240 pruebas siguientes, 250 casos
de uso, 204 qué datos cruzan fronteras, 207 La arquitectura
de implementación determina la facilidad de, 150
componentes, 178–180 componentes como unidades de, 96
impacto de la arquitectura en, 138 pruebas usan modo de
desacoplamiento independiente, 250 a nivel de
implementación, 156–157, 178–179 Enfoques de diseño a.
Ver Arquitectura de organización de código vs., 4 disminución
de la productividad/aumento del costo del código, 5–7 hacerlo
bien, 2 objetivo del bien, 4–5 reducción de la volatilidad de
las interfaces, 88 firma de un desorden, 7–8 Principios SÓLIDOS
de, 57 –59 para capacidad de prueba, 251 Diseño de aplicaciones
C++ orientadas a objetos mediante el método de Booch, 369
Detalle
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 349 349
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 350 350
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 351 351
F
Patrón de fachada, límites parciales, 220
Métricas de fan-in/fan-out, estabilidad de componentes, 122–123
Plumas, Michael, 58
Sistemas de archivos, mitigación del retraso de tiempo, 279–280
Cortafuegos, cruces de límites vía, 176
firmware
en el proyecto de arqueología 4-TEL, 343–344
definiciones de, 256–257 eliminando el cuello
de botella del hardware objetivo, 262–263 línea borrosa
entre el software y, 263–264 obsoleto a medida que el
hardware evoluciona, 256 deja de escribir tanto, 257–258
Resumen del
programa FitNesse de, 163–165
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 352 352
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 353 353
ejemplos de SRP, 67
GRAMO
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 354 354
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 355 355
duplicación, 154–155
capacidad de implementación
independiente, 154 capacidad de desarrollo
independiente, 153–154 dejar opciones abiertas,
150–151 operación, 149 descripción general de,
147–148 tipos de modos de desacoplamiento,
155–158 casos de uso, 148
Componentes independientes
que calculan métricas de estabilidad, 123
comprensión, 121
Despliegue independiente en
proyecto de arqueología 4-TEL, 344 como
falacia de servicios, 241 ejemplo de problema
de gatito, 242–243 en enfoque OO para
preocupaciones transversales, 244–245 descripción general de, 154
Capacidad de desarrollo
independiente como falacia de
servicios, 241 ejemplo de problema de
kitty, 242–243 en enfoque OO para preocupaciones transversales,
244–245 descripción general de, 153–154 de UI y base de datos, 47
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 356 356
j
Jacobson, Ivar, 196, 202
Archivos jar
arquitectura de componentes,
301 componentes como, 96
creación de límites parciales, 219
función de definición de componentes, 313
diseño de servicios basados en componentes, 245–246
Regla Download and Go para, 163 en
modo de desacoplamiento a nivel de fuente, 176
Java
componentes abstractos en, 125
enfoques de organización de código en. Consulte Componentes de
organización de código como archivos jar en, 96 DIP y, 87
declaraciones de importación para dependencias, 184 ejemplo de
ISP, 84–85 casarse con el marco de biblioteca estándar en, 293
marcos de módulo en, 319 paquete por capa adentro, 304–306
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 357 357
Lenguajes
arquitectura limpia y, 223–226
Juego de aventuras Hunt the Wumpus, 222–223
Laser Trim, proyecto de arqueología
Proyecto 4-TEL, 339
resumen de, 334–338
arquitectura en capas
organización del código de paquete por capa, 304–306
relajada, 311–312 por qué se considera mala, 310–311
Enfoque
de capas para la organización del código, 304–306
uso de arquitectura limpia, 202–203 desacoplamiento,
151–152 duplicación de, 155 eliminación del cuello
de botella del hardware de destino, 262–263
capacidad de desarrollo independiente, 154
jerárquico de protección y, 74
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 358 358
política y, 184–187
bibliotecas
ubicación del código fuente, 97–98
binarios reubicables, 99–100 Ciclo de
vida, sistema de soporte de arquitectura, 137
Enlazadores, separación de cargadores, 100–102
Liskov, Barbara, 78 Principio de sustitución de Liskov
(LSP). Ver LSP (principio de sustitución de Liskov)
Lenguaje LISP, programación funcional, 23 Lenguaje
Lisp, ejemplo de cuadrados de enteros, 50–51 Cargadores
enlace, 100–102
binarios reubicables, 99–100
Límites del proceso local, 179–180
LSP (principio de sustitución de Liskov)
arquitectura y, 80 conclusión, 82 definido,
59 guía del uso de la herencia, 78
descripción general de, 78 problema de
ejemplo de cuadrado/rectángulo, 79
violación de, 80–82
METRO
computadora m365
Proyecto de arqueología 4-TEL, 340–341
Proyecto de arqueología Laser Trim, 335–338
Proyecto de arqueología SAC, 345–347
Buzones, los procesos locales se comunican a través de, 180
Conclusión del
componente
principal, 237 como componente
concreto, 91 definido, 232
programación orientada a objetos, 40
polimorfismo, 45 pequeño impacto de la
liberación, 115
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 359 359
abstracción, 127
distancia de la secuencia principal, 130–132
Meyer, Bertrand, 70
Arquitectura de microservicios
en el proyecto de arqueología Craft Dispatch System, 362–363
modo de desacoplamiento, 153 estrategia de implementación, 138
popularidad de, 239 Módems, proyecto de arqueología SAC, 346–
347 Módulos Principio de reutilización común, 107–108 definido,
62 herramientas de gestión, 104 tipos públicos vs. tipos publicados,
319 Principio de equivalencia de reutilización/liberación, 105
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 360 360
Monolitos que
norte
National Council of Architects Registry Board (NCARB), 370–372 .NET, componentes como
DLL, 96 NetNews, presencia del autor en, 367–369 Newkirk, Jim, 371–372 Nygaard, Kristen,
22
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 361 361
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 362 362
PAGS
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 363 363
Modificadores de acceso
a puertos y adaptadores,
318 enfoque de la organización del código, 308–310
desacoplar dependencias con árboles de código fuente, 319–320
Antipatrón periférico de, 320–321
Estabilidad posicional, componente, 122–123
Decisiones prematuras, acoplamiento a, 160–163
“Presentation Domain Data Layering” (Fowler), 305–306
Presentadores
es detalle, 265-269
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 364 364
mutabilidad y, 52
Producto, estudio de caso de ventas de video, 298
Disminución
de la productividad, aumento del costo del código,
5–7 firma de un desorden, 8–9
Los lenguajes de programación
resumen componentes en, 125–126
componentes, 96 tipificados
dinámicamente, 88
ISP y, 85 de
tipo estático, 87
variables en lenguajes funcionales, 51
Paradigmas de programación
programación funcional. Ver Historia de la programación funcional
de, 19–20 programación orientada a objetos. Consulte Introducción
a la programación orientada a objetos de 21–24 Programación estructurada.
Véase Programación estructurada Prueba de disciplina de, 27–28 falta de
programación estructurada, 30–31
R
Condiciones de carrera
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 365 365
inmersión y, 88
ISP y, 85
Herramienta RVM, gestión de módulos, 104
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 366 366
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 367 367
Servicios
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 368 368
Ejemplo de OCP, 72
que se refiere solo a abstracciones, 87–88
Los componentes de la interfaz de usuario reutilizan las reglas del juego mediante, 222–223
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 369 369
Estabilidad, componente
126–127
división, 227–228
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 370 370
historia de, 22
falta de pruebas formales, 30
descripción general de, 26 papel
de la ciencia en, 30–31 papel de
las pruebas en, 31 valor de, 31–
32 Sustitución LSP. Ver
Programación LSP (principio de
mutabilidad y, 52 horario/
orden de ejecución, 179
“Arquitectura” de tres niveles (como topología), 161
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 371 371
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 372 372
La
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 373 373
En
Web
Yourdon, Ed, 29
DE
Zonas de exclusión
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 374 374
evitar, 130
relación abstracción/estabilidad, 128
Zona de dolor, 129
Zona de inutilidad, 129–130
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 375 375
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 376 376
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 377 377
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 378 378
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 379 379
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 380 380
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 381 381
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 382 382
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 383 383
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 384 384
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 385 385
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 386 386
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 387 387
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 388 388
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 389 389
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 390 390
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 391 391
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 392 392
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 393 393
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 394 394
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 395 395
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 396 396
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 397 397
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 398 398
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 399 399
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 400 400
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 401 401
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 402 402
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 403 403
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 404 404
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 405 405
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 406 406
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 407 407
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 408 408
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 409 409
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 410 410
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 411 411
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 412 412
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 413 413
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 414 414
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 415 415
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 416 416
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 417 417
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 418 418
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 419 419
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 420 420
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 421 421
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 422 422
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 423 423
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 424 424
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 425 425
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 426 426
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 427 427
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 428 428
Fragmentos de código
¡GUAU!
eBook www.wowebook.org
Machine Translated by Google 429 429
¡GUAU!
eBook www.wowebook.org