Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Die Basis der Objektorientierung
3 Die Prinzipien des objektorientierten Entwurfs
4 Die Struktur objektorientierter Software
5 Vererbung und Polymorphie
6 Persistenz
7 Abläufe in einem objektorientierten System
8 Module und Architektur
9 Aspekte und Objektorientierung
10 Objektorientierung am Beispiel: Eine Web-Applikation mit PHP 5 und Ajax
A Verwendete Programmiersprachen
B Literaturverzeichnis
Stichwort
Ihre Meinung?

Spacer
 <<   zurück
Objektorientierte Programmierung von Bernhard Lahres, Gregor Rayman
Das umfassende Handbuch
Buch: Objektorientierte Programmierung

Objektorientierte Programmierung
2., aktualisierte und erweiterte Auflage, geb.
656 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1401-8
Pfeil 8 Module und Architektur
  Pfeil 8.1 Module als konfigurierbare und änderbare Komponenten
    Pfeil 8.1.1 Relevanz der Objektorientierung für Softwarearchitektur
    Pfeil 8.1.2 Erweiterung von Modulen
  Pfeil 8.2 Die Präsentationsschicht: Model, View, Controller (MVC)
    Pfeil 8.2.1 Das Beobachter-Muster als Basis von MVC
    Pfeil 8.2.2 MVC in Smalltalk: Wie es ursprünglich mal war
    Pfeil 8.2.3 MVC: Klärung der Begriffe
    Pfeil 8.2.4 MVC in Webapplikationen: genannt »Model 2«
    Pfeil 8.2.5 MVC mit Fokus auf Testbarkeit: Model-View-Presenter

Wir betrachten in diesem Kapitel anhand einer Reihe von Beispielen, wie objektorientierte Entwürfe in größere Kontexte eingebunden werden. Dabei diskutieren wir die verschiedenen Arten, mit denen Module in diesen Kontexten angepasst und erweitert werden können. Am Beispiel des Musters Model-View-Controller stellen wir vor, wie objektorientierte Verfahren Beiträge zu einer kompletten Systemarchitektur leisten können.

8 Module und Architektur


Rheinwerk Computing - Zum Seitenanfang

8.1 Module als konfigurierbare und änderbare Komponenten  Zur nächsten ÜberschriftZur vorigen Überschrift

Module sind die Bausteine, aus denen sich Software zusammensetzt. Die Mechanismen der Objektorientierung sollen uns helfen, diese Module zu definieren und interagieren zu lassen. Wir geben in diesem Abschnitt einen Überblick über die verschiedenen Möglichkeiten, Module erweiterbar zu halten und aus diesen Modulen ein funktionierendes System aufzubauen. Aber zunächst stellen wir die Frage: »Gibt es überhaupt so etwas wie eine objektorientierte Architektur?«


Rheinwerk Computing - Zum Seitenanfang

8.1.1 Relevanz der Objektorientierung für Softwarearchitektur  Zur nächsten ÜberschriftZur vorigen Überschrift

Der Begriff Softwarearchitektur allein kann Grundlage für ganze Workshops sein. Eine einheitliche Definition werden Sie auch in der Literatur nicht finden.

Icon Hinweis Deshalb versuchen wir, den Begriff Architektur anhand der Eigenschaften zu beschreiben, die Softwarearchitekturen häufig zugeschrieben werden:

  • Architektur ist das, was in einem Softwaresystem wichtig oder auch nur schwer zu ändern ist. [Als Konsequenz ist dann ein Softwarearchitekt in einem Projekt jemand, der wichtig ist oder dessen Meinung schwer zu ändern ist. ]
  • Architektur ist auch das, was wir jemandem, der nichts über unser System weiß, als Erstes vorstellen, damit er einen Überblick über das System erhält.
  • Architektur hat Auswirkungen auf die Entwickler eines Systems, weil sie Vorgaben macht, wie bestimmte Dinge umzusetzen sind.

Objektorientierung ist nicht in erster Linie ein Verfahren zum Architekturentwurf. Wir sprechen deshalb in der Regel von objektorientiertem Systemdesign, nicht von einer objektorientierten Architektur.

Allerdings hat ein objektorientiertes Design Rückwirkungen auf die Systemarchitektur, und umgekehrt kann eine Systemarchitektur die Anwendung von objektorientierten Verfahren erleichtern oder erschweren.

Diskussion: Sind Architekturen schwer zu ändern?

Gregor: Muss es denn wirklich immer so sein, dass eine Architektur schwer zu ändern ist? Ich denke, dass wir Architekturen auch so anlegen können, dass wir mögliche Änderungen schon mit in Betracht ziehen und die Architektur selbst änderbar halten.

Bernhard: Kannst du dafür ein Beispiel nennen? Nach meiner Erfahrung ändert man Architekturen nur mit größerem Aufwand. In der Praxis wirft man oft eine Architektur komplett weg und beschäftigt sich dann mit einer neuen Architektur.

Gregor: Da kann ich dir ein konkretes Beispiel nennen. In einem unserer Projekte haben wir eine Architektur auf Basis eines J2EE-Applikationsservers verwendet. Dabei haben wir aber explizit mit Pojos, also reinen Java-Objekten, gearbeitet und zum Beispiel die Mechanismen von Enterprise Java Beans nicht genutzt. In der Folge konnten wir unsere Architektur für bestimmte Szenarien einfach auf eine reine Client-Server-Architektur umstellen, in der die komplette Logik inklusive des Datenzugriffs in den Client verlagert ist. Wir konnten praktisch durch Konfiguration unsere Architektur von Thin-Client auf einen Fat-Client umstellen.

Bernhard: Das ist sicherlich ein guter Ansatz. Allerdings sehe ich es eher so, dass eure Architektur beide Varianten umfasst. Damit sind praktisch beide Verteilungsverfahren Teil eurer Architektur. Das spricht zwar für deren Qualität, aber eine Änderung der Architektur liegt eben beim Umschalten des Verteilungsverfahrens nicht vor. Änderungen an der Architektur selbst wären immer noch vergleichsweise schwer durchzuführen.
Rheinwerk Computing - Zum Seitenanfang

8.1.2 Erweiterung von Modulen  topZur vorigen Überschrift

Beim Entwurf von Modulen ist die Fragestellung zentral, wie sich diese Module später erweitern lassen. Wir stellen eine Reihe von etablierten Verfahren vor, mit denen Erweiterbarkeit unterstützt wird.

Vererbung

Wenn wir eine Möglichkeit suchen, um existierende Module zu erweitern, fällt uns im Bereich der Objektorientierung natürlich die Möglichkeit der Vererbung ein. Der einfachste (aber eben auch naive) Ansatz für eine Modulerweiterung ist es, für eine Klasse, die uns zur Nutzung zur Verfügung gestellt wird, eine abgeleitete Klasse zu definieren und stattdessen diese zu nutzen. Eine denkbare Architektur eines Systems wäre es also, während der Weiterentwicklung die existierende Hierarchie von Klassen immer weiter aufzufächern und neue Unterklassen einzuführen, sobald neue Anforderungen erkennbar werden. Dies führt aber in der Regel auch dazu, dass existierende Hierarchien geändert werden müssen. Dies ist in der Regel nur dann möglich, wenn es sich um interne Module handelt und die explizite Hierarchie der Klassen nicht nach außen offen gelegt ist.

In dieser Form ist Vererbung also als Basis für Erweiterungen eines Systems nicht gut geeignet. Sie haben auch bereits in Abschnitt 5.3.2, »Das Problem der instabilen Basisklassen«, gesehen, welche Probleme durch eine solche Nutzung von Vererbungsbeziehungen entstehen können.

Abgeleitete Klassen

Natürlich können Sie trotzdem Vererbung zur Erweiterung von Modulen einsetzen. Stellen Module eine dokumentierte Menge von Klassen und damit Operationen zur Verfügung, so können Sie für diese Klassen abgeleitete Klassen erstellen, die begrenzte Änderungen einführen. Wenn Sie dann in Ihrer Applikation ein Exemplar der abgeleiteten Klasse erstellen, wird dieses das geänderte Verhalten zeigen.

Fabriken als Erweiterungspunkte

Damit dieser Ansatz sinnvoll funktionieren kann, müssen sich die Entwickler eines Moduls allerdings bereits intensiv Gedanken gemacht haben, was die über Ableitung von Klassen nutzbaren Erweiterungspunkte des Moduls sind.

In Abschnitt 7.2, »Fabriken als Abstraktionsebene für die Objekterzeugung«, haben wir gesehen, wie uns verschiedene Verfahren zur Objekterzeugung definierte Möglichkeiten bieten, Module nachträglich noch zu erweitern. Fabriken sind auch ein zentraler Mechanismus, mit dem sogenannte Container arbeiten. Durch diese Methodik lassen sich begrenzte und definierte Anpassungen an Modulen vornehmen.

Einen ähnlichen Zweck verfolgt auch das Entwurfsmuster »Strategie«, das Sie in Abschnitt 2.5.1 kennen gelernt haben. Mit Hilfe des Entwurfsmusters lässt sich das Verhalten von existierenden Klassen anpassen.

Beide Mechanismen bieten uns Möglichkeiten, eigene Funktionalitäten in existierende Module einzubringen, ohne diese Module öffnen zu müssen. Sie sind also Beispiele für Vorgehensweisen, die uns helfen, das zentrale Prinzip Offen für Erweiterung, geschlossen für Änderung umzusetzen.

Frameworks

Eine eigene Sichtweise auf Erweiterbarkeit nehmen Frameworks ein. Diese haben als zentrales Konzept die Umkehr des Kontrollflusses (Inversion of Control).


Icon Hinweis Frameworks (Anwendungsrahmen)

Das Grundkonzept von Frameworks ist es, einen Rahmen für eine Anwendung oder einen Anwendungsbereich zur Verfügung zu stellen. Damit legen Frameworks eine Art Schablone für diesen Bereich fest, die bei der Entwicklung einer konkreten Anwendung dann ausgeprägt wird.

Die Entwicklung einer Anwendung auf Basis von Frameworks besteht darin, dass Klassen und Methoden umgesetzt werden, die aus dem bereits existierenden Framework heraus aufgerufen werden. Damit liegt die Steuerung des Kontrollflusses komplett bei den Framework-Klassen. Das Frameworks zugrunde liegende Prinzip wird deshalb auch Umkehrung des Kontrollflusses (Inversion of Control) oder Hollywood-Prinzip genannt.2


Ablaufsteuerung

Der intuitive Normalfall bei der Entwicklung eines Programms in einer prozeduralen Programmiersprache ist es, dass der Programmablauf durch den von Ihnen geschriebenen Code bestimmt wird. Sie schreiben auf, was ausgeführt werden soll, und genau in dieser Reihenfolge passiert es dann auch.

Mit den Mitteln der objektorientierten Programmierung wird bereits ein relevanter Teil der Ablaufsteuerung nicht mehr explizit beschrieben, sondern an Mechanismen der Programmiersprache abgegeben. Bei Aufruf einer polymorphen Operation entscheidet der Typ eines Objekts zur Laufzeit darüber, welche Methode nun genau aufgerufen wird.

Wenn Sie nun aber die Kontrolle darüber, wann im Programmablauf unsere implementierte Funktionalität tatsächlich aufgerufen wird, an ein anderes Modul abgeben, sprechen wir von einer Umkehrung des Kontrollflusses. Frameworks sind Zusammenstellungen von Klassen, die den Rahmen für den Ablauf von solchen Anwendungen liefern. Eine Anwendung, die ein Framework nutzt, ist damit von der Aufgabe entbunden, den Kontrollfluss selbst zu steuern. Sie stellt nur noch im Rahmen dieses Kontrollflusses benötigte fachliche Implementierungen zur Verfügung, der Kontrollfluss ist damit umgekehrt worden.

Schablonenmethoden

Ebenfalls häufig in Frameworks anzutreffen sind neben den bereits erwähnten Fabriken die Schablonenmethoden. Wir haben diese in Abschnitt 5.3.1, »Überschreiben von Methoden«, bereits vorgestellt. Schablonenmethoden geben einen Rahmen vor, innerhalb dem Operationen aufgerufen werden, die erst von abgeleiteten Klassen implementiert werden müssen. Dadurch können Frameworks über abstrakte Klassen eine Sequenz von Aktionen steuern.

In Abschnitt 8.2 werden Sie den Ansatz Model-View-Controller (MVC) für die Steuerung des Kontrollflusses in der Präsentationsschicht kennen lernen. In einem MVC-Framework können zum Beispiel die Klassen, die für konkrete Darstellungen auf dem Bildschirm zuständig sind, als Ableitungen von abstrakten Klassen umgesetzt werden. Diese müssen dann für ihre korrekte Aktualisierung sorgen. Die Erstellung dieser auch Views genannten Klassen und die Interaktion mit anderen Komponenten kann jedoch vom Framework übernommen werden.

Swing, MFC

Typische Beispiele sind hierbei Frameworks, die den Ablauf für Interaktionen an einer Benutzeroberfläche steuern. So bringen zum Beispiel die GUI-Frameworks Swing und Microsoft Foundation Classes (MFC) Bestandteile mit, die generell die Interaktion innerhalb eines MVC-Ansatzes regeln. Die vom Anwendungsprogrammierer eingebrachten Module fügen sich in den Ablauf des Frameworks ein. Die neu umgesetzte Funktionalität wird vom Framework innerhalb dessen eigenem Kontrollfluss aufgerufen.

Frameworks: Probleme

Frameworks haben allerdings in der Praxis mit ein paar Problemen zu kämpfen. Das Hauptproblem ist dabei, die vorzusehenden Erweiterungspunkte zu bestimmen, ohne schon alle konkreten Anwendungsfälle zu kennen. Werden zu wenige oder die falschen Erweiterungspunkte eingebaut, ist das Framework zu unflexibel. Werden zu viele Erweiterungspunkte eingebaut, wird das Framework zu komplex und schwierig zu warten.

Was soll erweiterbar sein?

In der Praxis ist es nicht einfach, die Punkte zu bestimmen, die für Erweiterungen zugänglich sein sollen. Oft wird zu viel von den Eigenschaften der Framework-Klassen öffentlich gemacht. Ist diese öffentlich gemachte Information dann einmal von Framework-Anwendern verwendet worden, kann sie praktisch nicht mehr geändert werden und verhindert zu einem gewissen Grad notwendige Umbauarbeiten innerhalb des Frameworks. Nach unserer Erfahrung hat ein Framework diesen Zustand erreicht, wenn häufiger der Satz fällt: »Aber ihr verwendet das Framework doch ganz falsch! Dafür war diese Methode nie gedacht!« In diesem Fall wurde zu viel von den Interna des Frameworks offen gelegt, oder dieses ist einfach insgesamt zu komplex geworden.

JUnit

Frameworks haben sich deshalb meist dort bewährt, wo sie für einen klar definierten und überschaubaren Einsatzbereich konzipiert worden sind. Ein gutes Beispiel ist das JUnit-Framework, das erfolgreich für den Test von Java-Modulen eingesetzt wird. Zunächst einmal weist JUnit ein sauberes Design auf.

Ein weiterer sehr wichtiger Punkt ist aber, dass der von JUnit abgedeckte Bereich so eingegrenzt ist, dass die notwendigen Erweiterungspunkte des Frameworks überschaubar geblieben sind. Deshalb ist JUnit für den gewählten Einsatzbereich auch so nützlich und stabil. Erst die Erweiterung der Sprache Java um die Annotations führte zu größeren Änderungen von JUnit.

Frameworks für technische Abläufe

In den meisten Anwendungsfällen versuchen Frameworks, die technischen Abläufe zu kapseln, so dass die Umsetzung der Fachlichkeit erfolgen kann, ohne sich mit allen technischen Details beschäftigen zu müssen. JUnit und die bereits erwähnten MVC-Frameworks sind Beispiele hierfür. Komplexer wird die Aufgabenstellung, wenn Gemeinsamkeiten von fachlichen Abläufen und Objekten über ein Framework strukturiert werden sollen. Sofern dies innerhalb eines Unternehmens mit einer weitgehend homogenen Sicht auf Objekte und Prozesse geschieht und außerdem das entstehende Framework auf beobachteten Gemeinsamkeiten verschiedener Bereiche basiert, ist eine Framework-Umsetzung auch noch aussichtsreich.

Fachliche Frameworks

Die Ausdehnung von Frameworks auf die Fachlichkeit von Geschäftsanwendungen über verschiedene Unternehmen und Branchen hinweg hat sich dagegen als sehr komplexe Aufgabe erwiesen. IBM hat mit den San Francisco Framework Classes gegen Ende der 90er-Jahre einen Versuch unternommen, Frameworks auf den fachlichen Bereich von Unternehmensanwendungen auszudehnen. Ziel war es dabei, die Gemeinsamkeiten von Geschäftsobjekten und der zugeordneten Prozesse über ein fachliches Framework abzubilden. Die spezifischen Ausprägungen sollten dann über die Mittel der zur Verfügung stehenden Erweiterungspunkte an die fachlichen Anforderungen erfolgen. Die San Francisco Classes wurden allerdings nie wirklich erfolgreich. Microsoft arbeitet mit den Microsoft Business Classes an einem neuen Versuch, mit dem es aber noch keine praktischen Erfahrungen gibt.

Nach unserer Einschätzung sind Frameworks, die relevante Teile von Fachlichkeit bereits abbilden, nur dann realistisch einsetzbar, wenn nicht nur das Framework, sondern auch die Geschäftsprozesse selbst anpassbar sind. Der klassische Ansatz von SAP illustriert das, dort gibt die Software einen relevanten Teil der Prozesse vor, ein nutzendes Unternehmen muss sich weitgehend daran anpassen.

Container

In der Praxis hat sich eine besondere Form von Frameworks etabliert, deren Fokus darauf liegt, den Lebenszyklus von Objekten selbst zu verwalten. Diese Frameworks werden als Container bezeichnet.


Icon Hinweis Container

Container sind eine spezielle Form von Frameworks, die sich um den Lebenszyklus von Objekten kümmern. Für die so verwalteten Objekte bieten Container dann Basisdienste an und machen diese für einen Anwender nutzbar.

Der Begriff Container rührt daher, dass die Objekte einer Applikation im Container enthalten sind. Sie werden also komplett unter dessen Kontrolle gestellt. Dafür, dass sie die Dienste eines Containers in Anspruch nehmen können, müssen Objekte, die von einem Container verwaltet werden, im Gegenzug ebenfalls eine Leistung erbringen. Der mit dem Container geschlossene Kontrakt erfordert dabei häufig, dass sich die vom Nutzer des Containers eingebrachten Komponenten eine Reihe von Schnittstellen zur Verfügung stellen bzw. implementieren und sich an bestimmte Konventionen halten.

Ein Beispiel dafür sind die Container für Enterprise Java Beans. Dort wird ein EJB-Container als Bestandteil eines Applikationsservers gesehen, der für eine Reihe von übergreifenden Aufgaben zuständig ist. Über Konfiguration wird festgelegt, welche Eigenschaften den Objekten zugeordnet werden.


Container im Allgemeinen sind aber Laufzeitumgebungen, in denen Objekte verwaltet werden und dort bestimmte Services nutzen können. Es ist also nicht grundsätzlich notwendig, dass ein solcher Container Teil eines Applikationsserver ist.

Leichtgewichtige Container

Diese Bindung an Applikationsserver aufzuheben ist eine Zielsetzung der sogenannten leichtgewichtigen Container. Ein Beispiel für solche Container ist der Ansatz des Spring-Frameworks, bei dem die fachlichen Klassen alle als einfache Java-Klassen, sogenannte Pojos, umgesetzt werden. [Der Begriff Pojo für Plain Old Java Objects wurde von Martin Fowler eingeführt. Die Motivation war, den guten alten Java-Klassen durch einen hippen Namen wieder zu mehr Präsenz zu verhelfen. Überhaupt gelingt es Martin Fowler häufig, Begriffe für Vorgehensweisen und Sachverhalte zu prägen. So stammt auch der Begriff Dependency Injection für eine bestimmte Form der Umkehrung des Kontrollflusses von ihm und Rod Johnson. Value Objects in der J2EE-Spezifikation wurden zu Data Transfer Objects, nachdem Martin Fowler diese Benennung kritisiert hatte. ]

Dependency Injection

Den Mechanismus der Dependency Injection haben wir bereits in Abschnitt 7.2.7 beschrieben. Dependency Injection ist ein Mechanismus, der als Bestandteil einer Architektur verwendet werden kann. Über Dependency Injection werden die Abhängigkeiten zwischen genutzten Modulen und ihren Nutzern aus dem Code extrahiert. Der Container, der unsere Objekte verwaltet, ist dafür zuständig, die genutzten Objekte bereitzustellen. Die Entscheidung, ob dabei ein einziges Exemplar ausreicht oder ob für jeden Nutzungsfall ein eigenes Exemplar notwendig ist, liegt in der Konfiguration beziehungsweise beim Container. Bei Nutzung dieses Mechanismus ist es zum Beispiel auch nicht mehr notwendig, mit Singletons zu arbeiten. Ein Singleton wäre in diesem Fall durch Konfiguration über den Container zu erreichen, wir haben aber bei keiner Klasse mehr die unbedingte Voraussetzung, dass nur ein Exemplar davon existieren kann.

Plugins

Am Beispiel der freien Entwicklungsplattform Eclipse ist zu sehen, dass sich komplexe Applikationen auch auf der Grundlage von sogenannten Plugins aufbauen lassen.

Plugin ist wieder ein recht schillernder Begriff. Wir setzen unsere Definition auf der Beschreibung von Martin Fowler auf, der eine große Erfahrung in der Definition von schwammigen Begriffen mitbringt. [Siehe auch [Fowler 2003] oder auch http://www.martinfowler.com/eaaCatalog/plugin.html. ]


Icon Hinweis Plugin

Ein Plugin ist ein Modul, das an einem Erweiterungspunkt eines Programms eingesetzt werden kann. Dabei wird über eine zentrale Konfiguration gesteuert, welches Plugin verwendet werden soll und wie das Plugin selbst konfiguriert wird.

Ein Plugin löst zwei Probleme durch eine zentrale, zur Laufzeit ausgewertete Konfiguration: Konfigurationsinformation, die in Fabriken über die Applikation verteilt ist, ist schwer zu pflegen. Außerdem soll eine Änderung der Konfiguration nicht erfordern, dass ein Neubauen oder eine erneute Auslieferung notwendig wird.


Plugins, wie sie von Eclipse verwendet werden, basieren auf der Definition von Erweiterungspunkten. Hier ist es zum einen möglich, eigene Erweiterungen einzubringen. Auf der anderen Seite können diese aber bereits wieder selbst Erweiterungsmöglichkeiten definieren, so dass eine gestaffelte Erweiterung der Plattform möglich wird. Diese Möglichkeit wird von Eclipse ganz klassisch als Konzept der Erweiterungspunkte (extension points) bezeichnet.

Es deutet einiges darauf hin, dass Eclipse mit diesem Konzept zumindest für den zunächst gewählten Anwendungsbereich (nämlich eine Entwicklungsplattform) auf einem guten Weg ist. Der Ansatz vermeidet viele Fallen der klassischen Frameworks und hält die Komplexität durch seine klare Aufgabentrennung über Erweiterungspunkte in überschaubarem Rahmen.

Einfacher Einbau der Erweiterungspunkte

Was hier ebenfalls wichtig ist und was nicht bei allen Komponentenmodellen bisher beachtet wurde: Es muss einfach sein, einen Erweiterungspunkt einzubauen, sobald klar wird, dass dieser benötigt wird. Damit sind wir nicht gezwungen, von vornherein alle möglichen Erweiterungspunkte in unserem Modul vorwegzunehmen.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 <<   zurück
  Zum Rheinwerk-Shop
Neuauflage: Objektorientierte Programmierung






Neuauflage:
Objektorientierte Programmierung

Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: Einstieg in Python






 Einstieg in Python


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern