Design mit statischen Elementen

Obwohl Java weitgehend objektorientiert ist, ist es keine reine objektorientierte Sprache. Einer der Gründe, warum Java nicht rein objektorientiert ist, ist, dass nicht alles darin ein Objekt ist. Zum Beispiel Java können Sie Variablen von primitiven Typen erklären ( int, float, boolean, usw.) , die nicht - Objekte sind. Und Java verfügt über statische Felder und Methoden, die unabhängig und von Objekten getrennt sind. Dieser Artikel enthält Ratschläge zur Verwendung statischer Felder und Methoden in einem Java-Programm unter Beibehaltung eines objektorientierten Fokus in Ihren Entwürfen.

Die Lebensdauer einer Klasse in einer Java Virtual Machine (JVM) hat viele Ähnlichkeiten mit der Lebensdauer eines Objekts. So wie ein Objekt einen Status haben kann, der durch die Werte seiner Instanzvariablen dargestellt wird, kann eine Klasse einen Status haben, der durch die Werte ihrer Klassenvariablen dargestellt wird. So wie die JVM Instanzvariablen vor dem Ausführen des Initialisierungscodes auf Standardanfangswerte setzt, setzt die JVM Klassenvariablen vor dem Ausführen des Initialisierungscodes auf Standardanfangswerte. Und wie Objekte können Klassen mit Müll gesammelt werden, wenn sie von der laufenden Anwendung nicht mehr referenziert werden.

Trotzdem bestehen signifikante Unterschiede zwischen Klassen und Objekten. Der vielleicht wichtigste Unterschied ist die Art und Weise, wie Instanz- und Klassenmethoden aufgerufen werden: Instanzmethoden sind (größtenteils) dynamisch gebunden, Klassenmethoden sind jedoch statisch gebunden. (In drei Sonderfällen sind Instanzmethoden nicht dynamisch gebunden: Aufruf privater Instanzmethoden, Aufruf von initMethoden (Konstruktoren) und Aufrufe mit dem superSchlüsselwort. Weitere Informationen finden Sie unter Ressourcen.)

Ein weiterer Unterschied zwischen Klassen und Objekten ist der Grad des Versteckens von Daten, der von den privaten Zugriffsebenen gewährt wird. Wenn eine Instanzvariable als privat deklariert ist, können nur Instanzmethoden darauf zugreifen. Auf diese Weise können Sie die Integrität der Instanzdaten sicherstellen und Objekte threadsicher machen. Der Rest des Programms kann nicht direkt auf diese Instanzvariablen zugreifen, sondern muss die Instanzmethoden durchlaufen, um die Instanzvariablen zu bearbeiten. Um eine Klasse wie ein gut gestaltetes Objekt zu verhalten, können Sie Klassenvariablen privat machen und Klassenmethoden definieren, die sie bearbeiten. Trotzdem erhalten Sie auf diese Weise keine so gute Garantie für Thread-Sicherheit oder sogar Datenintegrität, da eine bestimmte Art von Code über ein spezielles Privileg verfügt, das ihnen direkten Zugriff auf private Klassenvariablen ermöglicht: Instanzmethoden,und sogar Initialisierer von Instanzvariablen können direkt auf diese privaten Klassenvariablen zugreifen.

Die statischen Felder und Methoden von Klassen sind zwar in vielerlei Hinsicht den Instanzfeldern und Methoden von Objekten ähnlich, weisen jedoch erhebliche Unterschiede auf, die sich auf die Art und Weise auswirken sollten, wie Sie sie in Entwürfen verwenden.

Klassen als Objekte behandeln

Beim Entwerfen von Java-Programmen werden Sie wahrscheinlich auf viele Situationen stoßen, in denen Sie das Bedürfnis nach einem Objekt verspüren, das sich in gewisser Weise wie eine Klasse verhält. Möglicherweise möchten Sie beispielsweise ein Objekt, dessen Lebensdauer mit der einer Klasse übereinstimmt. Oder Sie möchten ein Objekt, das sich wie eine Klasse auf eine einzelne Instanz in einem bestimmten Namensraum beschränkt.

In solchen Entwurfssituationen kann es verlockend sein, eine Klasse zu erstellen und wie ein Objekt zu verwenden, um Klassenvariablen zu definieren, sie privat zu machen und einige öffentliche Klassenmethoden zu definieren, die die Klassenvariablen manipulieren. Wie ein Objekt hat eine solche Klasse einen Zustand. Wie bei einem gut gestalteten Objekt sind die Variablen, die den Status definieren, privat, und die Außenwelt kann diesen Status nur durch Aufrufen der Klassenmethoden beeinflussen.

Leider gibt es einige Probleme mit diesem "Klasse-als-Objekt" -Ansatz. Da Klassenmethoden statisch gebunden sind, kann Ihre Klasse als Objekt nicht die Flexibilitätsvorteile von Polymorphismus und Upcasting nutzen. (Definitionen von Polymorphismus und dynamischer Bindung finden Sie im Artikel über Entwurfstechniken, Zusammensetzung versus Vererbung.) Polymorphismus wird durch dynamische Bindung ermöglicht und ist nützlich, aber Klassenmethoden sind nicht dynamisch gebunden. Wenn jemand Ihre Klasse als Objekt unterordnet, kann er Ihre Klassenmethoden nicht überschreiben , indem er gleichnamige Klassenmethoden deklariert. Sie können sich nur versteckenSie. Wenn eine dieser neu definierten Klassenmethoden aufgerufen wird, wählt die JVM die Methodenimplementierung aus, die zur Laufzeit nicht nach der Klasse eines Objekts, sondern nach dem Typ einer Variablen zur Kompilierungszeit ausgeführt werden soll.

Darüber hinaus ist die Thread-Sicherheit und Datenintegrität, die durch Ihre sorgfältige Implementierung der Klassenmethoden in Ihrer Klasse als Objekt erreicht wird, wie ein Haus aus Stroh. Ihre Thread-Sicherheit und Datenintegrität wird garantiert, solange jeder die Klassenmethoden verwendet, um den in den Klassenvariablen gespeicherten Status zu manipulieren. Ein unvorsichtiger oder ahnungsloser Programmierer könnte jedoch durch die Hinzufügung einer Instanzmethode, die direkt auf Ihre privaten Klassenvariablen zugreift, versehentlich schnauben und pusten und Ihre Thread-Sicherheit und Datenintegrität wegblasen.

Aus diesem Grund lautet meine Hauptrichtlinie für Klassenvariablen und Klassenmethoden:

Behandle Klassen nicht wie Objekte.

Mit anderen Worten, entwerfen Sie nicht mit statischen Feldern und Methoden einer Klasse, als wären sie die Instanzfelder und Methoden eines Objekts.

Wenn Sie einen Status und ein Verhalten wünschen, dessen Lebensdauer mit der einer Klasse übereinstimmt, vermeiden Sie die Verwendung von Klassenvariablen und Klassenmethoden, um ein Objekt zu simulieren. Erstellen Sie stattdessen ein tatsächliches Objekt und verwenden Sie eine Klassenvariable, um eine Referenz darauf zu speichern, und Klassenmethoden, um den Zugriff auf die Objektreferenz zu ermöglichen. Wenn Sie sicherstellen möchten, dass nur eine Instanz eines bestimmten Status und Verhaltens in einem einzelnen Namensraum vorhanden ist, versuchen Sie nicht, eine Klasse zu entwerfen, die ein Objekt simuliert. Erstellen Sie stattdessen einen Singleton - ein Objekt, das garantiert nur eine Instanz pro Namensraum hat.

Wofür sind Klassenmitglieder gut?

Meiner Meinung nach ist es die beste Denkweise beim Entwerfen von Java-Programmen, Objekte, Objekte, Objekte zu denken. Konzentrieren Sie sich auf das Entwerfen großartiger Objekte und stellen Sie sich Klassen in erster Linie als Blaupausen für Objekte vor - die Struktur, in der Sie die Instanzvariablen und Instanzmethoden definieren, aus denen Ihre gut gestalteten Objekte bestehen. Außerdem können Sie sich Klassen als einige spezielle Dienste vorstellen, die Objekte nicht oder nicht so elegant bereitstellen können. Stellen Sie sich Klassen vor als:

  • der richtige Ort, um "Dienstprogrammmethoden" zu definieren (Methoden, die Eingaben übernehmen und nur über übergebene Parameter und den Rückgabewert ausgeben)
  • eine Möglichkeit, den Zugriff auf Objekte und Daten zu steuern

Dienstprogrammmethoden

Methoden, die den Status eines Objekts oder einer Klasse, die ich als "Dienstprogrammmethoden" bezeichne, nicht manipulieren oder verwenden. Dienstprogrammmethoden geben lediglich einen Wert (oder Werte) zurück, der ausschließlich aus Daten berechnet wurde, die als Parameter an die Methode übergeben wurden. Sie sollten solche Methoden statisch machen und sie in die Klasse einordnen, die dem Dienst, den die Methode bereitstellt, am nächsten kommt.

Ein Beispiel für eine Dienstprogrammmethode ist die String copyValueOf(char[] data)Klassenmethode String. Diese Methode erzeugt ihre Ausgabe, einen Rückgabewert vom Typ String, ausschließlich aus ihrem Eingabeparameter, einem Array von chars. Da copyValueOf()der Status eines Objekts oder einer Klasse weder verwendet noch beeinflusst wird, handelt es sich um eine Dienstprogrammmethode. Und wie alle Dienstprogrammmethoden sein sollten, copyValueOf()handelt es sich um eine Klassenmethode.

Eine der Hauptmethoden für die Verwendung von Klassenmethoden sind Dienstprogrammmethoden - Methoden, die eine Ausgabe zurückgeben, die ausschließlich aus Eingabeparametern berechnet wird. Andere Verwendungen von Klassenmethoden umfassen Klassenvariablen.

Klassenvariablen zum Ausblenden von Daten

Eine der Grundregeln bei der objektorientierten Programmierung ist das Ausblenden von Daten, wodurch der Zugriff auf Daten eingeschränkt wird, um die Abhängigkeiten zwischen den Teilen eines Programms zu minimieren. Wenn ein bestimmtes Datenelement nur eingeschränkt zugänglich ist, können sich diese Daten ändern, ohne die Teile des Programms zu beschädigen, die nicht auf die Daten zugreifen können.

Wenn ein Objekt beispielsweise nur von Instanzen einer bestimmten Klasse benötigt wird, kann ein Verweis darauf in einer privaten Klassenvariablen gespeichert werden. Dies gibt allen Instanzen dieser Klasse einen praktischen Zugriff auf dieses Objekt - die Instanzen verwenden es nur direkt -, aber kein anderer Code irgendwo anders im Programm kann darauf zugreifen. In ähnlicher Weise können Sie Paketzugriff und geschützte Klassenvariablen verwenden, um die Sichtbarkeit von Objekten zu verringern, die von allen Mitgliedern eines Pakets und von Unterklassen gemeinsam genutzt werden müssen.

Öffentliche Klassenvariablen sind eine andere Geschichte. Wenn eine öffentliche Klassenvariable nicht endgültig ist, handelt es sich um eine globale Variable: dieses böse Konstrukt, das das Gegenteil von verstecktem Daten ist. Es gibt keine Entschuldigung für eine öffentliche Klassenvariable, es sei denn, sie ist endgültig.

Endgültige öffentliche Klassenvariablen, ob primitiver Typ oder Objektreferenz, dienen einem nützlichen Zweck. Variablen primitiver Typen oder Typen Stringsind einfach Konstanten, die im Allgemeinen dazu beitragen, Programme flexibler zu machen (einfacher zu ändern). Code, der Konstanten verwendet, ist einfacher zu ändern, da Sie den Konstantenwert an einer Stelle ändern können. Mit öffentlichen Variablen der letzten Klasse von Referenztypen können Sie globalen Zugriff auf Objekte gewähren, die global benötigt werden. Zum Beispiel System.in, System.outund System.errist public final class Variablen , die die globalen Zugriff auf die Standardeingabe und Fehlerströme geben.

Daher besteht die Hauptmethode zum Anzeigen von Klassenvariablen darin, die Zugänglichkeit von Variablen oder Objekten zu beschränken (dh zu verbergen). Wenn Sie Klassenmethoden mit Klassenvariablen kombinieren, können Sie noch kompliziertere Zugriffsrichtlinien implementieren.

Verwenden von Klassenmethoden mit Klassenvariablen

Klassenmethoden dienen nicht nur als Dienstprogrammmethoden, sondern können auch verwendet werden, um den Zugriff auf in Klassenvariablen gespeicherte Objekte zu steuern, insbesondere um zu steuern, wie die Objekte erstellt oder verwaltet werden. Zwei Beispiele für diese Art von Klassenmethode sind die setSecurityManager()und getSecurityManager()Methoden der Klasse System. Der Sicherheitsmanager für eine Anwendung ist ein Objekt, das wie die Standardeingabe-, Ausgabe- und Fehlerströme an vielen verschiedenen Stellen benötigt wird. Im Gegensatz zu den Standard-E / A-Stream-Objekten wird ein Verweis auf den Sicherheitsmanager jedoch nicht in einer öffentlichen endgültigen Klassenvariablen gespeichert. Das Security Manager-Objekt wird in einer privaten Klassenvariablen gespeichert, und die Methoden set und get implementieren eine spezielle Zugriffsrichtlinie für das Objekt.

Das Sicherheitsmodell von Java schränkt den Sicherheitsmanager besonders ein. Vor Java 2 (früher als JDK 1.2 bekannt) begann eine Anwendung ihr Leben ohne Sicherheitsmanager ( getSecurityManager()zurückgegeben null). Der erste Aufruf zur setSecurityManager()Einrichtung des Sicherheitsmanagers, der sich danach nicht mehr ändern durfte. Alle nachfolgenden Aufrufe von setSecurityManager()würden eine Sicherheitsausnahme ergeben. In Java 2 beginnt die Anwendung immer mit einem Sicherheitsmanager. Ähnlich wie in den vorherigen Versionen können Sie mit dieser setSecurityManager()Methode den Sicherheitsmanager jedoch höchstens einmal ändern .

Der Sicherheitsmanager bietet ein gutes Beispiel dafür, wie Klassenmethoden in Verbindung mit privaten Klassenvariablen verwendet werden können, um eine spezielle Zugriffsrichtlinie für Objekte zu implementieren, auf die von den Klassenvariablen verwiesen wird. Stellen Sie sich Klassenmethoden neben Dienstprogrammmethoden als Mittel vor, um spezielle Zugriffsrichtlinien für Objektreferenzen und Daten festzulegen, die in Klassenvariablen gespeichert sind.

Richtlinien

Der wichtigste Ratschlag in diesem Artikel lautet:

Behandle Klassen nicht wie Objekte.

Wenn Sie ein Objekt benötigen, erstellen Sie ein Objekt. Beschränken Sie die Verwendung von Klassenvariablen und -methoden auf das Definieren von Dienstprogrammmethoden und das Implementieren spezieller Arten von Zugriffsrichtlinien für Objekte und primitive Typen, die in Klassenvariablen gespeichert sind. Obwohl Java keine reine objektorientierte Sprache ist, ist es dennoch weitgehend objektorientiert, und Ihre Entwürfe sollten dies widerspiegeln. Denken Sie an Objekte.

Nächsten Monat

Nächster Monat Design - Techniken Artikel wird die letzte dieser Spalte sein. Ich werde bald anfangen, ein Buch zu schreiben, das auf dem Design Techniques-Material Flexible Java basiert , und dieses Material im Laufe der Zeit auf meiner Website platzieren. Bitte folgen Sie diesem Projekt und senden Sie mir Feedback. Nach ein oder zwei Monaten Pause bin ich wieder bei JavaWorld und SunWorld mit einer neuen Kolumne, die sich auf Jini konzentriert.

Eine Anfrage zur Teilnahme des Lesers

Ich ermutige Sie zu Kommentaren, Kritik, Vorschlägen, Flammen - allerlei Rückmeldungen - zu dem in dieser Kolumne vorgestellten Material. Wenn Sie mit etwas nicht einverstanden sind oder etwas hinzufügen möchten, lassen Sie es mich bitte wissen.

Sie können an einem Diskussionsforum teilnehmen, das diesem Material gewidmet ist, einen Kommentar über das Formular am Ende des Artikels eingeben oder mir direkt eine E-Mail über den in meiner Biografie unten angegebenen Link senden.

Bill Venners schreibt seit 12 Jahren professionell Software. Er ist im Silicon Valley ansässig und bietet Software-Beratungs- und Schulungsdienste unter dem Namen Artima Software Company an. Im Laufe der Jahre hat er Software für die Unterhaltungselektronik-, Bildungs-, Halbleiter- und Lebensversicherungsbranche entwickelt. Er hat in vielen Sprachen auf vielen Plattformen programmiert: Assemblersprache auf verschiedenen Mikroprozessoren, C unter Unix, C ++ unter Windows, Java im Web. Er ist Autor des Buches Inside the Java Virtual Machine, das von McGraw-Hill veröffentlicht wurde.