Ressourcen-Teilung: was verbirgt sich dahinter?
Als die Themen der Referate an die Teilnehmer des Proseminars "Objektorientierte
Konzepte & Anwendungen", kurz OOKA, verteilt wurden, entschloß
ich mich das Thema "Ressourcen-Teilung" als Teilbereich der Objektorientierten
Programmierung zu behandeln. Diese Entscheidung basierte allerdings nicht
auf überragender Kenntnis der Materie oder auch nur auf einem fundiertem
Grundwissen, nein, vielmehr faszinierte mich die in diesem Begriff implizierte
Möglichkeit sich Arbeitsaufwand zu ersparen, indem man auf schon einmal
geschriebene oder auf von Dritten bereitgestellte Programme oder Programmteile
zurückgreifen kann.
Also begab ich mich auf die Suche nach Information, zunächst auf
der
"Cetus Links"-Seite.
"5725 Links zum Thema Objektorientierung" versprach die Überschrift,
mittlerweile sind es mehr als 8000. Da mußte
einfach auch etwas für mich dabei sein. Zu meinem Bedauern mußte
ich feststellen, daß der Begriff Ressourcen-Teilung nicht besonders
oft (sprich garnicht) genannt wurde. Das lag allerdings sicher daran, daß
es sich um eine Seite in englischer Sprache handelt. Dies stellte natürlich
kein Problem dar, nur;
welches ist die korrekte Übersetzung von Ressourcen-Teilung, vielleicht
resource-sharing, das wäre wohl zu einfach gewesen. Glücklicherweise
erfuhr ich, daß
sich die ersehnte Information dem Laien unter der Bezeichnung Reuse, zu
deutsch Wiederverwendung, verbarg. Tatsächlich ist die Wiederwendbarkeit
von Softwarekomponenten eine verbreitete und für Softwarehäuser
lebensnotwendige Praxis geworden. Die Wiederverwendung ist notwendig geworden
um den Entwicklungs- und Innovationszyklus von Softwareprodukten möglichst
kurz zu halten, und dies bei gleichzeitiger Kostenreduktion. Dies ist für
den Erfolg einer Softwareschmiede heutzutage sehr wichtig, da es die mittlerweile
sehr große Konkurrenz im Softwarebereich notwendig macht, Entwicklungszeit
und Kosten minimal zu halten, um konkurrenzfähig zu bleiben. Dadurch, daß
auf bereits fertige, und vor allen Dingen, erprobte Softwarekomponenten
zurückgegriffen wird, nimmt die Wartung einer Software weniger Zeit
in Anspruch und es kann Arbeit in Inovationen gesteckt werden, die das
Produkt im direkten Vergleich mit Konkurrenten attraktiver erscheinen lassen
könnten. Es macht zum Beispiel wenig Sinn bei der Programmierung einer
Bildverarbeitungssoftware, bei jeder neuen Softwaregeneration, die Arbeitsumgebung neu zu entwickeln,
wenn sie, von Anfang an, erweiterbar konzipiert worden war. Bei der Komplexität heutiger Anwendungen
wäre ein kompletter Neubeginn finanziell kaum tragbar.
Auch kann es sich für eine Softwarefirma
durchaus rechnen, bestimmte Teile einer Software in Auftrag zu geben, um
zum Beispiel den Veröffentlichungszeitpunkt der eigenen Software vor
dem der Konkurrenz legen zu können und die Nachfrage schon früher
befriedigen zu können. Ein Hersteller von Schachprogrammen könnte
eine im Grafikbereich spezialisierte Firma beauftragen, eine möglichst
ansprechende grafische Benutzeroberfläche zu entwerfen in der die
Figuren 3dimensional dargestellt und bewegt werden können.
Dadurch kann dieser Hersteller sein, von der Spielstärke vielleicht schon gutes
Programm, auch durch seine Erscheinung dem Käufer noch interessanter erscheinen.
Bei allen Argumenten für die Ressourcen-Teilung darf allerdings
auch nicht verschwiegen werden, daß die Umstellung auf eine Softwareentwicklung,
die Wiederverwendung effektiv nutzen kann, mit zunächst höheren
Entwicklungskosten einhergeht und eine strenge Disziplin bei der Programmierung
erfordert. Zunächst müssen Schnittstellen konzipiert werden,
Komponenten müssen auf diesen standartisierten Schnittstellen aufsetzen
können, und über diese Schnittstellen Informatinen austauschen können.
Zusammenfassend kann man feststellen:
Positive Auswirkungen der Ressourcen-Teilung:
- - Reduktion der Entwicklungszeit
- - Kostenersparnis
- - Zugriff auf erprobte Komponenten
- - mögliche Integration von anderen fremden Komponenten
Anfängliche Schwierigkeiten:
- - zunächst Investitionsbedarf
- - Umstellung der Programmiergewohnheiten
2. Welche Möglichkeiten gibt es um Ressourcen-Teilung
einzusetzen?
Die einfachste Lösung wäre natürlich durch simples Cut&Paste,
ausschneiden und einfügen, oft benutzte Programmteile zentral zu sammeln
und bei Bedarf in den Programmcode einzufügen. Diese Vorgehensweise
mag für kleine Projekte in imperativalen Programmiersprachen wie BASIC,
ich meine jetzt nicht die Visual-Derivate, oder Pascal noch praktikabel
sein, für große Projekte und besonders im Rahmen der OOP ist
diese Praxis indiskutabel. Darüberhinaus wäre es schwierig, fremden Code zu integrieren, zumindest in einer wie oben
beschriebenen Situation, also um zum Beispiel ein spezielles Control (ein
grafischer Benutzerdialog) einzufügen.
2.1 Modularisierung
Die wachsende Komplexität von Programmen macht es schon seit geraumer
Zeit nötig, daß sie bei ihrer Erstellung in viele kleinere Unterprogramme
aufgespalten werden. So kann das Problem, das die Implementierung der Funktionalität
des Programms darstellt, in kleinere Probleme aufspaltet
werden, um diese dann einzeln leichter lösen zu können. Dies
leisteten schon imperative Programmiersprachen wie C, Pascal oder Modula-2, indem sie
es ermöglichen, wiederholt auszuführende Teile der Programme
in Form von Funktionen aufzurufen. In Modula-2 ist es überdies möglich,
zusammengehörige Funktionen in Modulen zusammenzufassen,
die bei ihrer Erstellung dann getrennt voneinander getestet und natürlich
auch wiederwendet werden können. Ein Modul, dessen Funktionalität
Routinen für die Bildschirmausgabe beinhaltet, kann in verschiedensten
Programmen Verwendung finden. Diese Art der Programmierung bietet schon
wichtige Ansätze, wie etwa das Information-hiding, indem bestimmte
Daten nur in den zugehörigen Modulen verwaltet werden und dadurch
von außen transparent erscheinen.
2.2 Funktionsbibliotheken
Funtionsbibliotheken (shared libraries) sind Dateien in denen Funktionen
zusammengefaßt sind, die von verschiedenen Programmen aufgerufen
werden können. Diese Funktionen sind nichts anderes als Unterprogramme,
die bestimmte Aktionen ausführen. Man kann zum Beispiel eine Mathematikbibliothek
erstellen, die einzelne mathematische Funktionen bereitstellt. Dies können
z.B. der Sinus einer Zahl oder ihre Fakultät sein. Auf diese Weise
braucht man grundlegende Funktionen nicht jedesmal neu zu implementieren,
sondern kann die von der Bibliothek bereitgestellte Funktion nutzen. Eine
Implementierung von Funktionen in Funktionsbibliotheken ist hauptsächlich
dann sinnvoll, wenn mehrere Anwendungen (Programme) auf diese Funktionen
zugreifen sollen. Nicht sinnvoll ist diese Vorgehensweise, wenn die Funktionen
nur von einem Programm genutzt werden soll oder Dritte keinen Zugriff auf
die in der Bibliothek enthaltenen Routinen erlangen können sollen.
Zudem ist das Öffnen einer weiteren Datei und das Ausführen von
nicht in der Ursprungsdatei enthaltenem Code immer mit einer Geschwindigkeitseinbuße
verbunden.
2.3 Der objektorientierte Ansatz
Seit der Entwicklung der ersten programmierbaren Rechenmaschinen ist die
Komplexität der zu lösenden Probleme (und damit auch der Programme
als deren Lösung) immer weiter gewachsen. Deshalb suchte man nach
einer Lösung um diese Komplexität zu bewältigen. Dies gelingt
bekanntlich am besten indem man von dem speziellen Problem zu einem allgemeineren
abstrahiert (verallgemeinert). Ein Beispiel:
Der gaußsche Satz:
Weil er seine Schüler einige Zeit beschäftigen will, fordert Gauß' Lehrer
seine Schüler auf, alle Zahlen von 1 bis 100 zu addieren. Doch anstatt die
Zahlen stur schriftlich zu addieren, erkennt der kleine Gauß, daß ihn
folgende Rechnung erheblich schneller ans Ziel bringt:
|
Sie sehen; der alternative Lösungsweg führt schneller zum Erfolg,
obendrein kann die allgemeine Form für alle Zahlen benutzt werden.
Die Abstraktion ermöglicht uns die Wiederverwendung des Lösungsweges.
Auch die OOP setzt bei der Abstraktion von dem Ursprungsproblem an. Um
zu erklären wie sie die Methode der Ressourcen-Teilung unterstützt,
muß ich allerdings ein wenig ausholen. Man spricht von einer objektorientierten
Programmierung, weil das zu lösende Programmierproblem auf ein Objektmodell
projiziert wird. Das Objekt bildet eine Einheit deren Struktur aus zwei
Elementen besteht: Zustand (state) und Verhalten (behavior), wobei der
Zustand eines Objekts seine Implementierungsdetails bezeichnet, also die
Form in der es programmiert worden ist. Sein Verhalten
ist ein öffentlich zugänglicher Satz von Funktionen, über
die es mit seiner Umwelt (andere Objekte) kommuniziert. Wichtig ist die
bewußt gewählte Analogie zu Lebewesen, wenn von Objekten gesprochen wird.
Die objektorientierte Betrachtungsweise eines Programms soll erreichen, daß
eine anschauliche Abbildung der realen Welt, auf die "virtuelle" Welt des
Computerprogramms erfolgen kann.
Ein Objekt wird allerdings nicht direkt programmiert, sondern es wird
eine Objekt-Klasse aufgestellt.
Vergleichbar mit dem klassifizieren von Tieren in der Biologie,
werden Objektklassen benutzt, um Programmteile zusammenzufassen, zu ordnen und
ihre Herkunft, ja sogar Verwandschaft zu bestimmen.
Objekte werden dann als Instanzen von Klassen ins Leben gerufen, z.B. Horst als
Instanz der Klasse Mensch, die wiederum eine Unterklasse der Klasse Säugetier
sein kann, welche abermals eine Unterklasse der Klasse Lebewesen sein kann.
Das bedeutet, daß Klassen von anderen Klassen abgeleitet werden können
und schließlich die Beschreibung einer Klasse (sozusagen ihr Bauplan)
benutzt wird um ein Objekt ins Leben zu rufen. Die Unterklassen (subclasses)
können, müssen aber nicht, alle Funktionen, in der OOP Methoden
genannt, und Daten, in der OOP Attribute genannt, der Oberklassen (superclasses)
erben. Es ist sogar möglich, daß eine Subklasse Methoden und
Attribute von mehreren Superklassen erbt, sie zu den geerbten neue eigene
Methoden erhält oder geerbte Methoden durch eigene Überlagert werden.
Eine Überlagerung von Funktionen erreicht man dadurch, daß diese Methoden
in der Subklasse neu implentiert (neu geschrieben) wird.
Objekorientierte Programmiersprachen sind zum Beispiel C++ und Java. C++ ist die wohl bekannteste
unter den OO-Sprachen, sie wird aber von einigen Verfechtern der OOP als
eine der schlechteren OO-Programmiersprachen angesehen, denn C++ ist im
Bezug auf die Einhaltung der OO-Programmierweise und Richtlinien, nicht besonders
restriktiv, d.h. man braucht nicht unbedingt objektorientiert zu programmieren,
da C++ nicht vielmehr als die objektorientierte Weiterentwicklung der imperativen
Programmiersprache C ist und mit C++-Compilern auch nur in C geschriebene Programme
oder C/C++ Mischprogramme übersetzt werden können.
(Ein Compiler ist ein Programm, das in einer bestimmten Programmiersprache geschriebene Anwendungen, in
von dem Computer ausführbare Programme übersetzt). Dies macht es zu einem Hybriden,
was den Programmierer dazu verleiten kann, den OO-Anspruch nicht konsequent
zu erfüllen. Sehr viel
konsequenter ist die OOP in Java implementiert, welches aufgrund seiner
Verwendung im WWW bekannt wurde. Der Java-Compiler besitzt die hochinteressante
Eigenschaft Programme nicht direkt in Maschinencode zu übersetzen,
sondern einen sogenannten Bytecode erzeugt. Dieser Bytecode ist ein Maschinenunabhängiger
Code, welche von einem Programm, nämlich der "Virtual Machine" (kurz
VM; Virtuelle Maschine) interpretiert,
d.h. ausgeführt wird. Dadurch
wird sichergestellt, daß ein Java-Programm auf jedem Computer unter
jedem Betriebssystem ausgeführt werden kann. Der gängige Slogan
ist: "Write once, run everywhere", zu deutsch; Einmal schreiben, überall
ausführen. Voraussetzung ist jedoch, daß es eine VM für
diese Computerplattform gibt. Auch so können Ressourcen gespart werden; Anwendungen brauchen
nicht mehr für unterschiedliche Systeme geschrieben werden. Es werden nicht mehr unterschiedliche Teams
für Windows, UNIX, MacOS, AmigaOS ;-) gebraucht und die Anwendung liegt sofort für alle Betriebssysteme vor.
Wodurch unterstützt uns nun die OOP bei der Wiederwendung?
Durch die strenge Kapselung des Zustands eines Objekts, also seiner Daten
und seiner privaten Methoden (solche Methoden sind von außen nicht
aufrufbar), ist es nur möglich mit dem Objekt über ihm eigene
Funktionen, seinen Methoden, zu kommunizieren. Programmierer brauchen also
nicht die Art und Weise kennen in der ein Objekt ein Problem löst,
sondern nur dessen öffentliche Methoden. Sie können diese dann
aufrufen um es aufzufordern die jeweilige Funktion auszuführen. Diese
öffentlichen Funktionen sind die einzige Schnittstelle eines
Objekt zu anderen Objekten.
3. Komponenten-software-komponenten
Wäre es nicht praktisch, wenn sich Softwarekomponenten verhielten
wie ein Objekt? Man könnte sie in seine Programme einbauen und könnte
sie ihre öffentlichen Methoden steuern, ohne wissen zu müssen
wie sie Arbeit tun. Genau diese Kapselung des Quelltextes eines Objekts
gegenüber der Außenwelt ist wichtig, wenn es darauf ankommt
Softwarekomponenten kommerziell zu vertreiben. Wäre nämlich ihr
Quelltext von dem Käufer einsehbar, hätte dieser mit der Lösung
des Problems auch das Wissen erstanden um dieses Problem zu lösen.
Zwar würde der Käufer wahrscheinlich gegen Lizenzbedingungen
verstoßen und außerdem ist mancher Quellcode ähnlich schwer
zu lesen wie dieser Text, aber es wäre sehr viel einfacher als den
Maschinencode zurückzuübersetzen. Und dieses Know-How ist Firmenkapital,
genauso wie die 3D-Engine (der Teil, meistens eines Spiels, welcher die
Darstellung einer 3dimensionalen Umgebung auf dem Bildschirm leistet) eines
bestimmten Computerspiels, daß in Deutschland auf dem Index ist,
aber fast jeder zumindest mal angespielt hat. Ein Kapital, daß das
Fortbestehen eines Unternehmens sicherstellen kann. Deshalb sollten sich,
gerade in Lizenz gegebene Softwarekomponenten, wie eine Blackbox benehmen,
deren Innenleben unsichtbar ist. Das Manko der bisher aufgezählten
Verfahren ist, daß Teilprogramme, außer bei den Funktionsbibliotheken,
immer im Sourcecode jeweils der zu ihrer Erstellung benutzten Programmiersprache
vorliegen. Dies ist unter Umständen nicht die Programmiersprache in
der das Programm vorliegt, in der sie eingebettet werden sollen. Und selbst
wenn dem so wäre, wäre es schwierig die Details der Implemtierung
einer solchen Komponente geheimzuhalten. Also
benötigte man einen Schnittstellenstandard, der es ermöglichte,
daß in unterschiedlichen Programmiersprachen erstellte Programme
über einheitliche Schnittstellen kommunizieren können, und zwar
ohne daß diese Komponenten zum Zeitpunkt des Zusammenfügens
als Quelltext vorliegen müßten.
Mittlerweile gibt es gleich mehrere konkurrierende Standards für
Komponentensoftware. Nennen möchte ich hier nur OLE, Java bzw. Java-Beans
und Corba. Ich verzichte darauf, Corba näher zu beschreiben, denn es wird ausführlich
im Online-Referat "CORBA" von Jan Setzer vorgestellt, welches
ebenfalls im Rahmen dieses Proseminars entstand.
Aus historischen Gründen erwähne ich noch OpenDoc, eine interessantes Projekt,
das jedoch mittlerweile eingestellt worden ist.
3.1 OLE
OLE, hauptsächlich dürfte seine Implementierung ActiveX bekannt
sein, ist ein propietärer Standard von Microsoft, und somit nur auf
Rechnern die unter Windows laufen einsetzbar, traurigerweise sind das 90%
aller Computer. Bemerkenswert
ist vor allem der kommerzielle Erfolg dieser Lösung, denn es gibt
einen florierenden Markt mit Softwarekomponenten nach dieser Spezifikation.
Falls Sie einen Windows-Rechner besitzen, können Sie
ActiveX-Elemente im Verzeichnis C:\WINDOWS\SYSTEM finden. Sie besitzen
die Endung .OCX, falls Sie keinen Windows-Rechner haben: Gl... gehabt!
Sie haben dann aber leider auch keine OCXe;-).
Ausgerechnet ein BASIC war eine treibende Kraft bei
der Erschaffung dieses Standards für wiederverwendbare
Sofware-Komponenten. Ursprünglich waren AktiveX-Steuerelemente, damals
noch mit der Endung .VBX, als Möglichkeit
gedacht, um seinen VisualBASIC-Programmen Schalter hinzuzufügen, also
grafische Objekte, die auf Ereignisse reagieren konnten (Mausklick) und
vielfältige Aktionen ausführen konnten. Diese Steuerelemente
mußten allerdings in einer anderen Programmiersprache geschrieben
sein, (C++, Delphi) und über den OLE-Standard mit der Applikation
(OLE-Container) kommunizieren deren Bestandteil sie waren. Findige Programmierer
fanden allerdings heraus, daß sich diese Steuerelemente auch ausweiten
ließ. So gab es schon bald die unterschiedlichsten OLE-Steuerelemente,
vom einfachsten Schalter, zur fast kompletten Bildbearbeitungssoftware.
Dies entging Microsoft natürlich nicht und als Java im Internet immer
populärer wurde, lancierte man den, nun ActiveX getauften, Standard, als ultimative Lösung
für die Erstellung von Steuerelementen für das WWW. Den in Redmond hat man anscheinend
nichts gegen einheitliche Standards, es sei denn, man besitzt sie nicht.
(ActiveX ist wahrscheinlich eine von MS' Marketing-Abteilung erfundene, kommerziell
wohl besser verwertbare Bezeichnug als OLE-Steuerelement, und
bedeutet wohl aktives Steuerelement. Darum war die Bezeichnung
auch bald in aller Munde.)
3.2 Java und Java-Beans
Java ist eine von Sun entwickelte OO-Programmiersprache, sie erlangte Bekanntheit
als Programmiersprache für das WWW, viele Steuerelemente oder grafische
Spielereien auf WWW-Seiten sind in Java erstellt. Wenn sich also etwas
auf einer WWW-Seite bewegt oder sich Bedienelemente, beim herüberfahren
mit der Maus, verändern, könnte Java im Spiel sein. Nichtsdestotrotz
ist Java eine ernstzunehmende Programmiersprache, ganze Büroanwendungssoftware
ist schon in Java geschrieben worden, mit dem Vorteil, daß sie auf
fast allen Computerplattformen lauffähig ist. JavaBeans sind seit
erscheinen der Version 1.1 Bestandteil des Javastandards (JDK 1.1). Sie
sind ein VBX und OCX ähnlicher Komponentensoftware-Standard
für Java. Auch sie können komplexe Steuerelemente sein, die auf
Ereignisse reagieren und selber aussenden.
3.3 OpenDoc
Schließlich gab es noch OpenDoc, ein unter anderen von Apple und
IBM entworfener, alternativer Standard für Komponentensoftware. Er
wurde geschaffen um Microsoft nicht das Monopol in diesem aufstrebendem
Markt zu überlassen und gleichzeitig auch andere Plattformen als Windows
mit einem Komponentenstandard zu versorgen. Außerdem bot OpenDoc
volle Kompatibilität zu OLE bei gleichzeitiger technischer Überlegenheit.
Durch eine von Grund auf neue Konzeption, wurden Beschränkungen von
OLE umgangen.
Geschützte Marken sind, obwohl hier nicht besonders kenntlich gemacht,
natürlich Eigentum ihrer jeweiligen Besitzer.