Tutorial – OOP in AutoIt

  • Teil #1
    (Alle Beispiele sind für die AutoIt Version 3.3.12.0 geschrieben und laufen eventuell auf älteren Versionen nicht!)
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
    Teil #2
    Teil #3
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +




    Was ist OOP?
    Normalerweise kennen wir als AutoIt Programmierer ausschließlich die prozedurale Programmierung mit normalen Variablen und Funktionen. Ein typisches AutoIt Skript ist eigentlich immer Prozedural aufgebaut. Jedoch existiert auch ein objektorientiertes Konzept welche in vielen anderen Programmiersprachen bereits nativ nutzbar ist. Das Konzept verfolgt eigentlich eine einfache Idee. Stellen wir uns einmal vor, dass wir einen Menschen programmieren wollten. Wir müssen diesen Menschen durch sein Aussehen und seinem Charakter (oder Verhalten) definieren. Dabei behalten wir im Hinterkopf, dass jeder Mensch individuell ist und deshalb nicht alle Eigenschaften oder Eigenarten auf jeden Menschen zutreffen. Diese sind also auf den individuellen Menschen bezogen. Bei der OOP ist dies genauso, wir haben ein Objekt welches wir durch Attribute (Eigenschaften) und Methoden (Verhalten) definieren können. Alle Attribute und Methoden beziehen sich dann auf dieses eine Objekt. Diese Grundidee verfolgt OOP. Wie wir dieses Konzept für die AutoIt Programmierung einsetzen können, möchte ich im folgendem erklären. Wer noch vorab weitere Informationen benötigt kann sich in der Wikipedia schlau machen: http://de.wikipedia.org/wiki/O…rientierte_Programmierung




    OOP und AutoIt:
    AutoIt unterstützt nativ kein OOP, daher wurde 2010 die AutoItObject UDF ins Leben gerufen. Mithilfe dieser UDF wurde OOP in AutoIt nutzbar. Damit sind neue Möglichkeiten für uns AutoIt Programmierer eingeräumt. Es wird (und kann) ausschließlich nur mit dieser UDF im gesamten Tutorial gearbeitet, daher ist es empfehlenswert die AutoItObject UDF hier zu downloaden: http://www.autoitscript.com/fo…/110379-autoitobject-udf/




    AutoItObject UDF
    Die UDF liefert eine Hilfedatei sowie Beispiele und zwei DLLs mit. Im Grunde reicht nur die AutoItObject.au3 Datei aus um OO zu programmieren. Vorab muss stetig die UDF initialisiert werden, dass kann mit der Funktion _AutoItObject_Startup() erreicht werden. Zwar ist es nicht nötig die UDF wieder zu schließen, jedoch gehört dies zum guten Programmierstil und deswegen sollte die Funktion _AutoItObject_Shutdown() vor dem Programmende aufgerufen werden.


    Für alle die für die UDF Syntax Highlights und Calltips für die UDF wünschen, ich habe ein kleines Präsent für euch:


    Hinweis: Die Referenz sollte im Verlauf dieses Tutorials parallel genutzt werden.




    Objekte erstellen:
    Uns stehen zwei Möglichkeiten zur Verfügung neue Objekte zu erstellen. Die erste Funktion die das bewerkstelligen kann nennt sich _AutoItObject_Create(), die zweite _AutoItObject_Class(). Im Grunde liefern beide Funktionen das gleiche Ergebnis, aber sind von der Verwendung her unterschiedlich. Dazu später mehr, vorab werden wir nur mit der _AutoItObject_Create() Funktion arbeiten.


    Um also ein neues Objekt zu erstellen ist nur die Funktion _AutoItObject_Create() aufzurufen.


    Jedoch enthält das Objekt weder Attribute oder Methoden. Das können wir jedoch mit den Funktionen _AutoItObject_AddProperty() und _AutoItObject_AddMethod() sehr schnell ändern.



    Attribute
    Attribute sind die Eigenschaften eines Objektes. Diese sind im Grunde nichts anderes als Variablen die mit jedem X beliebigen Wert gefüllt werden können. Um einem Objekt neue Attribute zuzuordnen, kann die Funktion _AutoItObject_AddProperty() verwendet werden. Mithilfe des vierten Parameters kann dem Attribut direkt ein Wert zugewiesen werden:


    Selbst Arrays, weitere Objekte oder gar Funktionen können in den Attributen hinterlegt werden:



    Methoden
    Methoden sind nichts anderes als Funktionen. Deswegen werden die Methoden auch nur als normale Funktionen definiert. Das wichtige ist jedoch, dass bei jeder Methoden Funktion über den ersten Parameter eine Referenz des Objektes übergeben wird, von dem es aufgerufen wurde. Alles andere bleibt wie gewohnt. Um die Funktion als Methode für ein Objekt zu registrieren, ist die Funktion _AutoItObject_AddMethod() zu nutzen:



    Zugriffsbeschränkungen
    Es ist möglich Attribute und Methoden mit Zugriffsbeschränkungen zu belegen. Bei den Attributen gibt es drei, bei den Methode zwei mögliche Beschränkungen.


    $ELSCOPE_PUBLIC (Attribut)
    Das Attribut ist von außerhalb lesbar und veränderbar.


    $ELSCOPE_READONLY (Attribut)
    Das Attribut ist von außerhalb nur lesbar.


    $ELSCOPE_PRIVATE (Attribut)
    Das Attribut ist von außerhalb überhaupt nicht ansprechbar.


    Diese Zugriffsbeschränkungen gelten nur, wenn man versucht auf das Attribut von außerhalb irgendwie zuzugreifen. Geschehen die Zugriffe direkt in eines der Methoden des entsprechenden Objektes gibt es keine Probleme. So können z.B. intern irgendwelche wichtigen Attribute angelegt werden ohne sich sorgen machen zu müssen dass sie außerhalb irgendwie manipuliert werden können. Es stellt eine Art Schutzmechanismus dar. Die Zugriffsberechtigungen können bei der Funktion _AutoItObject_AddProperty() im dritten Parameter übergeben werden.


    Wird bei der _AutoItObject_AddMethod() Funktion der vierte Parameter auf True gesetzt, so kann die Methode außerhalb nicht aufgerufen werden. Andere Methoden im gleichen Objekt hingegen können diese Methode ohne weiteres benutzen. Wird der Parameter jedoch weggelassen oder auf False gesetzt, so kann die Methode außerhalb aufgerufen werden.




    Klassen erstellen:
    Eine Klasse beinhalten ausschließlich die Definition eines Objektes. Das Objekt selber wird bei einer Klasse nicht erstellt. Durch die Klassen ist es möglich ein Objekt nur einmal zu definieren, aber dafür beliebig oft das selbe Objekt zu erstellen. Um eine neue Klasse zu erstellen liefert uns die AutoItObject UDF die Funktion _AutoItObject_Class mit.


    Die Klasse sollte dabei in eine eigene Funktion ausgelegt werden da nach der Verwendung des Attribut .Object die gesamte Klasse gelöscht wird. Die bereits erlernten Funktionen müssen bei einer Klasse als Methoden genutzt werden. Im weiteren Verlauf des Tutorials werde ich nur noch ausschließlich mit Klasse arbeiten. Natürlich bleibt es jeden selber überlassen ob das Objekt direkt erstellt wird oder mit Klassen gearbeitet wird. Beides liefert (wie bereits erwähnt) das selbe Ergebnis.




    Objekte Kopie / Referenz erzeugen:
    Was genau ist nun eine Kopie und was nun eine Referenz bezogen auf OOP? Bei einer Kopie handelt es sich um ein komplett neues Objekt welche alle Attribute und Methoden eines anderen Objektes kopiert. Bei einer Referenz hingegen, zeigt diese Referenz auf ein bereits existierende Objekt und greift auf die fremden Attribute und Methoden zu. Doch alles der Reihe nach.



    Kopie
    Um ein Objekt vollständig zu kopieren benötigen wir unsere bekannte Funktion _AutoItObject_Create(). Geben wir dort als Parameter des zu kopierenden Objektes mit, erhalten wir eine vollständige Kopie des Objektes:



    Referenz
    Um eine Referenz zu einem Objekt zu erzeugen, müssen wir lediglich den = Operator verwenden. Bedenke jedoch, wir erstellen damit kein neues Objekt, sondern zeigen auf ein existierendes Objekt:




    Konstruktor / Destruktor:
    In der OOP gibt es die Möglichkeit einen Konstruktor und einen Destruktor anzugeben. Beides sind nur Methoden in eines Objektes die jedoch nicht irgendwann, sondern zu einem ganz bestimmten Zeitpunkt aufgerufen werden. Der Konstruktor wird aufgerufen wenn ein Objekt erstellt wird, der Destruktor wenn dieses Objekt gelöscht wird. Jedoch bietet die AutoItObject UDF nur die Möglichkeit einen Destruktor anzugeben. Jedoch können wir unseren Konstruktor auch mit einem einfachen Workaround selber erstellen.



    Konstruktor
    Wie bereits erwähnt können wir so nicht direkt einen Konstruktor angeben, wir können aber Code ausführen unmittelbar nach der Erstellung eines Objektes. Somit haben wir die Funktionsweise eines Konstruktors mithilfe eines Workarounds nachgebaut:



    Destruktor
    Um eine Methode als Destruktor für ein Objekt zu definieren, benötigen wir einfach die Funktion _AutoItObject_AddDestructor(). Wird nun das Objekt gelöscht, so wird die zugewiesene Methode aufgerufen. Wir können sogar mehrere Destruktors zuweisen, sie werden dann in der umgekehrten Reihenfolge aufgerufen, in der sie zugewiesen wurden:


    Die Destruktors werden jedoch nur aufgerufen, wenn dass Objekt „per Hand“ gelöscht wird.




    Vererbung:
    Klassen können beliebig erweitert werden. Dies ist z.B. hilfreich wenn man eine bestehende Klasse einfach erweitern möchte. Wir benötigen nur eine Kopie der Klasse die wir erweitern möchten:


    Auch Objekte zu erweitern ist kein Problem, dafür stehen uns ja die Funktionen _AutoItObject_Create(), _AutoItObject_AddProperty(), _AutoItObject_AddMethod() und _AutoItObject_AddDestructor() zur Verfügung. Zudem haben wir auch die Möglichkeit mit _AutoItObject_RemoveMember bereits existierende Attribute und Methoden von einem Objekt zu löschen. Für Klassen steht parallel die Methode .RemoveMember() zur Verfügung:


    Vielleicht wunderst du dich jetzt warum AutoIt kein Fehler entgegen schmeißt. Da alle Objekte extern gehandhabt wird, weiß AutoIt beim Syntax Check nicht, ob das Attribut oder die Methode wirklich existiert. Stattdessen geben unzulässige Abrufe im besten Fall einfach einen Leerstring zurück.




    Abschluss Teil 1:
    OOP ist ein umfassendes Thema, deshalb sind mehrere Teile geplant welche immer das jeweilige Teilgebiet größtmöglich abdecken soll. In zukünftigen Teilen wird dann darauf eingegangen wie Objekte in DLLs angesprochen werden können. Doch zunächst möchte ich im nächsten Teil näher darauf eingehen wie sinnvolle Anwendungen / Spiele mit OOP realisiert werden können. Kritik, Anregungen und Wünsche sind gerne gesehen und willkommen! :)

  • Hallo Make


    danke, dass du dir diese Mühe für uns machst! :thumbup:
    Das ist sehr interessant und ich nehme mir vor, mir mit Hilfe dieses Tutorials (und den folgenden) Oop anzueignen.
    Ein kleiner Hinweis: Im Beispielcode der Klassenerstellung hat sich ein kleiner Fehler eingeschlichen. In Zeile 9 und 10 sollte das doch bestimmt "= Class().Object" sein.
    Hab ganz schön rumgerätselt bis ich das geschnalt hab ;)

  • Nein, sorry ^^
    Ist auch nicht geplant.
    Habe zwar das Tutorial komplett als Textdatei vorliegen,jedoch mit den BB-Codes für die Formatierungen. Jedoch schreibe ich gerade an einem BB-Code Viewer da es sehr lange dauert (bei meiner Inet-Verbindung) hier im Forum bis die Vorschau angezeigt wird. Ggf. kann ich da noch eine Speicherfunktion später einbauen sodass ich die Tutorials (also auch noch die, die kommen werden) als HTML Dateien hochladen kann. Bis dahin dauerts aber noch. :)


    Gesendet von meinem HTC Desire HD A9191 mit Tapatalk 2

  • Teil #2
    (Alle Beispiele sind für die AutoIt Version 3.3.12.0 geschrieben und laufen eventuell auf älteren Versionen nicht!)
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
    Teil #1


    Teil #3
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +




    Wofür OOP?
    Du weißt zwar jetzt was Objekte sind, aber vielleicht ist dir noch nicht klar geworden wofür man diese nun gebrauchen kann. Ich versuche es mal an einem Beispiel zu erklären. Stellen wir uns einmal eine GUI vor in dem viele unterschiedliche aber auch irgendwie ähnliche Controls enthalten sind. Beispielsweise Button Controls. Sie sehen vom Design her gleich aus, erfüllen aber je nach Programmierung einen anderen Zweck und beinhalten andere Texte. Jedes dieser Controls ist im Grunde nur ein Objekt, welches wir von seinen Eigenschaften her verändern können. Ein anderes Beispiel wäre da z.B. NPCs in diversen Computerspielen. Jeder reagiert unterschiedlich auf die Handlungen des Spielers und hat andere Eigenschaften. Kurz gesagt: Überall wo viele unterschiedlich aber irgendwie auch ähnliche Objekte existieren, kann OOP zum Einsatz kommen.




    Ziel des Teilabschnittes:
    Im ersten Teil des Tutorials hast du gelernt, wie man Objekte in AutoIt erstellt. In diesem Teil kommen die Objekte auch zum Einsatz. Bleiben wir einmal bei unseren GUI Controls. Wir wollen in diesem Abschnitt ein Pseudo Button erstellen. Dies wird zwar kein „echtes“ Control werden, erfüllt aber den Zweck eines Buttons. Ziel ist es, das Objekt möglichst so zu programmieren, dass es problemlos in jede Anwendung integriert werden kann. Dazu gehört auch, dass so viele Buttons wie gewünscht erstellt werden können.




    Schritt 1 - Erste Überlegungen:
    Zuerst einmal müssen wir uns überlegen was wir genau für unseren Button an Attribute und Methoden benötigen. Dazu ist es denke ich am einfachsten, wenn wir Schritt für Schritt vorgehen. Ein Button wird für gewöhnlich auf eine GUI erstellt, daher benötigen wir ein Attribut welches das Handle der GUI enthält (.handle). Zudem wird ein Button ja nicht irgendwo erstellt, sondern an der Position (.left|.top). Ein Button hat auch eine gewissen Höhe (.height) und Breite (.width) welche wir angeben können. Natürlich können wir für das Design dem Button einem Text zuweisen (.text). Damit haben wir schon 6 Attribute die wir im Objekt angeben können. Aber ein Button hat ja auch eine Funktion die Aufgerufen wird, sobald man es anklickt. Diese Funktion können wir in einem zusätzlichen Attribut (.func) hinterlassen. Jetzt muss der Button nur noch irgendwie in der GUI angezeigt werden, da können wir eine Methode (.draw()) definieren die das für uns erledigt. Damit haben wir im Grunde alles nötige für ein Button abgedeckt. Auf diese erste Definition des Objektes werden wir unseren Pseudo Button nun aufbauen:


    Nun steht quasi schon das Grundgerüst. Um den Button zu zeichnen benutze ich im folgendem GDI+. Wer damit nichts anfangen kann sollte sich vielleicht vorab dieses Tutorial hier anschauen: Gdi+ Tutorial [Part 5]




    Schritt 2 - Button zeichnen:
    Nun kommt eigentlich das tolle an OOP. Wir können direkt die Funktionalität zum zeichnen des Buttons erstellen ohne das wir großartig das ganze drum herum erst programmieren müssen. Wir können davon ausgehen dass das erstellte Objekt später auch alle nötigen Informationen enthält, dafür sind die Attribute schließlich da. Da wir nun mit GDI+ arbeiten und ggf. Ressourcen benötigen brauchen wir auch einen Destruktor welcher uns die gesamten Ressourcen wieder frei gibt, sobald das Objekt gelöscht wird. Klingt ja irgendwie auch logisch, oder?


    Die Handles zu den Ressourcen können wir später im Objekt als private Attribute hinterlegen. So kann sich das Objekt selber darum kümmern und von außen kann der Programmierer diese nicht „versehentlich“ überschreiben. Das bietet Sicherheit und weniger Gefahren für den Programmierer. Im Grunde müssen wir jetzt nur noch den Button auf unsere GUI zeichnen:


    Okey, ich gebe zu dass unser Button bis hier hin sehr bescheiden aussieht. Jedoch wird uns zumindest was in der GUI angezeigt, das ist doch schon mal was! Zwar ist es etwas unpraktisch dass wir noch die einzelnen Attribute selber setzen müssen (und das für jedes Objekt einzeln) aber darum kümmern wir uns später.




    Schritt 3 - Funktionalität:
    Noch kann unser Button nichts wenn man auf ihn klickt. Also fügen wir nun eine weitere Methode hinzu welche checkt ob der Button gedrückt ist. Wenn dieser tatsächlich gerade gedrückt ist, wird dann eine entsprechende Funktion aufgerufen. Unser Code erweitern wir also folgendermaßen:




    Schritt 4 – Optimierungen:
    Noch ist unser Button ziemlich unhandlich. Wir müssen alle Attribute per Hand setzen und dass ist nicht gerade so vorteilhaft. Zudem müssen wir uns auch manuell um das Zeichnen kümmern und auch für jedes Objekt nachprüfen ob der entsprechende Button gedrückt wurde. Jetzt machen wir aus unserem Objekt eine UDF. Wir nutzen einfach Prozedurale Programmierung um alle erstellten Buttons zu verwalten. Das spart Schreibaufwand beim Programmierer und vermindert mögliche Fehlerquellen die dieser praktizieren kann. Fangen wir einmal damit an, eine Funktion zu schreiben womit der Button auf einem Schlag erstellt wird. Im Grunde nehmen wir dem Programmierer damit die Arbeit ab, mehrere Zeilen für die Attribut-Definitionen zu verschwenden:


    Nun brauchen wir irgendein Mechanismus, der sich um alle Objekte verwaltet. Wir möchten ja schließlich alle Buttons mit nur einem einzigen Funktionsaufruf zeichnen. Und am besten sollen sich die Objekte gleich selber beim Programmende löschen:


    Wie man sieht wurde hier der Mechanismus mit einem globalen Array umgesetzt. Die _Button_Create() Funktion gibt jetzt auch nicht mehr das eigentliche Objekt zurück, sondern nur den Index zu dem globalen Array wo das Objekt nun abgespeichert ist. Leider geht das nicht anders! Sobald zu dem Objekt eine Referenz erstellt wird, muss sowohl die Referenz als auch das eigentliche Objekt gelöscht werden, damit auch wirklich das Objekt zerstört wird.




    Abschluss Teil 2:
    Nun haben wir einen eigenen Pseudo Button wovon wir gleich mehrere in eine GUI packen können. Zudem können wir nachträglich auch die Attribute verändern um so Größe, Position, Text und Funktion zu verändern. Am besten spielt ihr doch selber ein wenig damit herum. Letztendlich haben wir mithilfe von OOP nun eine eigene kleine UDF gebastelt. Wen das Design zu langweilig ist, der kann das Objekt ja selbständig noch erweitern! Dies sollte lediglich ein kleines Beispiel sein für sinnvollen Einsatz von OOP. Zu guter Letzt noch ein Beispielcode mit mehren anklickbaren Buttons:


    Wie immer sind Fragen, Kritik und Anregungen erwünscht! Weitere Teile folgen... :)

    • Offizieller Beitrag

    Mal ein kleiner Tipp zur Syntax. Es empfiehlt sich beim mehrfachen Zugriff auf dieselben Objekte od. Bestandteile dieses in With $obj ... EndWith abzuarbeiten.
    Also statt:

    besser:

  • Teil #3
    (Alle Beispiele sind für die AutoIt Version 3.3.12.0 geschrieben und laufen eventuell auf älteren Versionen nicht!)
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +
    Teil #1
    Teil #2
    + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ ++++++++++ +++++++++ ++++++++ +++++++ ++++++ +++++ ++++ +++ ++ +




    Background AutoItObject UDF:
    Vorab gibt es ein wenig Geschichtsunterricht. Alle Daten basieren natürlich auf eigener Recherche und deshalb kann ich nicht versprechen dass alles zu 100% stimmt. Ich gebe mir aber größte Mühe hier keinen Mist zu erzählen. :)


    Die Veröffentlichung der ersten Version (v1.1.0) fand am 19. Februar 2010 statt. Die wurde für die AutoIt Version 3.3.4.0 geschrieben. Der Hauptgedanke war es, mehr COM Objekte in AutoIt ansprechbar zu machen. Aus diesem Grund ist auch nur ein geringer Teil der UDF für OOP in AutoIt ausgelegt. Das Augenmerk richtete sich darauf mit Interfaces zu arbeiten um Bibliotheken wie Direct2D / Direct3D zugänglich zu machen. Wer sich einmal die mitgelieferten Beispiele angesehen hat, merkt schnell dass das eigentliche Potential in der UDF auf COM lag. Jedoch wurde am 11. September 2011 die AutoIt Version 3.3.7.15 (Beta) veröffentlicht, welche die Funktion ObjCreateInterface() mitlieferte. Dadurch war die AutoItObject UDF überflüssig geworden, da AutoIt seit dem an auf COM-Objekte nativ zugreifen kann. Das lustige dabei ist, dass trancexx (eine Entwicklerin der AutoItObject UDF) selber dafür verantwortlich war. In der Changelog von AutoIt steht nämlich folgende Zeile: - Added: ObjCreateInterface() (By trancexx). Ich vermute dass dies auch der Grund ist, warum die AutoItObject UDF in den 5 vergangenen Jahren keine große Fangemeinde gefunden hat. Zumindest finde ich selber nur wenige Skripte welche mit der AutoItObject UDF arbeiten. Außerdem gibt es kein einzigstes Tutorial dazu. Dies ist auch der Grund weshalb ich überhaupt mit dieser Tutorialreihe angefangen habe. Die AutoItObject UDF hat nämlich noch großes Potential in der OOP. Viele Aufgaben lassen sich einfacher mithilfe dieser UDF gestalten. Aus diesem Grund habe ich mich dazu entschieden nur bei der OOP in AutoIt zu bleiben und das Arbeiten mit COM-Objekten hier nicht zu behandeln. Dies werde ich in einem anderen separaten Tutorial noch tun. Hier möchte ich eher erklären wie Aufgabenstellungen analysiert und mithilfe von OOP umgesetzt werden können.




    Ziel des Teilabschnittes:
    In diesem Teil der Tutorialreihe geht es hauptsächlich um die Spielereien mit der AutoIt Syntax. Ich möchte hier einfach zeigen welche Möglichkeiten in AutoIt bereit stehen, um mit der UDF zu arbeiten. Es geht kurz gesagt einfach um allgemeine Programmiertechniken.


    Um es hier nicht im Tutorial nochmal wiederholen zu müssen verlinke ich einfach mal auf BugFix's Beitrag: With...EndWith




    Properties:
    Eigentlich könnte man meinen, dass Properties und Attribute das selbe sein müssten. Allein schon dass man mithilfe der _AutoItObject_AddProperty() Funktion ein Attribut erstellen kann. In der Funktionsweise ist ein Property und ein Attribut völlig identisch, ein Wert wird abgespeichert und kann wieder abgerufen werden. Jedoch unterscheiden sich beide vom Aufbau her. Daher ist die Funktionsbezeichnung in der UDF leider auch ein wenig missglückt. Kurz gesagt, ein Property ist eine Methode die aber den gleichen Effekt eines Attributes hat. Natürlich kommt jetzt die Frage auf, was für ein Sinn dann ein Property hat. In erster Linie geht es darum, dass man dynamischer arbeiten kann. Z.B. kann man in einem Property ein Array speichern, welches automatisch sich sortiert. Bei einem normalen Attribut müsste man dies erst noch „von Hand“ selber erledigen. Um nun ein Property zu definieren müssen, wir lediglich den Objekt einfach eine Methode anhängen welche einen optionalen Parameter hat. Mithilfe dieses Parameters können wir unterscheiden ob nun ein Wert zugewiesen oder abgerufen werden soll. Bleiben wir einfach mal bei der einfachsten Form eines Property:


    Im Objekt wird sowohl ein Attribut wie auch eine Methode gespeichert. Beides zusammen ergibt unser Property. Das Attribut wird als Speicherort für unser Property genutzt, die Methode um darauf zuzugreifen. Außerdem ist unschwer zu erkennen, dass wir das Property einmal als Methode (mit Klammern) oder als Attribut (ohne Klammern) aufrufen können. Diese Unterscheidung wird erst später interessant, für Properties sollte allerdings die Syntax wie bei einem Attribut gewählt werden. So ist es leichter zu erkennen dass es sich dabei um eine „Variable“ und keine Funktion handelt.



    Statisches Property
    In dieser oberen gezeigten Standardform ist das Property im Grunde nichts anderes als ein Attribut. Nun wird es Zeit, dass wir unseren Property ein wenig Leben einhauchen, dafür können wir statische Property definieren. Eine Änderung bezieht sich nicht mehr auf das aktuelle Objekt, sondern auf alle Objekte die dieses Property nutzen. Damit dürfte auch schnell klar sein, dass ab hier ein Attribut schon an seine Grenzen stößt. Ein statisches Property ist im Grunde einfach definiert, wer schon mal statische Variablen genutzt hat weiß worauf ich hinaus will:



    Beispiel für die Nutzung eines Property
    Bleiben wir mal bei unseren Array Beispiel. Speichern wir also ein Array in unser Property ab, so kann es dieses automatisch sortieren. Der Sinn und Zweck von Properties liegt damit nicht in der Speicherung von Werten, sondern in der direkten Verarbeitung der zu speichernden Werte. Ansonsten könnten wir uns ja einfach mit einem Attribut zufrieden geben. Als Beispiel sollte dieser Code ausreichen:



    Unterscheidung zwischen Methode und Property
    Jetzt kann man das alles noch regelrecht auf die Spitze treiben. Es ist möglich zu unterscheiden, ob ein Property als Methode (also mit Klammern) aufgerufen wird oder nicht. Damit lassen sich also vorab bestimmte Optionen für evtl. Spezialregelungen übergeben. Bleiben wir bei der Sortierung eines Arrays. Nehmen wir mal an, wir wollten einstellen können, ob das Array nach der ASCII Tabelle oder Alphanumerisch sortiert werden soll. Dafür könnten wir das Property einfach als Methode aufrufen um anzugeben, dass wir gerade eine Option ändern wollen und nicht den eigentlichen Wert des Property. Mit diesem Hintergrundgedanke macht auch das spezielle Attribut .__propcall__ Sinn. Dieses Attribut ist undokumentiert, daher erkläre ich einmal kurz seine Funktionsweise.


    Das .__propcall__ Attribut hat einen privaten Gültigkeitsbereich ($ELSCOPE_PRIVATE) wodurch es nur in Methoden nutzbar ist. Enthält das Attribut eine „1“, so werden Daten mithilfe des Gleichheitszeichen übergeben. Enthält es allerdings eine „0“, so werden entweder keine Parameter ($oObj.var oder $oObj.var()) übergeben oder eben mithilfe der Klammern ($oObj.var(5)).


    Mithilfe dieses Wissens können wir somit unterscheiden, wie nun die Methode genau aufgerufen wird. Als Beispiel sei einfach mal die Möglichkeit gegeben eine Zufallszahl mithilfe eines Property auszugeben. Als einstellbare Optionen können wir angeben in welchen Bereich die Zufallszahlen generiert werden sollen. Weisen wir den Property jedoch einen Wert zu, so wird der Seed für den Zufallsgenerator gesetzt:



    Hinweis
    Damit hast du nun alles nötige um die kompliziertesten Properties zu schreiben die dir einfallen. Du kannst ja beispielsweise einfach mal eine Option einbauen, um das Property zwischendurch einfach mal zum statischen Property zu switchen. Oder du verwendest gleich statische Optionen, so dass du einfach und bequem alle Einstellungen in jedem Objekt mit nur einer Zeile erledigen kannst. Wie du siehst bietet dir diese Art der Programmierung neue Freiheiten im Bezug auf OOP.




    Default Spezifikation:
    Ähnlich wie das .__propcall__ Attribut gibt es noch ein Spezialrezept in der AutoItObject UDF. Der Praktische nutzen liegt allerdings in der Syntax. Mir fällt dafür kein passender Name ein, deswegen nenne ich es einfach mal die Default Spezifikation. Mithilfe dieser Spezifikation kannst du „einem Objekt“ Werte zuweisen. Allerdings scheint dies nur in der Syntax so zu sein. Um unterscheiden zu können ob das das Objekt als Variable oder eben mit dieser Spezifikation ansprichst, müssen Klammern gesetzt werden. Beispiel:

    $oObj = ; value
    $oObj() = ; value


    In der ersten Zeile wird die AutoIt Variable angesprochen. Hat man also in dieser ein Objekt gespeichert, so wird die Referenz des Objektes zu dieser Variable gelöst. Setzt man allerdings wie in Zeile 2 gezeigt noch Klammern, so wird das eigentliche Objekt (und nicht die AutoIt Variable) damit angesprochen. Dadurch existiert eine eindeutige Unterscheidung aber auch eine potentielle Fehlerquelle. Aus diesem Grund weise ich vorher darauf hin. ^^



    Als Attribut
    Um diese Default Spezifikation als Attribut zu definieren, muss lediglich bei der Namensvergebung __defalut__ gewählt werden. Somit wird ab sofort auf dieses Attribut zugegriffen, sobald auf das Objekt zugegriffen wird. Kurzes Beispiel:



    Als Methode
    Parallel dazu kann auch eine Methode für die Default Spezifikation verwendet werden. Dann können sogar Parameter übergeben werden. Auch hier nur ein kurzes Beispiel dazu:



    Als Property
    Wer vorhin aufgepasst hat, der weiß dass es jetzt gar nicht so schwer ist auch die Default Spezifikation als Property festzulegen. Hier nochmal das Beispiel mit den Zufallszahlen, jedoch jetzt als Spezifikation:




    For...In...Next Schleife:
    Mithilfe der For...In...Next Schleife können nicht nur Arrays durchlaufen werden, sondern auch COM Objekte. Wer schon mal mit Dictionary's oder ArrayList's gearbeitet hat, der weiß wovon ich reden. Auch AutoItObject Objekte können damit durchlaufen werden, jedoch müssen wir selber festlegen auf welche Attribute/Properties zugegriffen werden sollen. Dafür stelle ich euch einfach mal die Funktion _AutoItObject_AddEnum() vor, womit wir genau dies festlegen können. Diese Funktion definiert 2 Methoden (welche jeweils nur ein Parameter besitzen dürfen) und ruft diese bei jedem Schleifendurchgang in der For...In...Next Schleife auf.


    Die $sResetFunc Funktion
    Der $sResetFunc Parameter ist der 3te Parameter in der _AutoItObject_AddEnum Funktion. Ich fange mit diesem Parameter an, da die hier definierte Funktion als erstes Aufgerufen wird. Diese Funktion wird nämlich noch vor dem eigentlichen durchlaufen der Schleife aufgerufen. Dadurch kann man den Zähler auf eine gewisse Startposition zu setzen oder (sofern man einen Mechanismus benutzt) spezielle Optionen getroffen werden. Dennoch können wir einzelne Elemente durchlaufen. Als Beispiel nehmen wir eine verkettete Liste. Wir durchlaufen die Liste bis zum letzten Glied. Als Vorlage dient uns folgendes Skript:


    Wir haben hier also Objekte welche in Attributen andere Objekte speichert. Zuerst definieren wir unsere Reset Funktion um den Startpunkt festzulegen. Diese Funktion sollte allerdings den ersten Parameter als ByRef übergeben, da wir ja den originalen Zähler verändern wollen:

    Func __List_ResetFunc($oSelf, ByRef $oList)
    $oList = $oSelf
    Return True
    EndFunc


    Es ist hier unbedingt zu beachten, dass True zurückgegeben wird, denn sonst wird der Zähler nicht gesetzt. Um dir also diese Information zu geben waren gut 8 Stunden Debugging nötig. :P



    Die $sNextFunc Funktion
    Diese Funktion wird aufgerufen, sobald die einzelnen Elemente durchlaufen werden. Sobald ein Error Code zurück gegeben wird, wird die Schleife abgebrochen. Hier die Funktion welche die einzelnen Elemente durchläuft:

    Func __List_NextFunc($oSelf, ByRef $oList)
    If Not IsObj($oList) Then Return SetError(1)
    Local $vRet


    $vRet = $oList.value
    $oList = $oList.next


    Return $vRet
    EndFunc


    Und dazu das gesamte Skript:



    Hinweis: Wer Objekte dynamisch durchlaufen möchte sollte sich diesen Thread mal anschauen!




    Abschluss Teil 3:
    Im Großen und Ganzen ist auch dieser Teil nun fertig. Ich hoffe euch damit neue Einblicke in die OOP mit AutoIt gegeben zu haben. Im nächsten Teil geht es dann um ein praktisches Anwendungsbeispiel mit dem A* Algorithmus. Dort werde ich erklären warum OOP zur Wegfindung Sinn macht und weshalb man mit prozeduraler Programmierung an seine Grenzen stößt. Wie immer ist Kritik und Anregungen erwünscht. Falls du konkrete Themenwünsche hast die ich behandeln soll, dann immer her damit! :)