 |
Coders' city Nasza pasja to programowanie!
|
Zobacz poprzedni temat :: Zobacz następny temat |
Autor |
Wiadomość |
Luke

Dołączył: 17 Cze 2007 Posty: 1893 Skąd: Szczecin
|
Wysłany: Czw Lip 14, 2016 1:36 pm OP Temat postu: [TypeScript] Sprawdzenie, czy obiekt implementuje określony interfejs |
|
|
Dla treningu tworzę edytor diagramów i w ramach rozwoju zdecydowałem się za skorzystanie z TypeScriptu, chociażby dlatego, że implementuje nowości nieobsługiwane jeszcze w przeglądarkach i transpiluje je do ECMAScriptu 5.
Chciałbym, aby niektóre bloki mogły mieć zmieniany rozmiar, a inne nie, dodatkowo niektóre np. tylko jednocześnie w pionie i poziomie ("po skosie").
Pierwsze, co nasuwa mi się na myśl, to stworzenie odpowiednich interfejsów, które będzie implementować klasa reprezentująca dany blok - ResizableHorizontal, ResizableVertical, ResizableDiagonal.
Niestety TypeScript nie pozwala na dynamiczne sprawdzenie, czy obiekt implementuje interfejs. (I tak - nie miałem pomysłu na tytuł wątku.) Więc jedyne, co mi pozostaje, to umieszczenie w bazowej dla wszystkich bloków w klasie wielu pól typu Boolean - isHorizontalResizable, isVerticalResizable... a to jakoś nie wygląda dobrze.
Proszę o sugestie, jak można zaimplementować to jakoś ładniej. :)
_________________ Moje projekty | Tani hosting |
|
Powrót do góry |
|
 |
|
marcin_an
Dołączył: 26 Maj 2005 Posty: 18822
|
Wysłany: Czw Lip 14, 2016 3:21 pm Temat postu: |
|
|
Nie piszę w TypeScripcie, więc będę odpowiadał trochę "na ślepo". Ponieważ jednak twój problem jest bardziej ogólnoobiektowy niż TypeScriptowy, nie powinno to stanowić większego kłopotu.
Przede wszystkim wpadłeś w pułapkę problemu circle-ellipse*. I to w podręcznikowym przykładzie! Napraw to, a twój problem zniknie. Najprościej będzie przez przejście na używanie obiektów niemodyfikowalnych. Wtedy będziesz miał hierarchię interfejsów opartą na getterach:- Rectangle
- Square extends Rectangle
- getWidth
- getHeight
- Gwarancja: getWidth() is getHeight() dają ten sam wynik.
Jeśli z jakiegoś powodu musisz mieć mutatory, obetnij problem z drugiej strony. Czyli zdejmij gwarancje z getterów i oprzyj interfejs na setterach. Wtedy masz:- Square
- setSize
- getWidth
- getHeight
- Gwarancja: setSize(x) powoduje, że getWidth() i getHeight() dają x.
- Rectangle extends Square
- setWidth
- setHeight
- setSize
- getWidth
- getHeight
Jeżeli potrzebujsz setterów oraz gwarancji na gettery, rozdziel to na dwie niezależne hierarchie, bo w takiej sytuacji te interfejsy nie są ze sobą związane (pomimo wrażenia, że są**). Jeżeli potrzebujesz mieć między nimi jakiś związek, to dodaj metodę konwertującą:- Square
- setSize
- getSize
- getRectangle - zwraca Rectangle o szerokości i wysokości równej getSize()
- Rectangle
- setWidth
- setHeight
- getWidth
- getHeight
- getBoundingSquare - zwraca kwadrat zawierający*** ten prostokąt
Metody te mogą też być odwrotnie zrobione: tzn. Square ma konstruktor/fabrykę tworzącą go z Rectangle, a Rectangle fabrykę**** tworzącą ze Square.
Sprawa druga: po wpadnięciu w circle-ellipse wybrałeś najgorszą możliwą metodę jego rozwiązania, czyli złamanie podstawowego prawa obiektowości: LSP. Chcesz sprawdzić, czy obiekt jest daną implementacją określonego interfejsu i na tej podstawie zmieniać widoczne***** działanie swojego kodu. Tego nie wolno robić - łamiesz w ten sposób abstrakcję i zaprzeczasz celowi, dal którego w ogóle stosujesz obiektowość.
____ * Znanego także jako "square-rectangle". ** Właśnie z tego wynika problem circle-ellipse - podobieństwo okręgu i elipsy powoduje, że ludziom wydaje się, że są ze sobą związane. *** Alternatywnie lub dodatkowo może to być np. kwadrat zawarty w prostokącie. **** Nie może mieć konstruktora, bo będzie niejednoznaczny - pozostaje tylko fabryka. ***** Co innego zastosowanie tego do optymalizacji, logowania albo napisania "niskopoziomowego" helpera, który z założenia operuje na typach obiektów (ale też: na wszystkich mających sens, a nie kilku z góry wybranych).
|
|
Powrót do góry |
|
 |
Luke

Dołączył: 17 Cze 2007 Posty: 1893 Skąd: Szczecin
|
Wysłany: Czw Lip 14, 2016 3:35 pm OP Temat postu: |
|
|
Mądrze piszesz, ale chyba niezbyt konkretnie opisałem swój problem. :|
Otóż na podstawie listy obiektów chcę później rysować je na płótnie i umożliwiać zmianę rozmiaru, np. tylko po skosie. Chcę uzyskać informację, czy można zmienić rozmiar danego bloku i w jaki sposób.
_________________ Moje projekty | Tani hosting |
|
Powrót do góry |
|
 |
marcin_an
Dołączył: 26 Maj 2005 Posty: 18822
|
Wysłany: Czw Lip 14, 2016 7:04 pm Temat postu: |
|
|
Niczego to nie zmienia. Nadal masz circle-ellipse, i nadal próbujesz to rozwiązać poprzez złamanie LSP.
Ta dodatkowa informacja pozwala jedynie zasugerować, że najlepszym rozwiązaniem będzie prawdopodobnie trzecie. Tyle tylko, że trochę zmodyfikowane. W te chwili zdajesz się mieć klasę odpowiedzialną za rysowanie, która traktuje kwadraty i prostokąty (tudzież bboxy obiektów - na jedno wychodzi) jako pasywną przechowywalnię danych. Natomiast to owe kwadraty i prostokąty powinny zajmować się rysowaniem. A w zasadzie dowolne kszałty, bo w takiej sytuacji przestajesz być ograniczony do dwóch. Zatem model powinien wyglądać tak, jak w załącznikach model1.
Ponieważ prawdopodobnie będziesz też chciał mieć jakieś opcje konfiguracji kształtu do tego, każdy z obiektów może dostarczać własny interfejs konfiguracyjny. Przykładowo w model2 jest to pokazane, gdzie rolę tę pełni Configurator. Gdy aplikacja potrzebuje interfejsu do ustalenia parametrów obiektu x, uzyskuje instancję Configurator poprzez wywołanie x.getConfigurator. Uzyskany obiekt posiada oczywiście referncję "zwrotną" do x. Następnie aplikacja prosi o zainstalowanie się wewnątrz określonego elementu (np. diva) poprzez wywołanie installUi. Metoda ta dodaje wszystkie potrzebne pod-elementy, eventy itd. Obsługa zdarzeń jest wykonywana wewnątrz implementacji Configurator, które - mając referncję do x - ustawia mu co trzeba.
Można iść dalej i uniezależnić generowanie szczegółów interfejsu konfiguracyjnego od konkretnej implementacji Configurator, ale nie chciałem jeszcze bardziej komplikować obrazu sprawy. Gdybyś jednak chciał, to installUi będzie korzystało ze wzorca odwiedzającego, który dostaje informację o kolejnych potrzebnych rodzajach elementów UI (np. polach tekstowych, polach numerycznych, opcjach wyboru, listach, ...) wraz z metainformacjami (tytuł pola, domyślna wartość, walidator, callback do informowania o zmianach, ...) i sam sobie generuje zawartość.
Uwaga: diagramy obydwu przedstawionych modeli są uproszczone i brakuje niektórych metod i parametrów potrzebnych do prawidłowego działania - diagramy te mają jedynie przedstawić omawianą ideę.
Opis: |
|
 Pobierz |
Nazwa pliku: |
model2.png |
Wielkość pliku: |
8.46 KB |
Pobierano: |
62 raz(y) |
Opis: |
|
 Pobierz |
Nazwa pliku: |
model2.uxf |
Wielkość pliku: |
5.73 KB |
Pobierano: |
71 raz(y) |
Opis: |
|
 Pobierz |
Nazwa pliku: |
model1.png |
Wielkość pliku: |
4.22 KB |
Pobierano: |
61 raz(y) |
Opis: |
|
 Pobierz |
Nazwa pliku: |
model1.uxf |
Wielkość pliku: |
2.61 KB |
Pobierano: |
67 raz(y) |
|
|
Powrót do góry |
|
 |
Luke

Dołączył: 17 Cze 2007 Posty: 1893 Skąd: Szczecin
|
Wysłany: Czw Lip 14, 2016 11:30 pm OP Temat postu: |
|
|
Głównie mam problem z tym, w jaki sposób zaimplementować rysowanie ramki wokół elementu oraz ustawianie odpowiednich kursorów, jeśli można zmieniać rozmiary obiektu.
Dziedziczenie raczej odpada, bo TS pozwala dziedziczyć tylko po jednej klasie. Zalecany sposób implementacji mixinów jest wg mnie z kolei zbyt "toporny".
_________________ Moje projekty | Tani hosting |
|
Powrót do góry |
|
 |
marcin_an
Dołączył: 26 Maj 2005 Posty: 18822
|
Wysłany: Pią Lip 15, 2016 12:38 am Temat postu: |
|
|
Luke napisał: | Głównie mam problem z tym, w jaki sposób zaimplementować rysowanie ramki wokół elementu oraz ustawianie odpowiednich kursorów, jeśli można zmieniać rozmiary obiektu. | Przecież opisałem, jak to zrobić. Drugi (ew. trzeci) sposób opisany w moim poprzednim poście.
Luke napisał: | Dziedziczenie raczej odpada, bo TS pozwala dziedziczyć tylko po jednej klasie. Zalecany sposób implementacji mixinów jest wg mnie z kolei zbyt "toporny". | O niczym takim nie pisałem - nie wiem zatem, do czego się tutaj odwołujesz.
|
|
Powrót do góry |
|
 |
|
|
Możesz pisać nowe tematy Możesz odpowiadać w tematach Nie możesz zmieniać swoich postów Nie możesz usuwać swoich postów Nie możesz głosować w ankietach Możesz dodawać załączniki na tym forum Możesz pobierać pliki z tego forum
|
 Debug: strone wygenerowano w 0.07231 sekund, zapytan = 13
|