Object Oriented Programming for All

Hi,
willkommen zu einem kleinen neuen Projekt von mir. Objektorientierte Programmierung für alle. Das meint, daß jede NICHT objektorientierte Programmiersprache, die fähig ist Amiga Shared Libraries zu benutzen, zur Programmierung von Objekten benutzt werden kann.

Jeder Programmierer hat schon mal was von JAVA gehört und im gleichen Atemzug, daß es auf dem Amiga zwar funktioniert, aber leider so langsam ist, das es nicht praktisch einsetzbar ist. Wer nun OO programmieren will, muß auf einen der diversen C++ Compiler umsteigen.

Da mir im großen und gesamten JAVA und OOP gefällt und ich dazu auch noch meine eigene Programmiersprache verwenden wollte, hab ich mir Gedanken gemacht , wie man OOP auf dem Amiga resourcenschonend und zugleich offen für alle Programmiersprachen gestalten kann.
Das Ergebnis sieht man hier:

Downloadpackage here

Installation hier lesen

Eigene Sprache benutzen

English Page

Inhalt

Abstrakte Methoden und Klassen

Basics über OOP4A

BOOPSI und OOP4A
lest es!

Static Kontext - AutoStrings

Typen und Überladung von Methoden

GarbageCollector

Packages

Gui Classes W.I.P.

Partner Projekte

Die "oop.library" ist das Herz des gesamten Systems. Sie bietet 3 Funktionen an:

Mit new() werden neue Objekte erzeugt, mit del() werden diese zerstört, und domethode() ruft Methoden dieser Objekte auf.

Objekte sind in Klassen definiert, diese finden sich in Klassenfiles wieder , Endung ".class". Diese Klassenfiles werden mit einem Präprozessor übersetzt und dann kompiliert und assembliert. Das Zielformat ist eine echte Amiga Shared Library.

Klassen werden nur einmal erzeugt und können beliebig oft instanziiert werden.
Eine Klasse definiert sich so:

class Klassenname 
 {

    Variablen und Objekte

    Methoden()
    {}

 }

Eine Klasse kann eine andere Klasse erweitern und dadurch die Methoden und Variablen der Superklasse erben.

class b extends a
{
    ...
}

Klassenerweiterungen können sich beliebig oft Verschachteln,d.h. Klasse Z erweitert Y, Y erweitert X, X erweitert W usw. usw.

Der Name der zu erweiterenden Klasse kann ein vollqualifizierter Name sein, oder der Name der Klasse ohne .class. Ein Packagename kann auch benutzt werden.

Variablen sind durch den Compiler auf LONG und Zeiger auf LONG festgelegt. Als Compiler wird PreAss verwendet. Als Assembler Asm-One. Der Präprozessor Preassxx kennt auch Objekte als Variablen. Dies ist wichtig damit Konstrukte der Form X=objektname.methodenname(arg1,arg2...).methodeY()... möglich sind. Variablen können privat ( default ) oder public sein. Das macht aber nur Sinn in den Objektvariablen, in den lokalen Variablen der Methoden ist public zwar angebbar , aber nicht wirksam, da es sich um Stackvariablen handelt.

Methoden können vier definierte Zustände haben:

  1. private
  2. protected
  3. public

  4. abstract

Nur public-Methoden können von aussen angesprochen werden. Abstrakte Methoden sind Methoden ohne Methodenkörper, also Programmcode, und können so nicht kompiliert werden.


class Test
{
    long A B C

    public setA(long value)
     {
        a==value
     }

    public setB(long value)
     {
        B==value
     }

    private setC(long value)
     {
        C==value*-1
     }

    public calc()
     {
        long returnvalue

        returnvalue==A*B

        setC(returnvalue)

        return returnvalue

     }
}

* Die hier verwendete Test.class ist im ODK nicht enthalten! *


Ein Programm welches die Klasse benutzen will
Start: test=new("test",0) domethode(test,"setA",>settag1:10) domethode(test,"setB",>settag2:20) ret=domethode(test,"calc",0) printf("10 * 20 = %ld\n",*ret) test=del(test) {* return *}
Will eine Klasse ein Objekt einer anderen Klasse benutzen, muß es einfach ein Objekt dieser Klasse mit new() instanziieren:

class a 
{
    long testobject

    public methode()
    {
        testobject=new("test",0)
        ...
        del(testobject)
    }
}

oder

class a 
{
    long testobject

    public methode()
    {
        testobject new Test
        ...
        del(testobject)
    }
}

Bei der letzten Schreibweise kann man auch gleichzeitig mehrere identische Objekte erzeugen, indem man einfach mehrere Variablennamen angibt.

class a 
{
    public methode()
    {
        test1 test2 test3 new Test
        ...
        del(test1)
        del(test2)
        del(test3)
    }
}

Möglich ist auch ein direktes Initialisieren der Objekte über Ihren Konstruktor.

class a 
{
    public methode()
    {
        test1 new Test("Hallo",1492)
        ...
        del(test1)
    }
}

Damit man nicht mehr auf normale Programme zurückgegriffen werden muß, gibt es eine Methode Main() , welche als Programmstart angesehen werden kann. Damit Klassen mit der Methode Main() auch benutzt werden können gibt es OOPA. OOPA nimmt als Kommandozeilenargument den Namen der zu startenden Klasse und ggf. eine Argumentzeile an, die an die Methode Main() weitergreicht wird.

class a
{
    ...

    public Main()
    {
        Object testobject

        testobject=new("test",0)
        
        Result=testobject.Test()
        if Result#0
         {
           printf("Result != null\n")
         }
        testobject=del(testobject)
    }      
}

WARNUNG: Es gibt keinen GarbageCollector der nach dem Zerstören eines Objektes alte Referenzen findet und freigibt. Das hat jedes Objekt selbst zu leisten. Es ist sicher ein Objekt mehrfach zu zerstören, wenn man sich an die Regel Object=del(Object) hält. Einen kleinen GarbageCollector gibt es allerdings doch, flushlibs , liegt im Aminet bzw. ist im odk enthalten , und das ODK eigene flush können zusammen als Collector eingesetzt werden. Dabei werden nicht mehr benötigte Objekt-Libraries aus dem Speicher entfernt. Dazu muß der OpenCounter der Libraries Null anzeigen. Wenn man Klassen verändert, müssen sie neu kompiliert werden. Dies ist normal, wenn man im OOP4A diese neue Version der Klasse auch wirklich benutzen will, dann muß man die alte Klasse mit flushlibs aus dem Speicher entfernt haben, sonst wird die passende Library nicht neu von der Festplatte gelesen und natürlich auch nicht ausgeführt. Der AmigaOS Libraryloader, der zum Laden der Libraries zwangsweise eingesetzt wird, ist leider nicht der Meinung, daß er mal nachsehen sollte, ob sich die Libs geändert haben, wenn die Lib lange nicht benutzt wurde. Das ist auf die alten Amigas ohne Festplatte ausgerichtet, um so Zeit zusparen. Erst eine Anforderung nach freiem RAM-Speicher für das System entfernt unbenutzte Libraries aus dem Speicher, z.b. `Avail flush` oder `flushlibs`.

Um zubenutzende Objekte anzulegen bzw. zu zerstören gibt es einen Konstruktur und einen Dekonstruktor. Im Dekonstruktor kann auch die empfohlende "object=del(object)" Klausel verzichtet werden, da es nach dem Beenden der Methode ja keiner mehr auf das Objekt zugreifen kann.

class a 
{
    long testobject

    public methode()
    {
        testobject=new("test.class",0)
        ...
        testobject=del(testobject)
    }

    public DeConstructor()
    {
      del(testobject)
    }

}

Im Konstruktor werden Routinen abgearbeitet die bei jedem Erstellen eines Objektes dieser Klasse durchlaufen werden sollen. Dies passiert exakt einmal im Leben eines Ojektes, es sei denn der Konstruktor wird expliziert aufgerufen. Der Dekonstruktor wird auch nur einmal aufgerufen, wenn das Objekt zerstört werden soll.

FunktionName
KonstruktorConstructor()
DeKonstruktorDeConstructor()

Abstrakte Klassen sind Klassen die nicht instanziierbar sind, weil konkrete Methoden fehlen.


abstract class A
{
    long Zahl

    abstract public Methode1(long a,long b)
             
    public Methode2(long a,long b)
    {
        ... konkreter code ...
    }
}

Diese Klasse A kann nicht instanziiert werden, da für die Methode1() kein Sourcecode vorliegt. Eine Klasse B muß nun diese Klasse erweitern und den nötigen Sourcecode konkret implementieren:
class b extends a
{
    public Methode1(long a,long b)
    {
      long returnvalue
    
      returnvalue==A+b

      return returnvalue
    }
}

Dies geschieht, damit Objekte als ganzes austauschbar werden. Das Beispiel wird dies verdeutlichen.
class a
{
    Object class String  Name
    Object class Integer Zahl

    Methode()
    {
    
      Name.Set("hallo")
    }
}

Daraus wird das folgende De/KonstruktorPaar zusammengebaut, selbst wenn ein De/Konstruktor vorgeben wird. Bitte beachten Sie das!

 Constructor()
    {
      name=new("String",0)
      Zahl=new("Integer",0)
      if name&zahl=0
       {
         del(name)
         del(zahl)
         {* UnFramereturn -1*}
       }
    }

 DeConstructor()
    {
      del(name)
      del(zahl)
    }

Abb. zeigt einen kleinen Einblick in das Framework

Abb. zeigt die Abhänigkeiten von Objekten und Klassen untereinander


Inhalt

Abstrakte Methoden und Klassen

Basics über OOP4A

BOOPSI und OOP4A
lest es!

Static Kontext - AutoStrings

Downloadpackage here

Installation hier lesen

Eigene Sprache benutzen

Gui Classes W.I.P.


Installation:

Saugt euch von der StartSeite die PREASS Archive und installiert dieses zuerst. Dann entpackt das oop4a Archiv . Darin sind die nötigen LVO/FD/AUTODOCS für die oop.library und der Präprozessor für die ClassFiles enthalten, dazu eine auf das jeweilige ODK angepaßte Version von Preass. Einige Klassenfiles sind im Archiv dabei, diese sind aber ohne richtige Funktion, da es noch keinen DeveloperKit für PreassXX gibt. Wir sind da erst am Anfang! Wer Lust hat darf gerne eigene Packages schreiben und zu mir senden, die werden gern genommen.
Packages bauen sich eigentlich wie bei JAVA auf. Das Ganze muß relativ zu Libs:classes geschrieben sein.Statt . kann auch / verwendet werden, das kommt auf gleiche raus.

Wer keine eigene Domain im Netz besitzt, der baut das so auf:

TopLevelDomain des Landes in dem man wohnt ( z.b. de ) + Name des Authors + Projektname + ..

also z.b. de.Cyborg.Hamsterkäfig

Wo der Source liegt, ist eigentlich egal. Der Sourcetree soll die gleiche Struktur haben, wie der Baum in Libs:classes, sonst kommen wir da ins Caos! Die fertig assemblierten Libraries kommen dann nach "Libs:Classes" , dazu muß "libs:classes" in den Libspfad eingebaut werden..


Einen eigenen Präprozessor schreiben:


Wie war das jetzt mit der eigenen Sprache und OOP ?
Naja, eigentlich ist das jetzt ganz einfach. Dem ODK liegt ein Beispiel Präprozessor für Preass dabei. Diesen muß man eigentlich nur an die verwendete Sprache anpassen. Der Präprozessor arbeitet in zwei Schritten. Im 1. Schritt werden alle Zeilen des Klassenfiles eingelesen und in Ihre Bestandteile zerlegt. Das wären z.b. Anweisungen wie CLASS , EXTENDS Public , RETURN usw. Jede Zeile die nichts enthält , was der Präprozessor wissen muß , wird mit der entsprechenden MethodenID ( 0 = Klassencode ( z.Z. nicht unterstützt ) in der Line-Liste ( LineRoot) gespeichert. Alle Objekte und Variablen werden ebenfalls in solchen verketten Listen gespeichert. Aus diesen Listen wird im 2. Schritt der Source für Preass gebaut. Kurz um, am Ende des Prozesses muß eine Struktur mit dem gleichen Verhalten wie aus dem Preass++ Präprozessor. Dies ist eine Librarystruktur und muß vom Compiler auch direkt in eine Library oder Assembercode übersetzt werden können. Wichtig dabei sind die Arrays für Funktionen und Strings, die für die OOP Lib gebraucht werden!

Ein wichtiger Hinweis: alle Klassen die mit Preass++ geschrieben und übersetzt werden sind threadsafe! Wenn das der von Ihnen geschriebene Präprozessor nicht sicherstellen sollte, dann kann er nicht eingesetzt werden, da Thread-Sicherheit eine Grundvorraussetzung ist.

Das ist nicht zu verwechseln mit syncronisiertem Zugriff auf eine Methode!

Viel Spaß.

OOP4A History


Partner Projekte

PowerD from Martin Kuchinka