w rozdziale omówiono siedem problemów, które należy rozwiązać, aby prawidłowo zaprojektować Lexi, w tym wszelkie ograniczenia, których należy przestrzegać. Każdy problem jest dogłębnie analizowany i proponowane są rozwiązania., Każde rozwiązanie jest wyjaśnione w całości, włączając w to pseudo-kod i nieco zmodyfikowaną wersję techniki modelowania Obiektowego.
wreszcie, każde rozwiązanie jest powiązane bezpośrednio z jednym lub kilkoma wzorcami projektowymi. Pokazano, w jaki sposób rozwiązanie jest bezpośrednią implementacją tego wzorca projektowego.
siedem problemów (w tym ich ograniczenia) i ich rozwiązania (w tym wzorce) są następujące:
struktura Dokumentuedytuj
dokument jest „układem podstawowych elementów graficznych”, takich jak znaki, linie, inne kształty itp.,, że „wychwytuje całkowitą treść informacyjną dokumentu”(s. 35). Struktura dokumentu zawiera zbiór tych elementów, a każdy element może z kolei być podkonstrukcją innych elementów.
problemy i ograniczenia
- tekst i grafika powinny być traktowane w ten sam sposób (tzn. grafika nie jest pochodną instancji tekstu, ani odwrotnie)
- implementacja powinna traktować złożone i proste struktury w ten sam sposób. Nie powinien znać różnicy między nimi.,
- specyficzne pochodne elementów abstrakcyjnych powinny mieć specjalistyczne elementy analityczne.
rozwiązanie i wzór
kompozycja rekurencyjna jest hierarchiczną strukturą elementów, która buduje „coraz bardziej złożone elementy z prostszych” (s. 36). Każdy węzeł w strukturze wie o swoich własnych dzieciach i rodzicach. Jeśli operacja ma być wykonana na całej strukturze, każdy węzeł wywołuje operację na swoich dzieciach (rekurencyjnie).
jest to implementacja wzorca złożonego, który jest zbiorem węzłów., Węzeł jest abstrakcyjną klasą bazową, a pochodnymi mogą być liście (liczba pojedyncza) lub zbiory innych węzłów (które z kolei mogą zawierać liście lub zbiory-węzły). Gdy operacja jest wykonywana na rodzicu, operacja ta jest rekurencyjnie przekazywana w dół hierarchii.
Formatowanieedit
formatowanie różni się od struktury. Formatowanie jest metodą konstruowania konkretnej instancji struktury fizycznej dokumentu. Obejmuje to dzielenie tekstu na linie, używanie myślników, dostosowanie szerokości marginesów itp.,
problemy i ograniczenia
- balans między (formatowaniem) jakością, szybkością i przestrzenią dyskową
- Zachowaj formatowanie niezależne (niezwiązane) od struktury dokumentu.
rozwiązanie i wzorzec
Klasa Compositor hermetyzuje algorytm używany do formatowania kompozycji. Compositor jest podklasą prymitywnego obiektu struktury dokumentu. Komponent ma powiązaną instancję obiektu kompozycji., Gdy komponent uruchamia swój Compose()
, iterata przez każdy element powiązanej kompozycji i zmienia strukturę, wstawiając Obiekty wierszy i kolumn w razie potrzeby.
Sam Compositor jest klasą abstrakcyjną, pozwalającą klasom pochodnym korzystać z różnych algorytmów formatowania (takich jak podwójne odstępy, szersze marginesy itp.)
do osiągnięcia tego celu służy wzorzec strategii. Strategia jest metodą enkapsulacji wielu algorytmów, które mają być używane w oparciu o zmieniający się kontekst., W tym przypadku formatowanie powinno być różne, w zależności od tego, czy tekst, grafika, proste elementy itp., są formatowane.
upiększanie interfejsu Użytkownikaedytuj
możliwość zmiany interfejsu graficznego, którego użytkownik używa do interakcji z dokumentem.,
problemy i ograniczenia
- Odgranicz stronę tekstu obramowaniem wokół obszaru edycji
- paski przewijania, które pozwalają użytkownikowi przeglądać różne części strony
- obiekty interfejsu użytkownika nie powinny wiedzieć o ozdobach
- unikaj „eksplozji klas”, która byłaby spowodowana podklasowaniem dla „każdej możliwej kombinacji ozdobników” i elementów (str. 44)
rozwiązanie i wzór p >
zastosowanie przezroczystej obudowy umożliwia dodawanie do kompozycji elementów zwiększających zachowanie kompozycji. , Elementy te, takie jak Border i Scroller, są specjalnymi podklasami samego elementu liczby pojedynczej. Pozwala to na rozbudowę kompozycji, skutecznie dodając elementy przypominające stan. Ponieważ te rozszerzenia są częścią struktury, ich odpowiednie Operation()
zostaną wywołane podczas wywoływania struktury Operation()
. Oznacza to, że Klient nie potrzebuje specjalnej wiedzy ani interfejsu ze strukturą, aby korzystać z upiększeń.,
jest to wzorzec dekoratora, który dodaje obowiązki do obiektu bez modyfikowania samego obiektu.
Obsługa wielu standardów Look-and-Feelededit
Look-and-feel odnosi się do specyficznych dla platformy standardów interfejsu użytkownika. Standardy te „definiują wytyczne dotyczące sposobu, w jaki aplikacje pojawiają się i reagują na użytkownika” (str. 47).
problemy i ograniczenia
- edytor musi zaimplementować standardy wielu platform, aby był przenośny
- z łatwością dostosowując się do nowych i pojawiających się standardów
- pozwalał na zmianę wyglądu w czasie pracy (np.,: No hard-coding)
- mają zestaw abstrakcyjnych podklas elementarnych dla każdej kategorii elementów (ScrollBar, Buttons, etc.)
- mają zestaw konkretnych podklas dla każdej abstrakcyjnej podklasy, które mogą mieć inny standard wyglądu i odczucia. (ScrollBar posiadający MotifScrollBar i PresentationScrollBar dla wyglądu motywu i prezentacji)
rozwiązanie i wzorzec
ponieważ nie można tworzyć różnych konkretnych obiektów w czasie wykonywania, proces tworzenia obiektu musi być abstrakcyjny., Odbywa się to za pomocą abstrakcyjnego guiFactory, który bierze na siebie odpowiedzialność za tworzenie elementów interfejsu użytkownika. Abstrakcyjny guiFactory ma konkretne implementacje, takie jak MotifFactory, który tworzy konkretne elementy odpowiedniego typu (MotifScrollBar). W ten sposób program musi tylko poprosić o pasek przewijania i w czasie działania otrzyma właściwy konkretny element.
To jest fabryka abstrakcji. Zwykła fabryka tworzy betonowe obiekty jednego typu. Fabryka abstrakcyjna tworzy konkretne obiekty różnego typu, w zależności od konkretnej realizacji samej fabryki., Jego zdolność skupiania się nie tylko na konkretnych obiektach, ale na całych rodzinach konkretnych obiektów „odróżnia go od innych wzorów kreacyjnych, które obejmują tylko jeden rodzaj obiektu produktu” (s. 51).
Obsługa wielu systemów Oknedytuj
tak jak wygląd i styl różni się na różnych platformach, tak i sposób obsługi okien. Każda platforma wyświetla, układa, obsługuje wejścia i wyjścia z i warstw okna inaczej.,
problemy i ograniczenia
- edytor dokumentów musi działać na wielu istniejących „ważnych i w dużej mierze niekompatybilnych systemach okien” (str. 52)
- nie można użyć abstrakcyjnej fabryki. Ze względu na różne standardy, nie będzie wspólnej klasy abstrakcyjnej dla każdego typu widżetu.
- nie twórz nowego, niestandardowego systemu okien
rozwiązanie i wzór
możliwe jest opracowanie „własnych abstrakcyjnych i konkretnych klas produktów”, ponieważ „wszystkie systemy okienne zasadniczo robią to samo” (s. 52)., Każdy system okien zapewnia operacje rysowania prymitywnych kształtów, ikonifikacji/de-ikonifikacji, zmiany rozmiaru i odświeżania zawartości okna.
abstrakcyjna bazaWindow
może być pochodna do różnych typów istniejących okien, takich jak aplikacja, ikona, okno dialogowe. Klasy te będą zawierać operacje związane z oknami, takie jak przekształcanie, odświeżanie graficzne itp. Każde okno zawiera elementy, których funkcje Draw()
są wywoływane przez funkcje związane z rysowaniem Window
.,
aby uniknąć konieczności tworzenia podklas okien dla każdej możliwej platformy, zostanie użyty interfejs. KlasaWindow
zaimplementujeWindow
implementację (WindowImp
) klasy abstrakcyjnej. Klasa ta będzie następnie z kolei pochodną do wielu implementacji specyficznych dla platformy, każda z operacjami specyficznymi dla platformy., Dlatego tylko jeden zestaw klas Window
jest potrzebny dla każdego typu Window
I tylko jeden zestaw klas WindowImp
jest potrzebny dla każdej platformy (zamiast iloczynu kartezjańskiego wszystkich dostępnych typów i platform). Dodatkowo dodanie nowego typu okna nie wymaga żadnej modyfikacji implementacji platformy, ani odwrotnie.
To jest wzór mostu. Window
IWindowImp
są różne, ale powiązane., Window
zajmuje się oknami w programie, aWindowImp
zajmuje się oknami na platformie. Jeden z nich może się zmienić bez konieczności modyfikowania drugiego. Wzorzec mostka pozwala tym dwóm „oddzielnym hierarchom klasowym współpracować, nawet gdy ewoluują niezależnie” (s. 54).
operacje Użytkownikaedytuj
wszystkie działania, które użytkownik może wykonać z dokumentem, począwszy od wprowadzania tekstu, zmiany formatowania, zamykania, zapisywania itp.,
problemy i ograniczenia
- operacje muszą być dostępne przez różne wejścia, takie jak opcja menu i skrót klawiaturowy dla tego samego polecenia
- każda opcja ma interfejs, który powinien być modyfikowany
- operacje są zaimplementowane w kilku różnych klasach
- aby uniknąć sprzężenia, nie może być wiele zależności między implementacją a klasami interfejsu użytkownika.,
- polecenia Cofnij i ponów muszą być obsługiwane w większości operacji zmiany dokumentu, bez arbitralnego ograniczenia liczby poziomów Cofnij
- funkcje nie są wykonalne, ponieważ nie są łatwo cofać/ponawiać, nie są łatwo powiązane ze stanem i są trudne do rozszerzenia lub ponownego użycia.
- menu powinno być traktowane jak hierarchiczne struktury kompozytowe. Stąd menu jest elementem menu, który zawiera elementy menu, które mogą zawierać inne elementy menu, itp.,
rozwiązanie i wzorzec
każda pozycja menu, zamiast tworzenia instancji z listą parametrów, jest wykonywana za pomocą obiektu Command.
polecenie jest abstrakcyjnym obiektem, który ma tylko jedną abstrakcyjną metodę Execute()
. Obiekty pochodne rozszerzają odpowiednio metodę Execute()
(tzn. PasteCommand.Execute()
wykorzystywałyby bufor schowka zawartości). Obiekty te mogą być używane przez Widżety lub przyciski tak samo łatwo, jak mogą być używane przez elementy menu.,
aby obsługiwać cofanie i ponawianie,Command
podano równieżUnexecute()
IReversible()
. W klasach pochodnych pierwsza zawiera kod, który cofnie to polecenie, a druga Zwraca wartość logiczną, która określa, czy polecenie jest niewykonalne. Reversible()
pozwala niektórym poleceniom być nie do wykonania, np. poleceniu Save.
wszystkie wykonaneCommands
są przechowywane na liście z metodą utrzymywania znacznika „present” bezpośrednio po Ostatnio wykonanym poleceniu., Żądanie cofnięcia wywoła Command.Unexecute()
bezpośrednio przed „present”, a następnie przeniesie” present ” z powrotem o jedno polecenie. Z drugiej strony,Redo
żądanie wywołaCommand.Execute()
po „present” I przesunie „present” do przodu.
to podejścieCommand
jest implementacją wzorca poleceń. Zawiera żądania w obiektach i używa wspólnego interfejsu, aby uzyskać dostęp do tych żądań. W ten sposób klient może obsługiwać różne żądania, a polecenia mogą być rozproszone po całej aplikacji.,
sprawdzanie pisowni i Dzieleniezdaj
jest to zdolność edytora dokumentów do tekstowej analizy zawartości dokumentu. Chociaż istnieje wiele analiz, które można wykonać, sprawdzanie pisowni i formatowanie dzielenia wyrazów są najważniejsze.
problemy i ograniczenia
- pozwalają na wiele sposobów sprawdzania pisowni i identyfikowania miejsc dzielenia wyrazów
- pozwalają na rozbudowę w celu przyszłej analizy (np. liczba słów, sprawdzanie gramatyki)
- mają możliwość iteracji zawartości tekstu bez dostępu do rzeczywistej struktury tekstu (np.,, array, linked list, string)
- pozwalają na dowolne Przejście dokumentu (Od początku do końca, od końca do początku, w porządku alfabetycznym itp.)
rozwiązanie i wzorzec
usunięcie indeksu opartego na liczbach całkowitych z elementu basic pozwala na zaimplementowanie innego interfejsu iteracji. Wymaga to dodatkowych metod do wyszukiwania obiektów i obiektów. Metody te są umieszczone w abstrakcyjnym interfejsieIterator
., Każdy element implementuje wyprowadzenie Iterator
, w zależności od tego, jak ten element zachowuje swoją listę (ArrayIterator
, LinkListIterator
, itd.).
funkcje traversal i retrieval są umieszczane w abstrakcyjnym interfejsie iteratora. Przyszłe Iteratory mogą być wyprowadzane w oparciu o Typ listy, przez którą będą iterowane, np. tablice lub listy połączone. Tak więc, bez względu na to, jakiego typu metody indeksowania używa jakaś implementacja elementu, będzie on posiadał odpowiedni Iterator.
jest to implementacja wzorca iteratora., Pozwala klientowi na przejście przez dowolną kolekcję obiektów, bez konieczności bezpośredniego dostępu do zawartości kolekcji lub obawy o rodzaj listy, z której korzysta struktura kolekcji.
teraz, gdy już zajęto się traversalem, możliwe jest przeanalizowanie elementów struktury. Nie jest możliwe zbudowanie każdego rodzaju analizy w samej strukturze elementu; każdy element musiałby być zakodowany, a duża część kodu byłaby taka sama dla podobnych elementów.
zamiast tego, ogólna metodaCheckMe()
jest wbudowana w klasę abstrakcyjną elementu., Każdy Iterator otrzymuje odniesienie do określonego algorytmu (np. sprawdzanie pisowni, sprawdzanie gramatyki itp.). Gdy Iterator przechodzi przez swoją kolekcję, wywołuje CheckMe
, przekazując określony algorytm. CheckMe
następnie przekazuje odniesienie do swojego elementu z powrotem do wspomnianego algorytmu do analizy.
tak więc, aby sprawdzić pisownię, iterator od początku do końca otrzyma odniesienie do obiektuSpellCheck
., Następnie iterator uzyska dostęp do każdego elementu, wykonując swoją metodę CheckMe()
z parametrem SpellCheck
. Każdy CheckMe
wywoła następnie SpellCheck
, przekazując odniesienie do odpowiedniego elementu.
w ten sposób każdy algorytm może być używany z dowolną metodą traversal, bez łączenia jednego z drugim kodu twardego. Na przykład, Find może być użyty jako „find next” lub „find previous”, w zależności od tego, czy użyto iteratora” forward”, czy też” backward”.,
ponadto same algorytmy mogą być odpowiedzialne za radzenie sobie z różnymi elementami. Na przykład algorytm SpellCheck
ignoruje element Graphic
, zamiast zaprogramować każdy element Graphic
, aby nie wysyłać się do elementu SpellCheck
.