Das Kapitel durchläuft sieben Probleme, die behoben werden müssen, um Lexi ordnungsgemäß zu entwerfen, einschließlich aller Einschränkungen, die befolgt werden müssen. Jedes Problem wird eingehend analysiert und Lösungen vorgeschlagen., Jede Lösung wird vollständig erklärt, einschließlich Pseudocode und gegebenenfalls einer leicht modifizierten Version der Objektmodellierungstechnik.
Schließlich ist jede Lösung direkt einem oder mehreren Entwurfsmustern zugeordnet. Es wird gezeigt, wie die Lösung eine direkte Implementierung dieses Entwurfsmusters ist.
Die sieben Probleme (einschließlich ihrer Einschränkungen) und ihre Lösungen (einschließlich der Muster, auf die verwiesen wird) lauten wie folgt:
Dokumentstrukturedit
Das Dokument ist „eine Anordnung grundlegender grafischer Elemente“ wie Zeichen, Linien, andere Formen usw.,, dass „erfassen Sie den gesamten Informationsgehalt des Dokuments“ (S. 35). Die Struktur des Dokuments enthält eine Sammlung dieser Elemente, und jedes Element kann wiederum eine Unterstruktur anderer Elemente sein.
Probleme und Einschränkungen
- Text und Grafiken sollten auf die gleiche Weise behandelt werden (dh Grafiken sind keine abgeleitete Textinstanz oder umgekehrt)
- Die Implementierung sollte komplexe und einfache Strukturen auf die gleiche Weise behandeln. Es sollte nicht den Unterschied zwischen den beiden kennen müssen.,
- Spezifische Ableitungen abstrakter Elemente sollten spezielle analytische Elemente haben.
Lösung und Muster
Eine rekursive Komposition ist eine hierarchische Struktur von Elementen, die „immer komplexere Elemente aus einfacheren Elementen“ aufbaut (S. 36). Jeder Knoten in der Struktur kennt seine eigenen Kinder und seine Eltern. Wenn eine Operation für die gesamte Struktur ausgeführt werden soll, ruft jeder Knoten die Operation für seine untergeordneten Elemente auf (rekursiv).
Dies ist eine Implementierung des zusammengesetzten Musters, bei dem es sich um eine Sammlung von Knoten handelt., Der Knoten ist eine abstrakte Basisklasse, und Derivate können entweder Blätter (Singular) oder Sammlungen anderer Knoten sein (die wiederum Blätter oder Sammlungsknoten enthalten können). Wenn eine Operation am übergeordneten Element ausgeführt wird, wird diese Operation rekursiv an die Hierarchie übergeben.
Formatierungsedit
Die Formatierung unterscheidet sich von der Struktur. Die Formatierung ist eine Methode zum Erstellen einer bestimmten Instanz der physischen Struktur des Dokuments. Dazu gehören das Aufteilen von Text in Zeilen, das Verwenden von Bindestrichen,das Anpassen von Randbreiten usw.,
Probleme und Einschränkungen
- Balance zwischen (Formatierungs -) Qualität, Geschwindigkeit und Speicherplatz
- Halten Sie die Formatierung unabhängig (nicht abgekoppelt) von der Dokumentstruktur.
Lösung und Muster
Eine Compositor-Klasse kapselt den Algorithmus, der zum Formatieren einer Komposition verwendet wird. Compositor ist eine Unterklasse des primitiven Objekts der Dokumentstruktur. Einem Compositor ist eine Instanz eines Kompositionsobjekts zugeordnet., Wenn ein Compositor seine Compose()
ausführt, durchläuft er jedes Element seiner zugehörigen Komposition und ordnet die Struktur neu an, indem er Zeilen-und Spaltenobjekte nach Bedarf einfügt.
Der Compositor selbst ist eine abstrakte Klasse, mit der abgeleitete Klassen verschiedene Formatierungsalgorithmen verwenden können (z. B. doppelte Abstände, größere Ränder usw.).)
Das Strategiemuster wird verwendet, um dieses Ziel zu erreichen. Eine Strategie ist eine Methode zum Einkapseln mehrerer Algorithmen, die basierend auf einem sich ändernden Kontext verwendet werden sollen., In diesem Fall sollte die Formatierung unterschiedlich sein, je nachdem, ob Text, Grafiken, einfache Elemente usw., werden formatiert.
Verschönerung der Benutzeroberfläche
Die Möglichkeit, die grafische Oberfläche zu ändern, mit der der Benutzer mit dem Dokument interagiert.,
Probleme und Einschränkungen
- Abgrenzung einer Textseite mit einem Rand um den Bearbeitungsbereich
- Bildlaufleisten, mit denen der Benutzer verschiedene Teile der Seite anzeigen kann
- Benutzeroberflächenobjekte sollten nichts über die Verzierungen wissen
- Vermeiden Sie eine „Explosion von Klassen“, die durch Unterklassen für „jede mögliche Kombination von Verzierungen“ und Elementen (S. 44)
Lösung und Muster
Durch die Verwendung eines transparenten Gehäuses können Elemente, die das Verhalten der Komposition erweitern, zu einer Komposition hinzugefügt werden. , Diese Elemente wie Border und Scroller sind spezielle Unterklassen des Singularelements selbst. Dadurch kann die Komposition erweitert werden, wodurch zustandsähnliche Elemente effektiv hinzugefügt werden. Da diese Erweiterungen Teil der Struktur sind, wird ihre entsprechende Operation()
aufgerufen, wenn die Operation()
der Struktur aufgerufen wird. Dies bedeutet, dass der Kunde keine besonderen Kenntnisse oder Schnittstellen zur Struktur benötigt, um die Verzierungen verwenden zu können.,
Dies ist ein Dekoratormuster, das einem Objekt Verantwortlichkeiten hinzufügt, ohne das Objekt selbst zu ändern.
Unterstützung mehrerer Look-And-Feel-StandardsEdit
Look-and-Feel bezieht sich auf plattformspezifische UI-Standards. Diese Standards „definieren Richtlinien für das Erscheinungsbild und die Reaktion von Anwendungen auf den Benutzer“ (S. 47).
Probleme und Einschränkungen
- Der Editor muss Standards für mehrere Plattformen implementieren, damit er portabel ist
- Einfache Anpassung an neue und aufkommende Standards
- Ermöglichen Laufzeitänderung von Look-and-Feel (dh,: Keine Hardcodierung)
- Haben eine Reihe von abstrakten Elementunterklassen für jede Kategorie von Elementen (Bildlaufleiste, Schaltflächen usw.)
- Enthält für jede abstrakte Unterklasse eine Reihe konkreter Unterklassen, die einen anderen Look-and-Feel-Standard haben können. (Bildlaufleiste mit MotifScrollBar und PresentationScrollBar für Motiv-und Präsentations-Look-and-Feels)
Lösung und Muster
Da die Objekterstellung verschiedener konkreter Objekte zur Laufzeit nicht möglich ist, muss der Objekterstellungsprozess abstrahiert werden., Dies geschieht mit einer abstrakten guiFactory, die die Verantwortung für die Erstellung von UI-Elementen übernimmt. Die abstrakte guiFactory verfügt über konkrete Implementierungen wie MotifFactory, die konkrete Elemente des entsprechenden Typs (MotifScrollBar) erstellt. Auf diese Weise muss das Programm nur nach einer Bildlaufleiste fragen und erhält zur Laufzeit das richtige konkrete Element.
Dies ist eine abstrakte Fabrik. Eine normale Fabrik erstellt konkrete Objekte eines Typs. Eine abstrakte Fabrik erstellt konkrete Objekte unterschiedlicher Art, abhängig von der konkreten Implementierung der Fabrik selbst., Seine Fähigkeit, sich nicht nur auf konkrete Objekte zu konzentrieren, sondern auf ganze Familien konkreter Objekte „unterscheidet es von anderen Schöpfungsmustern, die nur eine Art von Produktobjekt betreffen“ (S. 51).
Unterstützung mehrerer Fenstersystemedit
So wie Look-and-Feel ist anders über Plattformen, so ist die Methode der Umgang mit Windows. Jede Plattform zeigt Fenster unterschiedlich an, legt sie an, verarbeitet Eingaben zu und Ausgaben von Fenstern und Ebenen.,
Probleme und Einschränkungen
- Der Dokumenteditor muss auf vielen der „wichtigen und weitgehend inkompatiblen Fenstersysteme“ ausgeführt werden, die vorhanden sind (S. 52)
- Eine abstrakte Factory kann nicht verwendet werden. Aufgrund unterschiedlicher Standards wird es für jeden Widget-Typ keine gemeinsame abstrakte Klasse geben.
- Erstellen Sie kein neues, nicht standardmäßiges Fenstersystem
Lösung und Muster
Es ist möglich, „unsere eigenen abstrakten und konkreten Produktklassen“ zu entwickeln, da „alle Fenstersysteme im Allgemeinen dasselbe tun“ (S. 52)., Jedes Fenstersystem bietet Operationen zum Zeichnen primitiver Formen, zum Iconify/de-Iconifying, zum Ändern der Größe und zum Aktualisieren des Fensterinhalts.
Eine abstrakte Basis Window
Klasse kann auf die verschiedenen Arten von vorhandenen Fenstern abgeleitet werden, wie Anwendung, iconified, Dialog. Diese Klassen enthalten Vorgänge, die Windows zugeordnet sind, z. B. Umformen, grafisch aktualisieren usw. Jedes Fenster enthält Elemente, deren Funktionen Draw()
von den eigenen zeichnungsbezogenen Funktionen Window
aufgerufen werden.,
Um zu vermeiden, plattformspezifische Fensterunterklassen für jede mögliche Plattform erstellen zu müssen, wird eine Schnittstelle verwendet. DieWindow
Klasse implementiert eineWindow
Implementierung (WindowImp
) abstrakte Klasse. Diese Klasse wird dann wiederum in mehrere plattformspezifische Implementierungen mit jeweils plattformspezifischen Operationen abgeleitet., Daher wird für jeden Typ von Window
– Klassen nur ein Satz von Window
und für jede Plattform nur ein Satz von WindowImp
– Klassen benötigt (anstelle des kartesischen Produkts aller verfügbaren Typen und Plattformen). Darüber hinaus erfordert das Hinzufügen eines neuen Fenstertyps keine Änderung der Plattformimplementierung oder umgekehrt.
Dies ist ein Brückenmuster. Window
und WindowImp
sind unterschiedlich, aber verwandt., Window
befasst sich mit windowing im Programm und WindowImp
befasst sich mit windowing auf einer Plattform. Einer von ihnen kann sich ändern, ohne jemals den anderen ändern zu müssen. Das Brückenmuster ermöglicht es diesen beiden „getrennten Klassenhierarchien, zusammenzuarbeiten, auch wenn sie sich unabhängig voneinander entwickeln“ (S. 54).
Benutzeroperationsedit
Alle Aktionen, die der Benutzer mit dem Dokument ausführen kann, von der Texteingabe über das Ändern der Formatierung bis hin zum Beenden, Speichern usw.,
Probleme und Einschränkungen
- Auf Operationen muss über verschiedene Eingaben zugegriffen werden, z. B. über eine Menüoption und eine Tastenkombination für denselben Befehl
- Jede Option verfügt über eine Schnittstelle, die modifizierbar sein sollte
- Operationen werden in mehreren verschiedenen Klassen implementiert
- Um eine Kopplung zu vermeiden, dürfen nicht viele Abhängigkeiten zwischen Implementierungs-und Benutzerschnittstellenklassen bestehen.,
- Undo-und Redo-Befehle müssen bei den meisten Dokumentänderungsvorgängen unterstützt werden, ohne dass die Anzahl der Undo-Ebenen beliebig begrenzt ist
- – Funktionen sind nicht realisierbar, da sie nicht einfach rückgängig gemacht/wiederholt werden können, sind nicht leicht mit einem Status verknüpft und schwer zu erweitern oder wiederzuverwenden.
- Menüs sollten wie hierarchische zusammengesetzte Strukturen behandelt werden. Daher ist ein Menü ein Menüpunkt, der Menüelemente enthält, die andere Menüelemente usw. enthalten können.,
Lösung und Muster
Jedes Menüelement wird stattdessen mit einem Befehlsobjekt instanziiert, anstatt mit einer Liste von Parametern instanziiert zu werden.
Command ist ein abstraktes Objekt, das nur eine einzelne abstrakteExecute()
– Methode hat. Abgeleitete Objekte erweitern dieExecute()
– Methode entsprechend (dh die PasteCommand.Execute()
würde den Zwischenablage-Puffer des Inhalts verwenden). Diese Objekte können von Widgets oder Schaltflächen genauso einfach verwendet werden wie von Menüelementen.,
Um Undo und redo zu unterstützen, wird Command
auch Unexecute()
und Reversible()
. In abgeleiteten Klassen enthält ersterer Code, der diesen Befehl rückgängig macht, und letzterer gibt einen booleschen Wert zurück, der definiert, ob der Befehl nicht rückgängig gemacht werden kann. Reversible()
erlaubt, dass einige Befehle nicht rückgängig gemacht werden können, z. B. ein Speicherbefehl.
Alle ausgeführten Commands
werden in einer Liste mit der Methode gespeichert, einen“ vorhandenen “ Marker direkt nach dem zuletzt ausgeführten Befehl beizubehalten., Eine Rückgängig-Anforderung ruft die Command.Unexecute()
direkt vor „present“ auf und verschiebt dann „present“ um einen Befehl zurück. Umgekehrt ruft eine Redo
– Anforderung Command.Execute()
nach „present“ auf und verschiebt „present“ nach vorne.
DieserCommand
Ansatz ist eine Implementierung des Befehlsmusters. Es kapselt Anforderungen in Objekten und verwendet eine gemeinsame Schnittstelle, um auf diese Anforderungen zuzugreifen. Somit kann der Client verschiedene Anforderungen verarbeiten und Befehle können in der gesamten Anwendung verteilt sein.,
Rechtschreibprüfung und Silbentrennung
Dies ist die Fähigkeit des Dokumenteditors, den Inhalt eines Dokuments textlich zu analysieren. Obwohl es viele Analysen gibt, die durchgeführt werden können, stehen Rechtschreibprüfung und Silbentrennung im Mittelpunkt.
Probleme und Einschränkungen
- Erlauben Sie mehrere Möglichkeiten, die Rechtschreibung zu überprüfen und Orte für die Silbentrennung zu identifizieren
- Erlauben Sie eine Erweiterung für zukünftige Analysen (z. B. Wortzahl, Grammatikprüfung)
- Iterieren Sie den Inhalt eines Textes ohne Zugriff auf die tatsächliche Struktur des Textes (z.,, array, verknüpfte Liste, Zeichenfolge)
- Erlauben Sie jede Art der Durchquerung des Dokuments(Anfang bis Ende, Ende bis Anfang, alphabetische Reihenfolge usw.)
Lösung und Muster
Durch Entfernen des ganzzahligen Index aus dem Basiselement kann eine andere Iterationsschnittstelle implementiert werden. Dies erfordert zusätzliche Methoden zum Durchlaufen und Abrufen von Objekten. Diese Methoden werden in eine abstrakte Iterator
Schnittstelle eingefügt., Jedes Element implementiert dann eine Ableitung der Iterator
, abhängig davon, wie dieses Element seine Liste beibehält (ArrayIterator
, LinkListIterator
usw.).
Funktionen zum Durchlaufen und Abrufen werden in die abstrakte Iteratorschnittstelle eingefügt. Zukünftige Iteratoren können basierend auf dem Typ der Liste abgeleitet werden, durch die sie iteriert werden, z. B. Arrays oder verknüpfte Listen. Unabhängig davon, welche Art von Indizierungsmethode eine Implementierung des Elements verwendet, verfügt es über den entsprechenden Iterator.
Dies ist eine Implementierung des Iterator-Muster., Es ermöglicht dem Client, jede Objektsammlung zu durchlaufen, ohne direkt auf den Inhalt der Sammlung zugreifen zu müssen, oder sich Gedanken über die Art der Liste zu machen, die die Struktur der Sammlung verwendet.
Nun, da Traversal behandelt wurde, ist es möglich, die Elemente einer Struktur zu analysieren. Es ist nicht möglich, jede Art von Analyse in die Elementstruktur selbst zu integrieren; Jedes Element müsste codiert werden, und ein Großteil des Codes wäre für ähnliche Elemente gleich.
Stattdessen wird eine generische CheckMe()
– Methode in die abstrakte Klasse des Elements integriert., Jeder Iterator erhält einen Verweis auf einen bestimmten Algorithmus (z. B. Rechtschreibprüfung, Grammatikprüfung usw.).). Wenn dieser Iterator seine Sammlung durchläuft, ruft er die CheckMe
jedes Elements auf und übergibt den angegebenen Algorithmus. CheckMe
gibt dann einen Verweis auf sein Element zur Analyse an diesen Algorithmus zurück.
Um eine Rechtschreibprüfung durchzuführen, erhält ein Front-to-End-Iterator einen Verweis auf ein SpellCheck
– Objekt., Der Iterator greift dann auf jedes Element zu und führt seine CheckMe()
– Methode mit dem Parameter SpellCheck
aus. Jede CheckMe
würde dann die SpellCheck
aufrufen und einen Verweis auf das entsprechende Element übergeben.
Auf diese Weise kann jeder Algorithmus mit jeder Traversalmethode verwendet werden, ohne dass ein Hardcode miteinander gekoppelt wird. Beispielsweise kann Find als“ find next „oder“ find previous „verwendet werden, je nachdem, ob ein“ Forward „- Iterator oder ein“ Backward “ – Iterator verwendet wurde.,
Zusätzlich können die Algorithmen selbst für den Umgang mit verschiedenen Elementen verantwortlich sein. Beispielsweise würde einSpellCheck
-Algorithmus einGraphic
– Element ignorieren, anstatt jedesGraphic
– abgeleitete Element programmieren zu müssen, um sich nicht an einSpellCheck
.