Fördert Java eine Trennung zwischen Klassendefinitionen und Implementierungen, ebenso wie C ++?

10

Ich habe eine Hausaufgabe und muss bewerten, welcher Ansatz gemäß GRASP "Protected Variation" besser ist. Ich habe bei Stack Overflow eine Frage zur Trennung von Header- und Codedateien in C ++ gefunden .

Was ich jedoch wissen möchte, warum Java C ++ nicht folgt, um die Trennung zwischen Klassendefinitionen und Klassenimplementierungen zu fördern. Gibt es Vorteile bei der Java-Methode gegenüber der C ++ - Methode?

Etienne Noël
quelle
Wenn Sie fragen möchten "Warum verwendet Java keine Header-Dateien?", Dann fragen Sie einfach danach und beseitigen Sie das "was ist besser" Zeug - wie Sie gesehen haben, sind wir allergisch dagegen;) Suchen Sie auch, ich Ich bin mir ziemlich sicher, dass dies (oder zumindest eng verwandte Fragen) schon einmal angesprochen wurden.
Ups, der Link hat nicht funktioniert. Ich werde neu formulieren, was ich im Grunde wissen wollte, sind die Unterschiede zwischen beiden und welche sind in der Regel einfacher, den Code wiederzuverwenden oder für die Erweiterbarkeit.
Etienne Noël
1
C (und damit auch C ++) hatte aufgrund der zum Zeitpunkt der Erstellung von C eingeschränkten One-Pass-Compilertechnologie keine andere Wahl, als die Header-Dateien von den Implementierungsdateien zu trennen.
Channel72
2
Java kann Schnittstellen haben, die Klassendefinition und Klassenimplementierung trennen können, wenn die betreffende Klasse die Schnittstelle implementiert. Nicht ganz das gleiche wie C ++.
FrustratedWithFormsDesigner
1
Außerdem bieten C ++ - Headerdateien erheblich mehr Implementierungen als ich möchte, es sei denn, Sie verwenden die PIMPL-Sprache. Es ist notwendig, alle Datenelemente aufzulisten, auch wenn private, damit die Implementierung die Größe kennt und die privateElementfunktionen auch funktionieren.
David Thornley

Antworten:

13

Wie viele Codezeilen enthält das folgende Programm?

#include <iostream>

int main()
{
   std::cout << "Hello, world!\n";
   return 0;
}

Sie haben wahrscheinlich mit 7 geantwortet (oder mit 6, wenn Sie die leere Zeile nicht gezählt haben, oder mit 4, wenn Sie die geschweiften Klammern nicht gezählt haben).

Ihr Compiler sieht jedoch etwas ganz anderes:

~$ cpp hello.cpp | wc
  18736   40822  437015

Ja, das sind 18,7 KLOC nur für eine "Hallo Welt!" Programm. Der C ++ - Compiler muss das alles analysieren . Dies ist ein Hauptgrund, warum die C ++ - Kompilierung im Vergleich zu anderen Sprachen so lange dauert und warum moderne Sprachen Header-Dateien meiden.

Eine bessere Frage wäre

Warum hat C ++ Header-Dateien?

C ++ wurde als Obermenge von C entwickelt, daher mussten Header-Dateien aus Gründen der Abwärtskompatibilität aufbewahrt werden.

OK, warum hat C Header-Dateien?

Wegen seines primitiven separaten Kompilierungsmodells. Die von C-Compilern generierten Objektdateien enthalten keine Typinformationen. Um Typfehler zu vermeiden, müssen Sie diese Informationen in Ihren Quellcode aufnehmen.

~$ cat sqrtdemo.c 
int main(void)
{
    /* implicit declaration int sqrt(int) */
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm -Dsqrt= sqrtdemo.c
sqrtdemo.c: In function main’:
sqrtdemo.c:5:5: warning: implicit declaration of function printf [-Wimplicit-function-declaration]
sqrtdemo.c:5:5: warning: incompatible implicit declaration of built-in function printf [enabled by default]
~$ ./a.out 
2.000000

Das Hinzufügen der richtigen Typdeklarationen behebt den Fehler:

~$ cat sqrtdemo.c 
#undef printf
#undef sqrt

int printf(const char*, ...);
double sqrt(double);

int main(void)
{
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm sqrtdemo.c
~$ ./a.out 
1.414214

Beachten Sie, dass es keine #includes gibt. Wenn Sie jedoch eine große Anzahl externer Funktionen verwenden (was die meisten Programme tun), wird das manuelle Deklarieren dieser Funktionen mühsam und fehleranfällig. Es ist viel einfacher, Header-Dateien zu verwenden.

Wie können moderne Sprachen Header-Dateien vermeiden?

Durch Verwendung eines anderen Objektdateiformats, das Typinformationen enthält. Das Java * .class-Dateiformat enthält beispielsweise "Deskriptoren", die die Feldtypen und Methodenparameter angeben.

Dies war keine neue Erfindung. Früher (1987), als Borland Turbo Pascal 4.0 separat kompilierte "Einheiten" hinzufügte, entschied es sich, ein neues *.TPUFormat anstelle von Turbo C zu *.OBJverwenden, um die Notwendigkeit von Header-Dateien zu beseitigen.

dan04
quelle
Obwohl interessant, bin ich mir ziemlich sicher, dass Sie Turbo Pascal so einstellen könnten, dass es OBJDateien ausgibt , anstatt TPUs ...
ein CVn
7

Java verfügt über Schnittstellen zum Definieren eines Vertrags. Dies bietet eine höhere Abstraktionsebene von den Anforderungen des Aufrufers und der tatsächlichen Implementierung. Das heißt, der Aufrufer muss die implementierende Klasse nicht kennen, er muss nur den Vertrag kennen, den er unterstützt.

Angenommen, Sie möchten eine Methode schreiben, die alle Schlüssel / Werte in einer Map verlangsamt.

public static <K,V> void printMap(Map<K,V> map) {
    for(Entry<K,V> entry: map.entrySet())
        System.out.println(entry);
}

Diese Methode kann entrySet () auf einer abstrakten Schnittstelle aufrufen, die aus der Klasse entfernt wird, die sie implementiert. Sie können diese Methode mit aufrufen.

printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());
Peter Lawrey
quelle
1
Willkommen bei den Programmierern dort Peter - dachte ich hätte den Namen erkannt :-). Ich werde hinzufügen, dass einige Leute argumentieren werden, dass abstrakte Basisklassen in Java auch einen Vertrag definieren - aber das ist wahrscheinlich ein Argument für einen separaten Thread.
Martijn Verburg
Hallo @MartijnVerburg, nette Ergänzung. Ich denke, abstrakte Klassen verwischen die Unterscheidung zwischen Schnittstellen ohne Implementierungen und konkreten Klassen. Erweiterungsmethoden verwischen die Unterscheidung noch weiter. Ich bevorzuge eher eine Schnittstelle, wenn ich kann, da sie einfacher ist.
Peter Lawrey
Ja, Java wird den Scala-Weg beschreiten, mehrere Möglichkeiten zu haben, einen öffentlichen Auftrag zu definieren - ich bin mir nicht sicher, ob das eine gute Sache ist oder noch nicht :-)
Martijn Verburg
-1 Dies ist auch in C ++ mit einem einfachen möglich #define interface class.
Sjoerd
@Sjoerd Ich wusste nicht, dass C ++ - Methoden das virtualSchlüsselwort nicht mehr verwenden müssen, um Polymorphismus zu erhalten, und dies hat keine Leistungseinbußen zur Folge, wenn Sie nur einen oder zwei konkrete Typen verwenden, wie sie in Java sind. Können Sie mich auf eine Dokumentation verweisen, wie dies in C ++ funktioniert?
Peter Lawrey
5

Überschriften existieren, ganz offen gesagt, als historischer Unfall. Es ist ein unglaublich schlechtes System, keine andere Sprache hat etwas so Schreckliches, und jeder, der sich nicht mit ihnen befassen muss, sollte sich freuen.

DeadMG
quelle
3

Überschriften ermöglichen eine separate Kompilierung. Durch das Einschließen der Header muss der Compiler nichts über die Binärstruktur des kompilierten C ++ - Codes wissen und kann diesen Job einem separaten Linker überlassen. Java verwendet mit seinem Compiler keinen separaten Linker. Da .class-Dateien streng definiert sind, kann der Compiler sie lesen, um alle Mitglieder mit all ihren Typen zu bestimmen, ohne sie in jeder Kompilierungseinheit erneut deklarieren zu müssen.

Sie können die gesamte Implementierung in einen C ++ - Header aufnehmen, der Compiler kompiliert sie jedoch jedes Mal neu, wenn sie # eingeschlossen ist, und zwingt den Linker, die doppelten Kopien zu sortieren und zu verwerfen.

Chuck Adams
quelle
0

Java fördert die Trennung von Klassendefinition und -implementierung. Es hängt nur davon ab, von wo aus Sie suchen.

Wenn Sie der Autor einer Java-Klasse sind, sehen Sie die Definition der Klasse sowie deren Implementierung in einer Datei. Dies vereinfacht den Entwicklungsprozess, da Sie nur an einen Ort gehen müssen, um die Klasse zu verwalten. Sie müssen nicht zwischen zwei Dateien wechseln (.h und .cpp wie in C ++). Wenn Sie jedoch der Konsument der Klasse sind, behandeln Sie die Definition nur über eine .class-Datei, die entweder in einer .jar- oder einer eigenständigen .class-Datei gepackt ist

Mit C ++ können Sie die Definition und Implementierung trennen, dies ist jedoch ad-hoc. Zum Beispiel hindert Sie nichts daran, die Methode inline in die Header-Datei zu schreiben, und für Vorlagenklassen ist dies obligatorisch. In der Header-Datei werden auch alle Mitgliedsvariablen aufgelistet, die für jeden sichtbar sind, der sich die Header-Datei ansieht, obwohl sie ein Implementierungsdetail der Klasse sind und für einen Verbraucher irrelevant sind.

Sean
quelle