Читать книгу Prinzipien des Softwaredesigns - John Ousterhout - Страница 27

Beispiele: Java und Unix-I/O

Оглавление

Eines der bekanntesten Beispiele für Klassizitis ist heutzutage die Klassenbibliothek von Java. Die Programmiersprache Java erfordert nicht viele kleine Klassen, aber in der Programmier-Community von Java scheint eine Kultur der Klassizitis Fuß gefasst zu haben. Um zum Beispiel eine Datei zum Einlesen serialisierter Objekte zu öffnen, musste man in der Java-Entwicklung viele Jahre lang drei verschiedene Objekte erstellen:

FileInputStream fileStream =

new FileInputStream(fileName);

BufferedInputStream bufferedStream =

new BufferedInputStream(fileStream);

ObjectInputStream objectStream =

new ObjectInputStream(bufferedStream);

Ein FileInputStream-Objekt stellt nur rudimentäre I/O zur Verfügung – es kennt kein gepuffertes I/O und kann auch keine serialisierten Objekte lesen oder schreiben. Das BufferedInputStream-Objekt ermöglicht das Puffern eines FileInputStream, und der ObjectInputStream bietet dann zusätzlich das Lesen und Schreiben serialisierter Objekte. Die ersten beiden Objekte im Code (fileStream und bufferedStream) werden nie mehr verwendet, nachdem die Datei erst einmal geöffnet wurde – alle weiteren Operationen nutzen objectStream.

Es ist besonders nervig (und fehleranfällig), dass das Puffern explizit angefordert werden muss, indem ein eigenes BufferedInputStream-Objekt erstellt wird – vergisst ein Entwickler, dieses Objekt zu erzeugen, gibt es kein Puffern, und die I/O wird langsam sein. Vielleicht würde man bei der Java-Entwicklung argumentieren, dass nicht jeder die Datei-I/O puffern will, daher sollte es nicht in den zugrunde liegenden Mechanismus eingebaut sein. Eventuell wäre es ja besser, das Puffern separat zu halten, damit die Entwickler selbst entscheiden können, ob sie es nutzen wollen oder nicht. Es ist zwar gut, die Wahl zu haben, aber Schnittstellen sollten so designt sein, dass sie den häufigsten Fall so einfach wie möglich machen (siehe die Formel auf Seite 20). So gut wie jede Anwendung der Datei-I/O wird einen Buffer nutzen wollen, daher sollte es standardmäßig angeboten werden. Für die wenigen Situationen, in denen Puffern nicht erwünscht ist, kann die Bibliothek einen Mechanismus anbieten, um es zu deaktivieren. Jeder Mechanismus, der das Puffern abschaltet, sollte in der Schnittstelle ganz klar getrennt sein (zum Beispiel durch einen eigenen Konstruktor für FileInputStream oder mithilfe einer Methode, die den Puffermechanismus abschaltet oder ersetzt), sodass sich die meisten Personen dessen Existenz beim Entwickeln gar nicht bewusst sind.

Im Gegensatz dazu haben die Designer der Unix-Systemaufrufe den häufigsten Fall einfach gestaltet. Sie haben beispielsweise erkannt, dass sequenzielles I/O am gebräuchlichsten ist, daher haben sie das zum Standardverhalten gemacht. Der wahlfreie Zugriff ist auch noch recht leicht mit dem Systemaufruf lseek zu nutzen, aber braucht man beim Entwickeln nur sequenziellen Zugriff, muss man diesen Mechanismus nicht kennen. Besitzt eine Schnittstelle viele Features, von denen aber fast immer nur wenige benötigt werden, ist die effektive Komplexität dieser Schnittstelle einfach die Komplexität der am häufigsten eingesetzten Features.

Prinzipien des Softwaredesigns

Подняться наверх