Export page to Open Document format

MIW 2009 LogTalk_UMLgraph

Zrealizował: Mariusz Sasko (4RI)

  • Analyze, describe, prepare a short tutorial as S5 for UMLgraph.
  • provide a simple Prolog API for genarating, and visualizing UML diagrams from Prolog, write a Prolog parser
  • research on existing approaches to representing UML in/with Prolog
  • Analyze, describe, prepare a short tutorial as S5 for LogTalk.
  • check first has anyone made some progress in the integration of these two!
  • provide menas for generating visualizations of UML diagrams from Prolog for LogTalk

Prezentacja

Sprawozdanie

Wstęp.

Cele projektu.

Zadaniem projektu jest wypracowanie metody tworzenia diagramów klas UML, reprezentowanych za pomocą języka Prolog. Sam Prolog nie jest językiem obiektowym, dlatego definicje reprezentowanych klas, musiałyby być całkowicie sztuczne, tworzone za pomocą faktów. Definicje takie nie były by obiektami, tylko opisem obiektu. Projekt zakłada wykorzystanie języka LogTalk, który wzbogaca Prolog o mechanizmy obiektowości i wypracowanie metody generowania diagramów klas za pomocą narzędzia UMLGraph.

Narzędzia.

  • LogTalk.

LogTalk jest rozszerzeniem dla języka Prolog, który wzbogaca go o dodatkowe możliwości. Jest połączeniem programowania obiektowego i logicznego. Pozwala na organizowanie i enkapsulowanie wiedzy reprezentowanej w deklaratywny, charakterystyczny dla języka Prolog sposób. Dzięki temu rozszerzeniu, wykorzystując obiektową reprezentację języka LogTalk, możemy w prosty sposób generować diagramy klas UML.

  • UMLGraph.

Narzędziem, które wykorzystamy do generowania diagramów będzie UMLGraph. Jest on przeznaczony do pracy z językiem Java. Potrafi on interpretować kod i wykorzystując dodatkową notację JavaDoc generować diagramy klas. Aby można go było wykorzystać trzeba było opracowac prosty sposób tłumaczenia języka LogTalk na język Java.

Plan Działania.

  1. Przegląd i opis możliwości UMLGraph.
  2. LogTalk - poznanie semantyki języka i opis najważniejszych elementów.
  3. Opracowanie metody integracji powyższych narzędzi do generowania diagramów klas UML.
  4. Prezentacja działania opracowanej metody.

Analiza funkcjonalności i opis tworzenia diagramów UML za pomocą UMLGraph.

Historia

UMLgraph został napisany przez greckiego profesora uniwersytetu Ateńskiego o imieniu Diomidis Spinellis. Jednakże wkład w budowę projektu mięli także inni, których lista znajduje się na stronie projektu. UMLgraph - Twórcy

Pierwsze publiczne wydanie UMLgraph to wersja 1.15 wydana w 2002-07-26. Od tej pory projekt przeszedł różne modyfikacje i zostały dodane nowe funkcjonalności. Aktualna wersja 5.2 została wydana 2008-12-03, a obecnie trwają prace nad wersją 5.3. Listę zmian można prześledzić na stronie UMLgraph - Zmiany.

Licencja

UMLgraph jest wydawany na bardzo liberalnej licencji BSD(Berkeley Software Distribution License), której podstawą jest wolny dostęp do kodu źródłowego, możliwość jego modyfikacji i ulepszania, a także rozprowadzanie programu w nowej zmodyfikowanej postaci, po to aby mogła korzystać z nich cała społeczność.

Do czego służy UMLGraph?

Jak sama nazwa wskazuje UMLgraph ma być narzędziem, które ma umożliwiać rysowanie diagramów UML (Unified Modeling Language), służących do modelowania obiektów w analizie obiektowej. Opis diagramu UML jest czysto tekstowym opisem reprezentacji, który UMLgraph analizuje i przedstawia w odpowiedniej formie graficznej. Narzędzie to zostało stworzone specjalnie dla języka Java. UMLgraph potrafi analizować składnię i semantykę tego języka i na podstawie analizy tworzyć diagramy UML. Ważną cechą jest to że nie wymaga on pełnych definicji metod czy nawet klas. Definiujemy to co chcemy aby znalazło się na diagramie. Oczywiście mamy możliwość zawierania również pełnych definicji. Narzędzie to daje nam tutaj wolna rękę i pełną swobodę działania.


Instalacja i wymagania UMLGraph!

Aby zainstalować UMLGraph musimy najpierw posiadać zainstalowane następujące pakiety:

  1. Java Jdk 6
  2. GraphViz

Dla systemu Ubuntu 8.4 na którym pracuję, wystarczy wywołać w konsoli następujące komendy, aby zainstalować wymagane pakiety:

sudo apt-get install sun-java6-jdk
sudo apt-get install graphviz

Gdy posiadamy już wszystkie wymagane składniki, pobieramy UMLGraph ze strony: UMLgraph - download i rozpakowujemy do kartoteki /ścieżka/UMLGraph. Po wykonaniu tych kilku prostych kroków możemy przystąpić do tworzenia diagramów.


Co potrafi UMLGraph?

UMLGraph ma bardzo duże możliwości generowania diagramów klas UML, których próbka zostanie zaprezentowana niżej. Posiada on jednak bardzo znaczące ograniczenie. Język który interpretuje to tylko i wyłącznie Java. Każde, nawet najmniejsze błędy syntaktyczne powodują przerwanie działania i zgłoszenie błędu.


Jak w prosty sposób tworzyć diagramy klas UML?

Musimy najpierw zmodyfikować skrypt: /ścieżka/UMLGraph/bin/umlgraph. Po jego otworzeniu w edytorze tekstu np. gedit należy dopisać ścieżki do zmiennych UMLGRAPH_HOME oraz JAVA_HOME. Pierwsza z nich ma zawierać pełną drogę dotarcia do kartoteki /ścieżka/UMLGraph/lib, natomiast ta druga miejsce gdzie znajduje się zainstalowane wersja Java Jdk 6.

Przykład tak zmodyfikowanego pliku znajduje się poniżej:

#!/bin/sh
#
# Unix shell script to run UMLGraph on the specified base file name
# For this to work you must adjust the following defintion of
# UMLGRAPH_HOME to point to the directory where UmlGraph.jar is installed.
#
# $Id: umlgraph,v 1.5 2008/09/12 15:26:53 dds Exp $
#

UMLGRAPH_HOME=/home/mariusz/Pulpit/umlgraph/lib
JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.07

if [ x$2 = x ]
then
	echo usage: umlgraph base_file_name filetype [umlgraph arguments] 1>&2
	echo example: umlgraph MyClass png 1>&2
	echo '(The above will convert MyClass.java into MyClass.png)' 1>&2
	exit 1
else
	BASE=$1
	FILETYPE=$2
	shift 2
	java -classpath "$UMLGRAPH_HOME/UmlGraph.jar:$JAVA_HOME/lib/tools.jar" \
	org.umlgraph.doclet.UmlGraph -package $* -output - $BASE.java |
	dot -T$FILETYPE -o$BASE.$FILETYPE
fi

Po tych krokach, możemy w prosty sposób sprawdzić czy UMLGraph działa poprawnie. W tym celu można skopiować np. plik InterfaceMatcher.java z kartoteki UMLGraph/src/org/umlgraph/doclet/ do /UMLGraph/bin/ i na jego podstawie wygenerować diagram UML. Robimy to w następujący sposób:

./umlgraph InterfaceMatcher png

W efekcie otrzymujemy zapisany w kartotece na dysku diagram w formacie png.

Jak łatwo zauważyć jako argumenty podajemy tutaj na pierwszym miejscu nazwę pliku, a na drugim format w jakim UMLGraph ma wygenerować diagram np. gif, ps, png, svg.

Komendę zapisaną w skrypcie oczywiście możemy również wpisywać wprost z konsoli podając odpowiednio ścieżki jako argumenty. Sposób ten jednak wymaga wpisywania bardzo długiej linijki w konsoli, dlatego lepszym rozwiązaniem wydaje się być zapisanie jej do skryptu.


Przegląd możliwości UMLGraph - modelowanie diagramów klas.

Za pomocą diagramów UMLGraph możemy modelować:

  • klasy (używając tagu: @opt shape nazwa )
  • funkcje (wyspecyfikowane jako metody w klasie)
  • atrybuty (wyspecyfikowane jako pola w klasie)
  • stereotypy (używając tagu: @stereotype nazwa )
  • wartości oznaczone (używając tagu: @tagvalue nazwa wartość )
  • relację implementowania (używając deklaracji z języka Java: implements )
  • relację dziedziczenia (używając deklaracji z języka Java: extends albo tagu: @extends )
  • relację assocjacji (używając tagu: @assoc )
  • relację skierowanej assocjacji (używając tagu: @navassoc )
  • relację agregacji (używając tagu: @has )
  • relację kompozycji (używając tagu: @composed )
  • relację zależności (używając tagu: @depend )

W jaki sposób używać JavaDoc?

UMLGraph jest narzędziem dość rozbudowanym. Pozwala on między innymi na ingerowanie w to jak chcemy aby dany element był przedstawiony na diagramie za pomocą dodanych w komentarzach komend JavaDoc, przed definicją klasy, której mają dotyczyć. Możemy dodawać własne informacje aby wygenerowany diagram lepiej przedstawiał zachodzące między obiektami relacje, albo przedstawiał te relacje które chcemy aby się tam znalazły. Stosowanie składni języka Java pozwala na zawarcie pewnych informacji np. dziedziczenia czy implementowania. Dodając opcje możemy przedstawić bardziej szczegółowo zachodzące relacje.

Najważniejsze opcje:

Poza wymienionymi wyżej warto wspomnieć o następujących:

Nazwaopis
@opt … Służy do formatowania. Posiada liczne opcje z którymi można go stosować. Dokładny opis wszystkich możliwości znajduje się pod linkiem: Tag @opt.
@note … Powoduje dodanie dodatkowej etykiety,
@view @match Są to znaczniki używane do nadania pewnych opcji klasom których nazwy spełniają np. wyrażenie regularne. Widoki są dziedziczone od innych klas i posiadają ich cechy.
@hidden Dany obiekt nie będzie przedstawiony na diagramie, służy do definiowania parametrów w całym pliku

Prosty przykład:

/**
 * @opt shape class
 * @opt edgecolor "yellow"
 * @opt nodefontname "Times"
 * @opt nodefillcolor "#a0a0a0"
 * @opt nodefontsize 14
 * @hidden
 */
class UMLOptions{}

/**
 * @opt nodefontname "Helvetica-Bold"
 * @opt nodefontcolor "white"
 * @note opis ...
 * ... kontynuacja opisu ...
 * @note inny opis ....
 */
class klasa{}

Opis:

Przykład ten pomimo tego, że jest bardzo prosty pokazuje zastosowanie kilku ciekawych technik w UMLGraph. Po pierwsze widzimy że pierwsza klasa nie jest pokazana na diagramie. Dzieje się to za sprawą znacznika @hidden. Co więcej klasa{} posiada cechy wyszczególnione dla UMLOptions{}. Jest to sposób na ustawianie wartości domyślnych dla wszystkich obiektów w pliku, a także elementów takie jak np. tło diagramu. Cechy zostają odziedziczone przez kolejne obiekty. Ponadto widzimy w jaki sposób tworzone są notki dla naszej klasy i jak są reprezentowane.

Więcej przykładów znajduje się w dokumentacji. Można na ich podstawie lepiej zrozumieć zasadę działania UMLGraph.

Język LogTalk.

Początki języka LogTalk

Język ten został napisany przez Paulo Moura. Pierwsza wersja LogTalk 1.0 została wydana w 1995 roku, jednak autor nie był z niej całkowicie zadowolony. Spowodowało to że szybko bo już w 1998 roku, przyszła na świat wersja 2.0, która do tej pory jest wersją aktualną i nie jest ona kompatybilna z LogTolk 1.0. Od tego czasu wydanie to jest stale modyfikowane i dodawane są nowe funkcjonalności. Najnowsza wersja 2.35.1 została wydana dosłownie przed kilkoma dniami, bo 1 marca 2009 roku. Informacje dotyczące rozwoju tego projektu są systematycznie publikowane na stronie Zmiany. Ponadto jest prowadzony blog o tym języku, gdzie można również szukać informacji dotyczących tego projektu LogTalk - blog.

Cechy LogTalk

LogTalk jest próbą integracji programowania logicznego z programowaniem obiektowym. Pozwala na enkapsulowanie bazy wiedzy, napisanej w deklaratywny sposób, wewnątrz obiektu. Dzięki temu jest ona dostępna jedynie tam gdzie jest potrzebna, a kod programu staje się łatwiejszy w utrzymaniu i bardziej efektywny. Od strony programisty definiowanie obiektu jest podobne do tworzenia kilku przestrzeni nazw z osobnymi bazami wiedzy w każdym obiekcie.

O jakie cechy LogTalk wzbogaca Prolog?

  • Integracja programowania logicznego z programowaniem obiektowym.
  • Wielokrotne używanie napisanego kodu.
  • Wsparcie zarówno dla programowania opartego na klasach jak i prototypach.
  • Utrzymywanie hierarchii obiektów.
  • Separacja interfejsów od definicji klas.
  • Możliwość ukrywania składników klas. Występują cztery typy praw dostępu do komponentów obiektu: pakietowy, private, protected, i public.
  • Dziedziczenie.
  • Wsparcie dla aplikacji wielowątkowych.
  • Polimorfizm. Wsparcie dla wczesnego jak i późnego wiązania.

Jak działa LogTalk?

LogTalk jest kompatybilny praktycznie z każdym współczesnym kompilatorem języka Prolog, dzięki czemu aplikacje w nim napisane są łatwo przenośne. Interpreter LogTalk działa jak preprocesor. Pliki źródłowe są najpierw kompilowane do źródeł Prologa a te są następnie wykonywane jak zwykły program w Prologu. Różne jednostki są osobno kompilowane co pozwala na dynamiczne edytowanie obiektów podczas działania programu bez konieczności ponownej kompilacji. Jest to cecha Prologa przeniesiona do LogTalk. Po kompilacji zostają wygenerowane pliki Prologa z rozszerzeniami .pl o takich samych nazwach jak pliki wejściowe.


Jak tworzyć programy w języku LogTalk?

Język LogTalk jest przede wszystkim językiem obiektowym. Posiada te same cechy co inne języki obiektowe. Jednak semantyka języka różni się od tej, którą znamy z dotychczas poznanych języków programowania. Najważniejszymi składnikami tego języka są:

  • obiekty - prototypy, klasy i instancje.
  • protokoły - odpowiedniki interfejsów.
  • kategorie - nie posiadają odpowiedników. Są to kontenery zawierające pewne funkcjonalności.
  • predykaty - odpowiedniki funkcji.

LogTalk posiada trzy typy jednostek służących do enkapsulacji wiedzy. Jest to nadrzędny cel tworzenia tego typu elementów. Nie zawsze możemy chcieć aby pewna wiedza była zapisywana w postaci jednolitego kodu w jednym pliku. Czasami lepiej podzielić ją na mniejsze, łatwiejsze w zarządzaniu fragmenty, do których dostęp mają jedynie wybrane jednostki.

Wszystkie programy pisane w LogTalk mają domyślne rozszerzenie .lgt, ale można to zmienić. W jednym pliku może się znaleźć jeden lub więcej obiektów. Preferowane jest nazywanie plików nazwami obiektów, jeżeli są one jedynymi w pliku.


Wywoływanie predykatów.

Podobnie jak w innych językach zorientowanych obiektowo posiadających mechanizm późnego wiązania, to który predykat zostanie wywołany zależny od relacji między obiektami, dlatego potrzeba różnych operatorów do ich wywoływania.

LogTalk wykorzystuje trzy rodzaje operatorów:

:- op(600, xfx, ::).
:- op(600,  fx, ::).
:- op(600,  fx, ^^).
:- op(600,  fy,  :).

Wywoływanie odpowiednieo predykatu z obiektu jest wykonywane za pomocą operatora ::/2.

| ?- object::Message.

Możliwe jest także wywołanie:

| ?- object::(message1, message2, ...).
| ?- (object1, object2, ...)::message.

Odpowiada ono:

| ?- object::message1; object::message2; ... .
| ?- object1::message, object2::message, ... .

W tym wypadku wywoływane predykaty muszą być zadeklarowane jako publiczne.

Wywoływanie predykatu przez obiekt z samego siebie:

::message
::(message1, message2, ...)
::(message1; message2; ...)

Jeżeli w potomku nadpiszemy istniejący predykat to ten który został zasłonięty możemy wywołać operatorem ^^/1.

predicate :-
    ...,            % do something
    ^^predicate,    % call inherited definition
    ... .           % do something more

Możliwe jest wywoływanie predykatów za pomocą operatora :/1. Jednak należy być pewnym że nie został on nadpisany gdzieś w potomku.


Obiekty.

LogTalk uzywa terminu object w bardzo szerokim znaczeniu. Wszystkie instancje takie jak:

  • prototypy - prototype,
  • rodzice - parent,
  • klasy - class,
  • podklasy - subclass,
  • superklasy - superclass,
  • metaklasy - metaclass,
  • instancje - instance,

to wszystko są obiekty, ale posiadające inną charakterystykę i inny sens występowania w kodzie.

LogTalk pozwala na tworzenie obiektów, które nie są związane z innymi żadną relacją, jak i tworzyć hierarchie obiektów oparte na prototypach albo klasach. Można ponadto używać zarówno pojedynczego jak i wielokrotnego dziedziczenia.

Relacje między obiektami.

Obiekty tworzymy za pomocą dyrektyw: object/1-5 oraz end_object/0, pomiędzy którymi umieszczamy definicję obiektu. object/1-5 może przyjąć sześć argumentów, z których jedynie pierwszy jest obowiązkowy. Reszta określa typ tworzonego obiektu:

  1. objectName - nazwa obiektu, argument obowiązkowy.
  2. implements(protocolName) - protokół implementowany przez dany obiekt.
  3. imports(categoryName) - kategoria importowana przez dany obiekt.
  4. extends(parentName) - rodzic po którym następuje dziedziczenie.
  5. instantiates(className) - obiekt jest instancją klasy className.
  6. specializes(superclassName) - obiekt jest podklasą a superclassName superklasą.

Nazwami obiektów mogą być atomy albo złożone termy. Obiekty, kategorie i protokoły dzielą tę samą przestrzeń nazw, dlatego w programie nie mogą występować jednostki o takiej samej nazwie.

Spróbuję teraz opisać tworzenie obiektów z przykładowym kodem LogTolk:

:- object(objectName).
    ...
:- end_object.

Jest to definicja najprostszego obiektu który nie ma połączeń z innymi. Wszystkie tego typu obiekty są interpretowane jako prototypy i mogą być wykorzystywane przez inne prototypy wchodząc w jednostronną relację: extends. Obiekty posiadające taką relację w swojej definicji są także interpretowane jako prototypy. Żadne inne obiekty nie mogą używać tej relacji.

Sposób użycia:Opis:
:- object(objectName,implements(protocolName)).
    ...
:- end_object.
Definicja obiektu implementującego protokół.
:- object(objectName,imports(categoryName)).
    ...
:- end_object.
Definicja obiektu importującego kategorię.
:- object(prototypeName,extends(parentName)).
    ...
:- end_object.
Relacja dziedziczenia przez prototyp.
:- object(className,
    instantiates(metaclassName),
    specializes(superclassName)).
    ...
:- end_object.
className jest instancją metaklasy MetaclassName specjalizującą superklasę SuperclassName.

Podsumowując, obiekty mogą być albo samodzielnymi jednostkami, albo mogą być częścią hierarchii obiektów. Hierarchia może być albo oparta na prototypach (definiowanych przez rozszerzanie innych obiektów), albo klasach. Dodatkowo obiekty mogą implantować protokoły (interfejsy) albo importować kategorie.

Dyrektywa inicjalizacyjna.

:- initialization(goal).

Powyższa dyrektywa jest wykonywana zaraz po skompilowaniu i załadowaniu obiektu do pamięci. Argumentem może być jakikolwiek cel z Prologa.

Pozostałe dyrektywy.

Służą one do ustawiania pewnych właściwości obiektu.

  •  :- synchronized. 

    - Synchronizuje obiekty.

  •  :- dynamic. 

    - Obiekt może być deklarowany w trybie runtime.

  •  :- info(list). 

    - Służy do tworzenia dokumentacji.


Protokoły.

Protokoły służą do odseparowywania interfejsów od implementacji. Kilka obiektów może implementować ten sam protokół a obiekt może implementować kilka protokołów. Protokoły mogą zawierać tylko deklarację predykatów. W typowych językach obiektowych używa się termu interfejs, który w tym wypadku ma podobne znaczenie. W LogTalk można definiować niepubliczne składniki protokołów.

Definiowanie protokołu jest bardzo podobne z definiowaniem obiektów. Definicje protokołów są enkapsulowane pomiędzy dwiema dyrektywami: protocol/1-2 i end_protocol/0. Najprostszy protokół to taki który nie jest zależny od żadnego innego. Jeżeli jest on w relacji extends z innym protokołem to jego deklaracja wygląda następująco:

:- protocol(protocolName,
    extends(protocolName1, protocolName2, ...)).
    ...
:- end_protocol. 

W celu maksymalnego zwiększenia wielokrotnego wykorzystywania już napisanego kodu, zaleca się aby wszystkie predykaty w protokole odnosiły się do tej samej funkcjonalności. Rozszerzanie protokołów jest zalecane jedynie wtedy gdy gdy potrzebne są zarówno uproszczone jak i rozszerzone w dodatkowe predykaty, wersje tego samego protokołu.

Protokoły posiadają również niektóre takie same dyrektywy co obiekty:

Implementowanie protokołów.

Zarówno kategorie jak i obiekty mogą implementować protokoły. Składnia jest następująca:

:- object(objectName,
    implements(protocolName)).
    ...
:- end_object.
:- category(objectName,
    implements(protocolName)).
    ...
:- end_category.

W ten sposób następuje implementacja publiczna. Możliwe są trzy typy implementacji:

  •  implements(private::protocolName) 
  •  implements(protected::protocolName) 
  •  implements(public::protocolName) 

Znaczenie poszczególnych dyrektyw jest zgodne z intuicją.


Kategorie.

Kategorie dostarczają metod enkapsulacji powiązanych predykatów i definicji, które nie opisują obiektów i które mają jedynie sens w połączeniu z innymi predykatami. Mogą one być także używane do podzielenia złożonych obiektów w jednostki funkcjonalne. Kategorie mogą być importowane przez wiele różnych obiektów, w tym tych które są w różnych relacjach ze sobą.

Kategorie są tworzone dzięki dyrektywom category/1-3 oraz end_category/0. Najprostsza definicja to:

:- category(categoryName).
    ...
:- end_category.

Kategorie mogą implementować protokoły oraz mogą być złożeniem innych kategorii. Jednak należy wykorzystywać te techniki jedynie wtedy gdy nie zaburza to spójności funkcjonalności definiowanej kategorii. Zaleca się w takiej sytuacji importowanie wielu kategorii do jednego obiektu. Jeżeli pewne predykaty kategorii zostaną nadpisane to można ich użyć stosując dyrektywę alias/3.

Możemy stosować kategorie w celu rozszerzania funkcjonalności istniejących obiektów, bez potrzeby modyfikacji ich kodu:

:- category(categoryName,
    complements(objectName1, objectName2, ....)).
    ...
:- end_category.

Kategorie nie mogą dziedziczyć od obiektów.

Kategorie posiadają również niektóre takie same dyrektywy co obiekty:

Importowanie kategorii.

Wiele obiektów może importować wiele kategorii. Składnia jest następująca:

:- object(objectName,
    imports(categoryName1, categoryName2, ...)).
    ...
:- end_object.

W ten sposób przebiega import publiczny. Możliwe są trzy typy importu:

  •  imports(private::categoryName)
  •  imports(protected::categoryName)
  •  imports(public::categoryName)

Znaczenie poszczególnych dyrektyw jest zgodne z intuicją.


Predykaty.

Z perspektywy języka zorientowanego obiektowo predykaty często zwane metodami reprezentują stany oraz zachowanie obiektu. Zmienne stany obiektu mogą być reprezentowane predykatami dynamicznymi. Mogą one być enkapsulowane wewnątrz obiektów lub kategorii. Protokoły mogą zwierać jedynie dyrektywy predykatów.

Deklarowanie Predykatów.

Wszystkie Predykaty do których chcemy mieć dostęp z innych obiektów muszą być zadeklarowane. Deklaracja musi zawierać przynajmniej nazwę predykatu oraz liczbę argumentów. Muszą one poprzedzać definicję albo wywołanie w celu zapewnienia poprawnego przebiegu kompilacji.

Podobnie jak w innych obiektowych językach programowania dyrektywy mogą posiadać następujące deklaracje zakresów widoczności:

  • public - predykat można wołać z dowolnego obiektu.
  • protected - mogą być wołane jedynie z potomka lub w obiekcie w którym są zdefiniowane.
  • private - dostęp do predykatów ma jedynie obiekt w którym jest zdefiniowany.
  • local - jak predykaty prywatne mogą być jedynie wołane z obiektu, ale są niewidoczne dla mechanizmu obsługi błedów.

Przykład najprostszej deklaracji:

:- public(init/1).
 
:- protected(valid_init_option/1).
 
:- private(process_init_options/1).

Jeżeli nie zadeklarujemy inaczej, predykat bez takiej deklaracji jest uważany za lokalny.

Argumenty

Większość argumentów predykatów nie może być przypadkowych. Poprawne argumenty mogą być zdefiniowane za pomocą dyrektywy mode/2 w obiekcie. Przykładowa definicja:

:- mode(dowolny_predykat(?ArgumentType, +ArgumentType), zero_or_more).

Pierwszy argument opisuje poprawny sposób wywoływania predykatu. Przed każdym argumentem predykatu może się znaleźć następujący symbol:

  • + Argument musi być instancją.
  • - Argument nie może być instancją.
  • ? Argument może być albo nie instancją.
  • @ Argument nie może być modyfikowany.

Dalej znajduje się deklaracja typu. Możliwe są następujące typy: event, object, category, protocol, callable, term, nonvar, var, atomic, atom, number, integer, float, compound, i list. Najważniejsze jest to że możemy używać też własnych, które mogą być albo atomami albo złożonymi termami.

Drugi argument określa ile razy ma wystąpić prawda dla określonego typu. Możliwe są wartości:

  • zero - predykat zawsze nieprawdziwy.
  • one - predykat prawdziwy tylko raz.
  • zero_or_one - predykat albo prawdziwy albo nieprawdziwy.
  • zero_or_more - predykat ma zero lub więcej rozwiązań.
  • one_or_more - predykat ma jedno lub więcej rozwiązań.
  • error - predykat będzie wyrzucał błąd.

Jeżeli istnieje więcej niż jedno poprawne wywołanie predykatu zapisujemy je pod sobą np.:

:- mode(atom_concat(?atom, ?atom, +atom), one_or_more).
:- mode(atom_concat(+atom, +atom, -atom), zero_or_one).

Przydatne dyrektywy.

Jeżeli wywołujemy wiele razy predykaty z pewnego obiektu może to być uciążliwe. Dyrektywa uses/2 służy do stosowania skróconego zapisu.

:- uses(objectName, [Functor1/Arity1, Functor2/Arity2, ...]).

Możemy zastąpić:

foo :-
    findall(X, list::member(X, L), A),
    list::append(A, B, C),
    list::select(Y, C, R),

tym:

:- uses(list,[append/3, member/2, select/3]).
 
    foo :-
        findall(X, member(X, L), A),
        append(A, B, C),
        select(Y, C, R),

LogTalk pozwala także na definiowanie nowych, zastępczych nazw dla odziedziczonych lub zaimportowanych predykatów za pomocą dyrektywy alias/3 w następujący sposób.

:- alias(entity, predicate, alias).

gdzie:

  • entity - jednostka z której pochodzi predykat.
  • predicate - predykat dla którego tworzony jest alias.
  • alias - nazwa aliasu.

Użycie może wyglądać na przykład tak:

:- object(my_data_structure,extends(list, set)).
 
    :- alias(list, member/2, list_member/2).
    :- alias(set, member/2, set_member/2).
 
:- end_object.

O bardziej zaawansowanych możliwościach predykatów można poczytać w manualu: Predicate dicectives

Definiowanie Predykatów.

Obiekty.

Predykaty dla obiektów w LogTalk są definiowane tak samo jak w Prologu. Dodawane są jedynie dodatkowe elementy.

Przykład obiektu listy:

:- object(list).
 
    :- public(append/3).
    :- public(member/2).
 
    append([], L, L).
    append([H| T], L, [H| T2]) :-
        append(T, L, T2).
 
    member(H, [H| _]).
    member(H, [_| T]) :-
        member(H, T).
 
:- end_object.

Na przykładzie obsługi listy, którą już pisaliśmy w Prologu, można zauważyć że tak naprawdę LogTalk nie wprowadza tutaj rewolucji. Napisane w Prologu predykaty zamykamy pomiędzy specjalnymi dyrektywami obiektów i to wszystko.

Kategorie.

Kategorie mogą być importowane przez wiele obiektów. Z tego względu dynamiczne, prywatne predykaty muszą być wywoływane za pomocą ::/1. Zabezpiecza to nas przed używaniem nieodpowiedniego predykatu, jeżeli by został ponownie zdefiniowany przez obiekt importujący.

Przykład kategorii:

:- category(variable).
 
    :- public(get/2).
    :- public(set/2).
 
    :- private(value_/2).
    :- dynamic(value_/2).
 
    get(Var, Value) :-
        ::value_(Var, Value).
 
    set(Var, Value) :-
        ::predykat1(value_(Var, _)), 
        ::predykat2(value_(Var, Value).
 
:- end_category.

Wbudowane Predykaty języka LogTalk

Predykaty Pomocnicze

W LogTalk zdefiniowane są pomocnicze predykaty, które dostarczają dodatkowe informacje:

  • this/1 - Nazwa aktualnego obiektu.
  • self/1 - Obiekt z którego wołano predykat.
  • sender/1 - Obiekt wywołujący predykat.

LogTalk dostarcza wiele różnych predykatów, które pozwalają określanie relacji pomiędzy obiektami.

Obiekty:

  • |?- instantiates_class(InstanceName, ClassName). - Sprawdza czy zachodzi relacja instantiates.
  • | ?- specializes_class(ClassName, SuperclassName). - Sprawdza czy zachodzi relacja specializes.
  • | ?- extends_object(ObjectName, ParentName). - Sprawdza czy zachodzi relacja extends.
  • | ?- imports_category(ObjectName, CategoryName). - Sprawdza czy zachodzi relacja imports.
  • | ?- implements_protocol(ObjectName, ProtocolName). - Sprawdza czy zachodzi relacja implements.
  • | ?- object_property(ObjectName, PropertyName). - Sprawdza czy ObjectName posiada właściwość PropertyName.

Protokoły:

  • | ?- extends_protocol(ProtocolName1, ProtocolName2). - Sprawdza czy zachodzi relacja extends.
  • | ?- implements_protocol(ObjectOrCategoryName, ProtocolName). - Sprawdza czy zachodzi relacja implements.
  • | ?- protocol_property(ProtocolName, PropertyName). - Sprawdza czy ProtocolName posiada właściwość PropertyName.

Kategorie:

  • | ?- extends_category(CategoryName1, CategoryName2). - Sprawdza czy zachodzi relacja extends.
  • | ?- imports_category(ObjectName, CategoryName). - Sprawdza czy zachodzi relacja imports.
  • | ?- implements_category(ObjectName, CategoryName). - Sprawdza czy zachodzi relacja implements.
  • | ?- category_property(CategoryName, PropertyName). - Sprawdza czy CategoryName posiada właściwość PropertyName.

Instalacja i uruchamianie programów LogTalk.

Proces instalacji jest bardzo łatwy. Odpowiednie pakiety w zależności od tego na jakim systemie operacyjnym pracujemy pobieramy ze strony: Download i instalujemy. Na ubuntu wystarczy odpalić plik instalacyjny i LogTalk zainstaluje się sam do kartoteki: /usr/share/logtalk/.

Uruchomienie LogTalk to po prostu uzgodnienie dwóch plików. Pierwszy z nich z kartoteki /usr/share/logtalk/configs/ zawiera ustawienia dla interpretera którego używamy. Drugi plik uruchamia interpreter LogTalk. Ja napisałem sobie prosty plik w Prologu, dzięki czemu nie muszę wpisywać za każdym razem ścieżek do tych plików no i nie muszę ich pamiętać:

:-consult('/usr/share/logtalk/configs/swi').
:-consult('/usr/share/logtalk/compiler/logtalk').

Po uzgodnieniu powyższych plików LogTalk jest gotowy. Jak już pisałem pliki z programami maja mieć rozszerzenia .lgt. Kompilowanie plików LogTalk możemy przeprowadzić za pomocą dwóch predykatów.

| ?- logtalk_compile([source_file1, source_file2, ...]).

lub

| ?- logtalk_load([source_file1, source_file2, ...]).

Obydwa kompilują pliki .lgt i zapisują odpowiednio wygenerowane pliki .pl z takimi samymi nazwami w tej samej kartotece. Różnica polega na tym że logtalk_load/1 dodatkowo wczytuje do pamięci skompilowane pliki i można ich używać w trybie runtime.

Porównanie składni LogTalk i Java.

UMLGraph jest narzędziem, które interpretuje kod języka Java i na jego podstawie generuje diagramy UML. Definicje obiektów muszą mieć formę znaną z Javy aby proces generowania diagramu zakończył się sukcesem. Bardzo ważne są tutaj także komentarze JavaDoc zapisywane przed deklaracjami klas, które zawierają definicje dodatkowych opcji.

ZnaczenieLogTalkJava
Komentarz UMLGraph
/**
 * Komentarz LogTalk
 *
 * @opt operations
 * @opt attributes
 * @opt types
 * @opt visibility
 * @hidden
 */
/**
 * Komentarz Java
 *
 * @opt operations
 * @opt attributes
 * @opt types
 * @opt visibility
 * @hidden
 */
Deklaracja obiektu
:- object(className).
    ...
:- end_object.
class ClassName {
    ...
}
Deklaracja interfejsu/protokołu
:- protocol(protocolName).
    ...
:- end_protocol.
interface InterfaceName {
    ...
}
Deklaracja kategorii
:- category(categoryName).
    ...
:- end_category.
brak
Dziedziczenie obiektu
:- object(className, extends(parentName)).
    ...
:- end_object.
class ClassName extends ParentName {
    ...
}
Implementowanie protokołu
:- object(className, implements(p1,p2,...)).
    ...
:- end_object.
class ClassName implements I1, I2,... {
    ...
}
Importowanie kategorii
:- object(className, imports(c1,c2,...)).
    ...
:- end_object.
brak
Dziedziczenie interfejsów przez inne interfejsy
:- protocol(protocolName, extends(p1, p2, ...)).
    ...
:- end_protocol.
interface InterfaceName extends I1, I2, ... {
    ...
}
Dziedziczenie kategorii przez inne kategorie
:- category(categoryName, extends(p1, p2, ...)).
    ...
:- end_category.
brak
Deklaracja zasięgu predykatu/metody
:- public(set/2).
:- protected(set/2).
:- private(set/2).
public void set(...,...){}
protected void set(...,...){}
private void set(...,...){}
Deklaracja predykatu/metody
predicate(_,_) :-
void method(...,...){}
Deklaracja pola/memberabrak
private void int a = 0
Zawracanie wartości brak
 return value; 
Informacje o typie brak
int, float, double, ... 

Zaproponowane rozwiązanie.

Moje rozwiązanie powyższego problemu sprowadza się do napisania obiektów w LogTalk, które przedstawiają relacje między obrazowanymi obiektami, za pomocą składni JAVA. Niestety Język LogTalk ma inną semantykę od języka Java i niektórych zależności nie da się przedstawić w taki sam sposób.

Twórcy LogTalk zalecają aby tworzone obiekty zapisywać w plikach o takich samych nazwach jak nazwy obiektów w nich zdefiniowanych. Moje rozwiązanie obejmuje:

obiekt javaobject - zawiera następujące publiczne predykaty:
  • object_drow/1 - zapisuje obiekty, za pomocą skłądni Java.
  • category_drow/1 - zapisuje kategorie, za pomocą skłądni Java.
  • predicate_drow/1 - zapisuje predykaty, za pomocą skłądni Java.
obiekt javacomment - zawiera następujące publiczne predykaty:
  • setComment/1 - przyjmuje listę stringów. Stosuje się go do umieszczenia dodatkowych definicji opisującymi tworzone diagramy.
  • setAsUMLMainComment/0 - ustawia zasięg komentarza dla całego diagramu.
obiekt mainobject - zawiera następujące publiczne predykaty:
  • mainFunction/0 - ten predykat należny zdefiniować samemu. Zawiera on definicję diagramu. Opisujemy tutaj elementy, które chcemy rysować na diagramie za pomocą javaobject, dodając komentarze opisujące diagram za pomocą javacomment.
plik drow.pl:
  • drow/0 - predykat ładuje wybrany plik LogTalk i zapisuje go do pliku.
skrypt umlgraph gdzie:
  • ${1} oznacza nazwę kompilowanego pliku prologa.
  * ${2} oznacza nazwę wywoływanego predykatu.
  • ${3} oznacza format w jakim zostanie zapisany diagram. 


  obiekty w pliku drowobject.lgt.

  * plik służy do zademonstrowania przykładu działania prezentowanego rozwiązania.


== javaobject ==

<code prolog>
:- object(javaobject).

	:- public(object_drow/1).
	:- public(protocol_drow/1).
	:- public(category_drow/1).
			
	protocol_drow(ProtocolName):- 
		protocol_drow_head(ProtocolName), protocol_body(ProtocolName), foot.
	protocol_drow(_):- foot.
	protocol_drow_head(ProtocolName):- 
		protocol_head_name(ProtocolName), protocol_head_relations(ProtocolName), head_foot.
	protocol_drow_head(_):- head_foot.

	category_drow(CategoryName):- 
		category_drow_head(CategoryName),category_body(CategoryName),foot.
	category_drow(_):- foot.
	category_drow_head(CategoryName):- 
		category_head_name(CategoryName), category_head_relations(CategoryName), head_foot.
	category_drow_head(_):- head_foot.

	object_drow(ObjectName):- 
		object_drow_head(ObjectName), object_body(ObjectName), foot.
	object_drow(_):- foot.
	object_drow_head(ObjectName):- 
		object_head_name(ObjectName), object_head_relations(ObjectName), head_foot.
	object_drow_head(_):- head_foot.


	protocol_head_name(ProtocolName) :-
        	write('interface '),
		write(ProtocolName).

	protocol_head_relations(ProtocolName) :-
		extends_protocol(ProtocolName, ParentName),
		write(' extends '),
		getExtendsProtocolList(ProtocolName,ProtocolList),
		drow_list(ProtocolList),!.

	protocol_body(ProtocolName) :- 
		create_object(foo, [implements(ProtocolName)],[],[]),
		getPredicateList(foo,PredicateList),
		abolish_object(foo),
		drow_predicate_list(PredicateList),!.


	category_head_name(CategoryName) :-
        	write('interface '),
		write(CategoryName).

	category_head_relations(CategoryName) :-
		extends_category(CategoryName,_),
		write(' extends '),
		getExtendsCategoryList(CategoryName,CategoryExtendsList),
		drow_list(CategoryExtendsList),
		implements_protocol(CategoryName,_),write(', '),
		getImplementsList(CategoryName,CategoryImplementsList),
		drow_list(CategoryImplementsList),!.

	category_head_relations(CategoryName) :-
		implements_protocol(CategoryName,_),
		write(' extends '),
		getImplementsList(CategoryName,CategoryList),
		drow_list(CategoryList),!.

	category_body(CategoryName) :- 
		create_object(foo, [imports(CategoryName)],[],[]),
		getPredicateList(foo,PredicateList),
		abolish_object(foo),
		drow_predicate_list(PredicateList),!.


	object_head_name(ObjectName) :-

        	write('class '),
		write(ObjectName).

	object_head_relations(ObjectName) :-
		extends_object(ObjectName, ParentName),
		write(' extends '),
		write(ParentName),
		imports_category(ObjectName,_),
		write(' implements '),
		getImportsCategoryList(ObjectName,CategoryList),
		drow_list(CategoryList),
		implements_protocol(ObjectName,_),
		write(', '),
		getImplementsList(ObjectName,ProtocolList),
		drow_list(ProtocolList),!.

	object_head_relations(ObjectName) :-
		imports_category(ObjectName,_),
		write(' implements '),
		getImportsCategoryList(ObjectName,CategoryList),
		drow_list(CategoryList),
		implements_protocol(ObjectName,_),
		write(', '),
		getImplementsList(ObjectName,ProtocolList),
		drow_list(ProtocolList),!.

	object_head_relations(ObjectName) :-
		implements_protocol(ObjectName, ProtocolName),
		write(' implements '),
		getImplementsList(ObjectName,L),
		drow_list(L),!.

	object_body(ObjectName) :- 
		getPredicateList(ObjectName,PredicateList),

		drow_predicate_list(PredicateList).


	head_foot :-
		write(' {'), nl.

	foot:-

        	write('}'),nl.

	getImplementsList(ObjectName,ImplementsList):- 
		bagof(ProtocolName,implements_protocol(ObjectName, ProtocolName),ImplementsList).

	getExtendsProtocolList(ProtocolName,ProtocolList):- 
		bagof(Protocol,extends_protocol(ProtocolName, Protocol),ProtocolList).

	getExtendsCategoryList(CategoryName,CategoryList):-
		bagof(Category,extends_category(CategoryName,Category),CategoryList).

	getImportsCategoryList(CategoryName,CategoryList):-
		bagof(Category,imports_category(CategoryName,Category),CategoryList).

	getPredicateList(ObjectName,ArgumentsList):-
		bagof(Arguments,getArgumentList(ObjectName,Arguments),ArgumentsList).

	getArgumentList(ObjectName,Arguments):-ObjectName::current_predicate(Functor/Arity),
		functor(Predicate, Functor, Arity),Predicate =.. Arguments.


	drow_list([]).
	drow_list([X]):-write(X).	
	drow_list([X|Y]):-write(X),write(', '),drow_list(Y).

	drow_predicate_list([]).	
	drow_predicate_list([X|Y]):-write('public void '),drow_arg_list(X),drow_predicate_list(Y).

	drow_arg_list([X|Y]):-write(X),write('('),drow_arg_list_item(Y).

	drow_arg_list_item([]):-write(') {}'),nl.	
	drow_arg_list_item([Y]):-write('Arg var'),write(') {}'),nl.
	drow_arg_list_item([X|Y]):-write('Arg var, '),drow_arg_list_item(Y).


:- end_object.
</code>

{{:pl:miw:2009:miw09_logtalk_umlgraph:javaobject.rar|}}

== javacomment ==

<code prolog>
:-object(javacomment).

	:-public(setComment/1).
	:-public(setAsUMLMainComment/0).

	setComment(Y):- write('/**'),nl,drowCommentList(Y),write(' */'),nl.
	setAsUMLMainComment:-write('class UMLOptions {}'),nl.

	drowCommentList([]).	
	drowCommentList([X|Y]):-write(' * '),write(X),nl,drowCommentList(Y).

:- end_object.
</code>

{{:pl:miw:2009:miw09_logtalk_umlgraph:javacomment.rar|}}

== mainobject==

<code prolog>
:-object(mainobject).

	:- initialization(createArgObject).
	:- initialization(mainFunction).

	mainFunction:- 
		javacomment::setComment(['@opt operations', '@opt attributes', '@opt types', '@opt visibility','@hidden']), 
		javacomment::setAsUMLMainComment,

		javacomment::setComment(['@stereotype Protocol 1']),
		javaobject::protocol_drow(protocolName1),
		javacomment::setComment(['@stereotype Protocol 2']),
		javaobject::protocol_drow(protocolName2),
		javacomment::setComment(['@stereotype Protocol 3']),
		javaobject::protocol_drow(protocolName3),

		javacomment::setComment(['@stereotype Category 1']),
		javaobject::category_drow(categoryName1),
		javacomment::setComment(['@stereotype Category 2']),
		javaobject::category_drow(categoryName2),
		javacomment::setComment(['@stereotype Category 3']),
		javaobject::category_drow(categoryName3),

		javacomment::setComment(['@stereotype Object Parent']),
		javaobject::object_drow(parent),

		javacomment::setComment(['@stereotype Object Children']),
		javaobject::object_drow(objectChildren).

	createArgObject:-javacomment::setComment([@hidden]),write('class Arg{}'),nl.

:- end_object.
</code>

{{:pl:miw:2009:miw09_logtalk_umlgraph:mainobject.rar|}}


== drow.pl ==

<code prolog>
:-consult('/usr/share/logtalk/configs/swi').
:-consult('/usr/share/logtalk/compiler/logtalk').
:-logtalk_load([javaobject,javacomment,drowobject]).



drow :- 
  tell(pipe('grep ^[^%] > drow.java')),
  logtalk_load([mainobject]),
  told.
</code>

{{:pl:miw:2009:miw09_logtalk_umlgraph:drow.pl|}}


== umlgraph ==

<code prolog>
#!/bin/sh

	UMLGRAPH_HOME=/home/mariusz/Pulpit/umlgraph/lib
	JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.07

	swipl -s ${1}.pl -g ${2} -t halt

	BASE=${1}

FILETYPE=$3
	shift 2
	java -classpath "$UMLGRAPH_HOME/UmlGraph.jar:$JAVA_HOME/lib/tools.jar" \
	org.umlgraph.doclet.UmlGraph -package $* -output - $BASE.java |
	dot -T$FILETYPE -o$BASE.$FILETYPE

</code>

umlgraph.rar

drowobject
:- protocol(protocolName1).
	:-public(predykat00/0).
:- end_protocol.
 
:- protocol(protocolName2).
	:-public(predykat01/2).
:- end_protocol.
 
:- protocol(protocolName3, extends(protocolName1, protocolName2)).
	:-public(predykat02/2).
:- end_protocol.
 
 
:-category(categoryName1).
	:-public(predykat10/0).
 
	predykat10:- write('empty').
:-end_category.
 
 
:-category(categoryName2).
	:-public(predykat11/1).
 
	predykat11(X):- write(X).
:-end_category.
 
 
:-category(categoryName3, extends(categoryName1, categoryName2), implements(protocolName1)).
	predykat00:- write('empty').
:-end_category.
 
 
:- object(parent).
:- end_object.
 
 
:- object(objectChildren, extends(parent), imports(categoryName1, categoryName3), implements(protocolName1,protocolName3)).
	:-public(predykat01/2).
	:-public(predykat02/2).
 
	predykat01(X,Y):- write(X), write(Y).
	predykat02(X,Y):- write(X), write(Y).
:- end_object.
Za pomoca komendy dostajemy plik .java i diagram drow.png
./umlgraph drow drow png
Plik drow.java
/**
 * @hidden
 */
class Arg{}
/**
 * @opt operations
 * @opt attributes
 * @opt types
 * @opt visibility
 * @hidden
 */
class UMLOptions {}
/**
 * @stereotype Protocol 1
 */
interface protocolName1 {
public void predykat00() {}
}
/**
 * @stereotype Protocol 2
 */
interface protocolName2 {
public void predykat01(Arg var, Arg var) {}
}
/**
 * @stereotype Protocol 3
 */
interface protocolName3 extends protocolName1, protocolName2 {
public void predykat00() {}
public void predykat01(Arg var, Arg var) {}
public void predykat02(Arg var, Arg var) {}
}
/**
 * @stereotype Category 1
 */
interface categoryName1 {
public void predykat10() {}
}
/**
 * @stereotype Category 2
 */
interface categoryName2 {
public void predykat11(Arg var) {}
}
/**
 * @stereotype Category 3
 */
interface categoryName3 extends categoryName1, categoryName2, protocolName1 {
public void predykat00() {}
public void predykat10() {}
public void predykat11(Arg var) {}
}
/**
 * @stereotype Object Parent
 */
class parent {
}
/**
 * @stereotype Object Children
 */
class objectChildren extends parent implements categoryName1, categoryName3, protocolName1, protocolName3 {
public void predykat00() {}
public void predykat01(Arg var, Arg var) {}
public void predykat02(Arg var, Arg var) {}
public void predykat10() {}
public void predykat11(Arg var) {}
}

drow.java.rar

Diagram drow.png

Ograniczenia i problemy.

Najważniejsze problemy z którymi się spotkałem obejmują poniższe punkty.

  • Ograniczenia UmlGraph

Największym problemem jest tu fakt że wykorzystuje on Java JRE do sprawdzenia czy składnia pliku z którego jest generowany diagram jest całkowicie składnią JAVA. Nie akceptuje on żadnych, nawet najmniejszych niedociągnięć w kodzie. Błędy kończą się przerwaniem wykonywania operacji.

  • Problem z semantyką LogTalk i JAVA

Język Java nie posiada w składni kategorii ani żadnych elementów, które mogły by je zastąpić na diagramie. Jest to problem na który tak naprawdę ciężko znaleźć dobre rozwiązanie. Jeżeli zapiszemy Kategorie jako klasy, zabraniamy pokazania dziedziczenia wielu kategorii przez inna kategorię. Zapis kategorii jako interfejsu powoduje że na diagramie znajduje się napis «interface» co nie jest zgodne z prawdą, bo kategorie mają zdefiniowane predykaty w swoim ciele. Ostatecznie zdecydowałem się na rozwiązanie z interfejsem, ponieważ nie znalazłem lepszego.

  • Brak typów w LogTalk

Znów jest to problem wynikający z charakterystyki języka Java i LogTalk. Java musi mieć zdefiniowany typ argumentu. LogTalk nie. Dlatgo aby dało się zaznaczyć predykat na diagramie musiałem zdefiniować obiekt Arg - który jest pokazany na diagramach jako argument, przekazywany do predykatu.

  • Brak możliwości dostępu do predykatów prywatnych

LogTalk nie udostępnia predykatów pozwalających na uzyskiwanie informacji o składowych prywatnych i protected. Możemy jedynie zdobyc informację o predykatach public. Problem ten powoduje ze nie da się przedstawić składowych prywatnych na diagramach.

Spotkania

Materiały

Internet

UMLgraph
LogTalk

Kopie Lokalne

pl/miw/2009/miw09_logtalk_umlgraph/projekt.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0