el capítulo pasa por siete problemas que deben abordarse para diseñar correctamente Lexi, incluidas las restricciones que deben seguirse. Cada problema se analiza en profundidad y se proponen soluciones., Cada solución se explica en su totalidad, incluyendo pseudo-código y una versión ligeramente modificada de la técnica de modelado de objetos cuando sea apropiado.
finalmente, cada solución está asociada directamente con uno o más patrones de diseño. Se muestra cómo la solución es una implementación directa de ese patrón de diseño.
los siete problemas (incluyendo sus restricciones) y sus soluciones (incluyendo los patrones referenciados), son los siguientes:
Document StructureEdit
el documento es «una disposición de elementos gráficos básicos» como caracteres, líneas, otras formas, etc.,, que «capturan el contenido total de la información del documento» (PP. 35). La estructura del documento contiene una colección de estos elementos, y cada elemento puede a su vez ser una subestructura de otros elementos.
problemas y restricciones
- El texto y los gráficos deben tratarse de la misma manera (es decir, los gráficos no son una instancia derivada de texto, ni viceversa)
- la implementación debe tratar las estructuras complejas y simples de la misma manera. No debería tener que saber la diferencia entre los dos.,
- Las derivadas específicas de elementos abstractos deben tener elementos analíticos especializados.
solución y patrón
una composición recursiva es una estructura jerárquica de elementos, que construye» elementos cada vez más complejos a partir de elementos más simples » (PP.36). Cada nodo de la estructura conoce a sus propios hijos y a su padre. Si se va a realizar una operación en toda la estructura, cada nodo llama a la operación en sus hijos (recursivamente).
Esta es una implementación del patrón compuesto, que es una colección de nodos., El nodo es una clase base abstracta, y las derivadas pueden ser hojas (singular), o colecciones de otros nodos (que a su vez pueden contener hojas o nodos-colección). Cuando se realiza una operación en el padre, esa operación se transmite recursivamente por la jerarquía.
FormattingEdit
el Formato difiere de la estructura. El formateo es un método para construir una instancia particular de la estructura física del documento. Esto incluye dividir el texto en líneas, usar guiones, ajustar el ancho de los márgenes, etc.,
problemas y restricciones
- equilibrio entre calidad (formato), velocidad y espacio de almacenamiento
- Mantener el formato independiente (desacoplado) de la estructura del documento.
solución y patrón
una clase de Compositor encapsulará el algoritmo utilizado para formatear una composición. Compositor es una subclase del objeto primitivo de la estructura del documento. Un Compositor tiene una instancia asociada de un objeto Composition., Cuando un Compositor ejecuta su Compose()
, itera a través de cada elemento de su composición asociada, y reorganiza la estructura insertando objetos de fila y columna según sea necesario.
el Compositor en sí es una clase abstracta, lo que permite que las clases derivadas utilicen diferentes algoritmos de formato (como espaciado doble, márgenes más amplios, etc.).)
El Patrón de Estrategia se utiliza para lograr este objetivo. Una estrategia es un método de encapsular múltiples algoritmos para ser utilizados en base a un contexto cambiante., En este caso, el formato debe ser diferente, dependiendo de si el texto, gráficos, elementos simples, etc., están siendo formateados.
embellecer la interfaz de Usuarioeditar
la capacidad de cambiar la interfaz gráfica que el usuario utiliza para interactuar con el documento.,
problemas y restricciones
- demarcar una página de texto con un borde alrededor del área de edición
- barras de desplazamiento que permiten al usuario ver diferentes partes de la página
- Los Objetos de la interfaz de usuario no deben conocer los adornos
- Evitar una «explosión de clases» que sería causada por la subclase de «cada posible combinación de adornos» y elementos (p. 44)
solución y patrón
el uso de un recinto transparente permite añadir a una composición elementos que aumentan el comportamiento de la composición. , Estos elementos, como Border y Scroller, son subclases especiales del elemento singular en sí. Esto permite que la composición se aumente, agregando efectivamente elementos similares a Estados. Dado que estos aumentos son parte de la estructura, se llamará a su Operation()
cuando se llame al Operation()
de la estructura. Esto significa que el cliente no necesita ningún conocimiento especial o interfaz con la estructura para utilizar los adornos.,
Este es un patrón de decorador, uno que agrega responsabilidades a un objeto sin modificar el objeto en sí.
compatible con múltiples estándares de aspecto y sensación Edit
El aspecto y la sensación se refiere a los estándares de interfaz de usuario específicos de la plataforma. Estos estándares «definen pautas para cómo las aplicaciones aparecen y reaccionan ante el usuario» (PP. 47).
problemas y restricciones
- El editor debe implementar estándares de múltiples plataformas para que sea portátil
- adaptarse fácilmente a estándares nuevos y emergentes
- permitir el cambio de apariencia en tiempo de ejecución (p. ej.,: No hard-coding)
- Tener un conjunto de subclases elementales abstractas para cada categoría de elementos (barra de desplazamiento, botones, etc.)
- Tener un conjunto de subclases concretas para cada subclase abstracta que puede tener un estándar de apariencia diferente. (La barra de desplazamiento tiene MotifScrollBar y PresentationScrollBar para Motif y Presentation look-and-feels)
solución y patrón
dado que la creación de objetos de diferentes objetos concretos no se puede hacer en tiempo de ejecución, el proceso de creación de objetos debe abstraerse., Esto se hace con una guía abstracta, que asume la responsabilidad de crear elementos de interfaz de usuario. La guiFactory abstracta tiene implementaciones concretas, como MotifFactory, que crea elementos concretos del tipo apropiado (MotifScrollBar). De esta manera, el programa solo necesita pedir una barra de desplazamiento y, en tiempo de ejecución, se le dará el elemento concreto correcto.
Esta es una fábrica abstracta. Una fábrica normal crea objetos concretos de un tipo. Una fábrica abstracta crea objetos concretos de diferentes tipos, dependiendo de la implementación concreta de la propia fábrica., Su capacidad para centrarse no solo en objetos concretos ,sino en familias enteras de objetos concretos «lo distingue de otros patrones de creación, que involucran solo un tipo de objeto de producto» (PP.51).
Soporta múltiples sistemas de Ventanaseditar
así como la apariencia es diferente en todas las plataformas, también lo es el método de manejo de ventanas. Cada plataforma muestra, presenta, maneja la entrada y la salida de, y capas de ventanas de manera diferente.,
problemas y restricciones
- El editor de documentos debe ejecutarse en muchos de los «sistemas de ventanas importantes y en gran parte incompatibles» que existen (p. 52)
- No se puede usar una fábrica abstracta. Debido a los diferentes estándares, no habrá una clase abstracta común para cada tipo de widget.
- NO crear un nuevo sistema de ventanas no estándar
solución y patrón
es posible desarrollar «nuestras propias clases de productos abstractos y concretos», porque «todos los sistemas de ventanas hacen generalmente lo mismo» (p. 52)., Cada sistema de ventanas proporciona operaciones para dibujar formas primitivas, iconificar/desiconificar, redimensionar y actualizar el contenido de las ventanas.
una base abstracta Window
la clase se puede derivar a los diferentes tipos de ventanas existentes, como aplicación, iconificado, diálogo. Estas clases contendrán operaciones que están asociadas con windows,como remodelar, actualizar gráficamente, etc. Cada ventana contiene elementos, cuyas funciones Draw()
son llamadas por las funciones propias de draw-related de Window
.,
para evitar tener que crear subclases de ventana específicas para cada plataforma posible, se utilizará una interfaz. El Window
clase implementará un Window
aplicación (WindowImp
) clase abstracta. A su vez, esta clase se derivará en múltiples implementaciones específicas de la plataforma, cada una con operaciones específicas de la plataforma., Por lo tanto, solo se necesita un conjunto de clases Window
para cada tipo de clases Window
, y solo se necesita un conjunto de clases WindowImp
para cada plataforma (en lugar del producto cartesiano de todos los tipos y plataformas disponibles). Además, agregar un nuevo tipo de ventana no requiere ninguna modificación de la implementación de la plataforma, o viceversa.
Este es un patrón de Puente. Window
y WindowImp
son diferentes, pero relacionados., Window
se ocupa de ventanas en el programa, y WindowImp
se ocupa de ventanas en una plataforma. Uno de ellos puede cambiar sin tener que modificar el otro. El patrón de Puente permite que estas dos «jerarquías de clase separadas trabajen juntas incluso mientras evolucionan independientemente» (p. 54).
User OperationsEdit
todas las acciones que el usuario puede tomar con el documento, que van desde introducir texto, cambiar el formato, salir, guardar, etc.,
problemas y restricciones
- Las operaciones deben ser accedidas a través de diferentes entradas, como una opción de menú y un atajo de teclado para el mismo comando
- Cada opción tiene una interfaz, que debe ser modificable
- Las operaciones se implementan en varias clases diferentes
- Para evitar el acoplamiento, no debe haber muchas dependencias entre la implementación y las clases de interfaz de usuario.,
- Los comandos Deshacer y rehacer deben ser compatibles con la mayoría de las operaciones de cambio de documentos, sin límite arbitrario en el número de niveles de deshacer las funciones
- no son viables, ya que no deshacen/rehacen fácilmente, no se asocian fácilmente con un estado y son difíciles de extender o reutilizar.
- Los menús deben ser tratados como estructuras compuestas jerárquicas. Por lo tanto, un menú es un elemento de menú que contiene elementos de menú que pueden contener otros elementos de menú, etc.,
solución y patrón
Cada elemento del menú, en lugar de ser instanciado con una lista de parámetros, se realiza con un objeto de comando.
el comando es un objeto abstracto que solo tiene un único método abstract Execute()
. Los objetos derivados extienden el método Execute()
apropiadamente (es decir, el PasteCommand.Execute()
utilizaría el buffer del portapapeles del contenido). Estos objetos pueden ser utilizados por widgets o botones tan fácilmente como pueden ser utilizados por elementos de menú.,
Para el apoyo de deshacer y rehacer, Command
también se da Unexecute()
y Reversible()
. En las clases derivadas, la primera contiene código que deshará ese comando, y la segunda devuelve un valor booleano que define si el comando se puede deshacer. Reversible()
permite que algunos comandos no se puedan deshacer, como un comando Save.
Todos los ejecutadosCommands
se mantienen en una lista con un método para mantener un marcador «presente» directamente después del comando ejecutado más recientemente., Una solicitud para deshacer llamará a Command.Unexecute()
directamente antes de «present», luego moverá «present» hacia atrás un comando. Por el contrario, una solicitud Redo
llamará a Command.Execute()
después de» presente», y moverá» presente » hacia adelante.
Este Command
es una implementación del patrón de comandos. Encapsula solicitudes en objetos y utiliza una interfaz común para acceder a esas solicitudes. Por lo tanto, el cliente puede manejar diferentes solicitudes, y los comandos se pueden dispersar por toda la aplicación.,
corrector ortográfico y Guioneditar
Esta es la capacidad del editor de documentos para analizar textualmente el contenido de un documento. Aunque hay muchos análisis que se pueden realizar, la revisión ortográfica y el formato de guiones son el enfoque.
problemas y restricciones
- Permiten múltiples formas de comprobar la ortografía e identificar lugares para la separación
- permiten la expansión para análisis futuros (por ejemplo, recuento de palabras, comprobación de gramática)
- Ser capaz de iterar a través del contenido de un texto sin acceso a la estructura real del texto (por ejemplo ,,, array, linked list, string)
- Permite cualquier tipo de recorrido del documento (de principio a fin, de fin a principio, orden alfabético, etc.)
solución y patrón
la eliminación del índice basado en enteros del elemento básico permite implementar una interfaz de iteración diferente. Esto requerirá métodos adicionales para el recorrido y la recuperación de objetos. Estos métodos se ponen en una interfaz abstracta Iterator
., Cada elemento implementa una derivación del Iterator
, dependiendo de cómo ese elemento mantenga su lista (ArrayIterator
, LinkListIterator
, etc.).
Las funciones para el recorrido y la recuperación se colocan en la interfaz del iterador abstracto. Los iteradores futuros se pueden derivar en función del tipo de lista a través de la que iterarán, como matrices o listas vinculadas. Por lo tanto, no importa qué tipo de método de indexación utilice cualquier implementación del elemento, tendrá el iterador apropiado.
Esta es una implementación del patrón Iterador., Permite al cliente recorrer cualquier colección de objetos, sin necesidad de acceder directamente al contenido de la colección, ni preocuparse por el tipo de lista que utiliza la estructura de la colección.
ahora que se ha manejado el recorrido, es posible analizar los elementos de una estructura. No es factible construir cada tipo de análisis en la estructura de los elementos; cada elemento tendría que ser codificado, y gran parte del código sería el mismo para elementos similares.
en su lugar, un método genérico CheckMe()
está integrado en la clase abstracta del elemento., A cada iterador se le da una referencia a un algoritmo específico (como el corrector ortográfico, el corrector gramatical, etc.).). Cuando ese iterador itera a través de su colección, llama a cada elemento CheckMe
, pasando el algoritmo especificado. CheckMe
luego pasa una referencia a su elemento a dicho algoritmo para su análisis.
Por lo tanto, para realizar una revisión ortográfica, un iterador front-to-end recibiría una referencia a un objeto SpellCheck
., El iterador accedería entonces a cada elemento, ejecutando su método CheckMe()
con el parámetro SpellCheck
. Cada CheckMe
entonces llaman el SpellCheck
, pasando una referencia al elemento apropiado.
de esta manera, cualquier algoritmo se puede utilizar con cualquier método transversal, sin el acoplamiento de código duro uno con el otro. Por ejemplo, Find se puede usar como «find next» o «find previous», dependiendo de si se usó un iterador «forward» o un iterador «backwards».,
además, los propios algoritmos pueden ser responsables de tratar con diferentes elementos. Por ejemplo, un SpellCheck
algoritmo de ignorar un Graphic
elemento, en lugar de tener que programar cada Graphic
elemento derivado a no enviar a un SpellCheck
.