¿Java promueve una separación entre las definiciones de clase y las implementaciones, como es C ++?

10

Tengo una tarea asignada y necesito evaluar qué enfoque es mejor de acuerdo con GRASP "Variación protegida". Encontré una pregunta en Stack Overflow sobre la separación de los archivos de encabezado y código en C ++ .

Sin embargo, lo que quiero saber es por qué Java no sigue a C ++ al promover la separación entre las definiciones de clase y las implementaciones de clase. ¿Hay alguna ventaja con el método Java, sobre el método C ++?

Etienne Noël
fuente
Si desea preguntar "¿Por qué Java no usa archivos de encabezado?", Simplemente pregunte eso y elimine las cosas de "lo que es mejor", como ha visto, somos alérgicos a eso;) También busque, yo Estoy bastante seguro de que esto (o al menos preguntas relacionadas) se han mencionado anteriormente.
Uy, el enlace no funcionó. Reformularé, lo que quería saber básicamente, es las diferencias entre ambos y cuál tiende a ser más fácil de reutilizar el código o de extensibilidad.
Etienne Noël
1
C (y por extensión C ++) realmente no tuvo más remedio que separar los archivos de encabezado de los archivos de implementación, debido a la limitada tecnología de compilación de un paso en el momento en que se creó C.
Canal72
2
Java puede tener interfaces que pueden separar la definición de clase y la implementación de clase, si la clase en cuestión implementa la interfaz. Sin embargo, no es lo mismo que C ++.
FrustratedWithFormsDesigner
1
Además, los archivos de encabezado de C ++ exponen considerablemente más implementación de la que me gusta, a menos que use el lenguaje PIMPL. Es necesario enumerar todos los miembros de datos, incluso si es privateasí, para que la implementación conozca el tamaño y las privatefunciones de los miembros también.
David Thornley

Respuestas:

13

¿Cuántas líneas de código hay en el siguiente programa?

#include <iostream>

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

Probablemente respondió 7 (o 6 si no contó la línea en blanco, o 4 si no contó las llaves).

Sin embargo, su compilador ve algo muy diferente:

~$ cpp hello.cpp | wc
  18736   40822  437015

Sí, eso es 18.7 KLOC solo por un "¡Hola, mundo!" programa. El compilador de C ++ tiene que analizar todo eso. Esta es una razón importante por la que la compilación de C ++ tarda tanto en comparación con otros lenguajes, y por qué los lenguajes modernos evitan los archivos de encabezado.

Una mejor pregunta sería

¿ Por qué C ++ tiene archivos de encabezado?

C ++ fue diseñado para ser un superconjunto de C, por lo que tenía que mantener los archivos de encabezado para la compatibilidad con versiones anteriores.

Bien, entonces ¿por qué C tiene archivos de encabezado?

Debido a su primitivo modelo de compilación por separado. Los archivos de objetos generados por los compiladores de C no incluyen ningún tipo de información, por lo que para evitar errores de tipo, debe incluir esta información en su código fuente.

~$ 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

Agregar las declaraciones de tipo adecuadas corrige el error:

~$ 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

Tenga en cuenta que no hay #includes. Pero cuando usa una gran cantidad de funciones externas (que la mayoría de los programas usarán), declararlas manualmente se vuelve tedioso y propenso a errores. Es mucho más fácil usar archivos de encabezado.

¿Cómo pueden los idiomas modernos evitar los archivos de encabezado?

Mediante el uso de un formato de archivo de objeto diferente que incluye información de tipo. Por ejemplo, el formato de archivo Java * .class incluye "descriptores" que especifican los tipos de campos y parámetros de métodos.

Este no fue un invento nuevo. Anteriormente (1987), cuando Borland agregó "unidades" compiladas por separado a Turbo Pascal 4.0, eligió usar un nuevo *.TPUformato en lugar de Turbo C *.OBJpara eliminar la necesidad de archivos de encabezado.

dan04
fuente
Aunque curiosamente, estoy bastante seguro de que podría configurar Turbo Pascal para dar salida a OBJlos archivos en lugar de TPUs ...
una CVn
7

Java tiene interfaces para definir un contrato. Esto proporciona un mayor nivel de abstracción de lo que necesita la persona que llama y la implementación real. es decir, la persona que llama no necesita conocer la clase de implementación, solo necesita conocer el contrato que admite.

Supongamos que desea escribir un método que ralentice todas las claves / valores en un Mapa.

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

Este método puede llamar a entrySet () en una interfaz abstracta que se elimina de la clase que lo implementa. Puedes llamar a este método con.

printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());
Peter Lawrey
fuente
1
Bienvenido a los Programadores allí Peter, pensé que reconocía el nombre :-). Agregaré que algunas personas argumentarán que las clases base abstractas en Java también definen un contrato, pero ese es probablemente un argumento para un hilo separado.
Martijn Verburg el
Hola @MartijnVerburg, buena adición. Creo que las clases abstractas difuminan la distinción entre interfaces sin implementaciones y clases concretas. Los métodos de extensión desdibujarán aún más la distinción. Tiendo a preferir usar una interfaz si puedo, ya que son más simples.
Peter Lawrey
Sí, Java comenzará a seguir el camino de Scala de tener múltiples formas de definir un contrato público. No estoy seguro de que sea algo bueno o no todavía :-)
Martijn Verburg
-1 Esto también es posible en C ++, con un simple #define interface class.
Sjoerd el
@Sjoerd No sabía que los métodos C ++ ya no necesitan usar la virtualpalabra clave para obtener polimorfismo y esto no tiene penalización de rendimiento si solo usa uno o dos tipos concretos, como en Java. ¿Me puede indicar alguna documentación sobre cómo funciona esto en C ++?
Peter Lawrey el
5

Los encabezados existen, francamente, como un accidente histórico. Es un sistema increíblemente pobre, ningún otro idioma tiene algo tan terrible, y cualquiera que no tenga que lidiar con ellos debería alegrarse.

DeadMG
fuente
3

Los encabezados están ahí para permitir una compilación separada. Al incluir los encabezados, el compilador no necesita saber nada sobre la estructura binaria del código C ++ compilado, y puede dejar ese trabajo en un vinculador separado. Java no utiliza un vinculador separado con su compilador, y dado que los archivos .class están estrictamente definidos, el compilador puede leerlos para determinar todos sus miembros con todos sus tipos, sin necesidad de volver a declararlos en cada unidad de compilación.

Puede incluir toda la implementación en un encabezado C ++, pero hace que el compilador lo vuelva a compilar cada vez que está #incluido, lo que obliga al vinculador a ordenar y descartar las copias duplicadas.

Chuck Adams
fuente
0

Java promueve la separación de la definición de clase y la implementación, solo depende de dónde se mire.

Cuando eres el autor de una clase Java, puedes ver la definición de la clase y su implementación en un solo archivo. Esto simplifica el proceso de desarrollo, ya que solo tiene que ir a un lugar para mantener la clase, no tiene que cambiar entre dos archivos (.h y .cpp como lo haría en C ++). Sin embargo, cuando eres el consumidor de la clase solo tratas con la definición, a través de un archivo .class que está empaquetado en un .jar o un .class independiente.

C ++ le permite separar la definición y la implementación, pero es ad-hoc. Por ejemplo, no hay nada que le impida escribir el método en línea dentro del archivo de encabezado, y para las clases de plantilla esto es obligatorio. El archivo de encabezado también enumera las variables miembro, que son visibles para cualquiera que mire el archivo de encabezado, aunque sean detalles de implementación de la clase e irrelevantes para un consumidor.

Sean
fuente