Java ส่งเสริมการแยกระหว่างคำจำกัดความของคลาสและการใช้งานเช่นเดียวกับ C ++ หรือไม่?


10

ฉันมีการบ้านและฉันต้องประเมินว่าแนวทางใดดีกว่าตาม GRASP "รูปแบบที่ได้รับการป้องกัน" ผมพบว่าคำถามในกองมากเกินเกี่ยวกับการแยกส่วนหัวและรหัสไฟล์ใน C ++

อย่างไรก็ตามสิ่งที่ฉันต้องการรู้ว่าทำไม Java ไม่ปฏิบัติตาม C ++ ในการส่งเสริมการแยกระหว่างคำจำกัดความของชั้นเรียนและการใช้งานระดับ มีข้อได้เปรียบกับวิธีการของจาวามากกว่าวิธี C ++ หรือไม่?


หากคุณต้องการถามว่า "ทำไม Java ไม่ใช้ไฟล์ส่วนหัว" ให้ถามคำถามนั้นและทำสิ่งที่ "ดีกว่า" อย่างที่คุณเห็นเราแพ้ไป;) นอกจากนี้ฉันก็ค้นหาด้วย ค่อนข้างแน่ใจว่าคำถามนี้ (หรืออย่างน้อยคำถามที่เกี่ยวข้องอย่างใกล้ชิด) ได้ถูกนำขึ้นมาก่อน

อ๊ะลิงก์ใช้งานไม่ได้ ฉันจะปรับโครงสร้างสิ่งที่ฉันต้องการรู้โดยทั่วไปคือความแตกต่างระหว่างทั้งสองและสิ่งที่หนึ่งมีแนวโน้มที่จะง่ายต่อการนำรหัสมาใช้ใหม่หรือเพิ่มความสามารถในการขยาย
เอเตียนโนเอล

1
C (และโดยส่วนขยาย C ++) ไม่มีทางเลือกนอกจากการแยกไฟล์ส่วนหัวออกจากไฟล์การนำไปใช้งานเนื่องจากเทคโนโลยีคอมไพเลอร์หนึ่งรอบที่ จำกัด ในเวลาที่ C ถูกสร้างขึ้น
Channel72

2
Java สามารถมีอินเตอร์เฟสซึ่งสามารถแยกนิยามคลาสและการนำคลาสไปใช้งานได้หากคลาสที่มีปัญหานั้นใช้อินเตอร์เฟส ไม่เหมือนกับ C ++ เลยทีเดียว
FrustratedWithFormsDesigner

1
นอกจากนี้ไฟล์ส่วนหัว C ++ ยังมีการใช้งานมากกว่าที่ฉันต้องการยกเว้นว่าคุณใช้ PIMPL idiom จำเป็นต้องแสดงรายการข้อมูลสมาชิกทั้งหมดแม้ว่าprivateการนำไปใช้จะทราบขนาดและprivateฟังก์ชันสมาชิกก็เช่นกัน
David Thornley

คำตอบ:


13

มีโค้ดกี่บรรทัดในโปรแกรมต่อไปนี้?

#include <iostream>

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

คุณอาจตอบ 7 (หรือ 6 ถ้าคุณไม่นับบรรทัดว่างหรือ 4 ถ้าคุณไม่นับวงเล็บ)

คอมไพเลอร์ของคุณอย่างไรก็ตามเห็นสิ่งที่แตกต่างกันมาก:

~$ cpp hello.cpp | wc
  18736   40822  437015

ใช่นั่นคือ 18.7 KLOC เพียงเพื่อ "สวัสดีโลก!" โปรแกรม. คอมไพเลอร์ C ++ ต้องแยกวิเคราะห์ทั้งหมด นี่คือเหตุผลสำคัญว่าทำไมการรวบรวม C ++ ใช้เวลานานมากเมื่อเทียบกับภาษาอื่นและทำไมภาษาสมัยใหม่จึงหลีกเลี่ยงไฟล์ส่วนหัว

คำถามที่ดีกว่าก็คือ

ทำไมไม่ C ++ มีไฟล์ส่วนหัว?

C ++ ได้รับการออกแบบให้เป็นชุดของ C ดังนั้นจึงต้องเก็บไฟล์ส่วนหัวไว้เพื่อใช้งานร่วมกันได้ย้อนหลัง

ตกลงเหตุใด C จึงมีไฟล์ส่วนหัว

เพราะรูปแบบการรวบรวมแยกดั้งเดิมของมัน ไฟล์อ็อบเจ็กต์ที่สร้างโดยคอมไพเลอร์ C จะไม่รวมข้อมูลประเภทใด ๆ ดังนั้นเพื่อป้องกันข้อผิดพลาดประเภทคุณจำเป็นต้องรวมข้อมูลนี้ในซอร์สโค้ดของคุณ

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

การเพิ่มการประกาศประเภทที่เหมาะสมแก้ไขข้อบกพร่อง:

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

แจ้งให้ทราบว่าไม่มี#includes แต่เมื่อคุณใช้ฟังก์ชั่นภายนอกจำนวนมาก (ซึ่งโปรแกรมส่วนใหญ่จะใช้) การประกาศด้วยตนเองจะทำให้เกิดความเบื่อหน่ายและเกิดข้อผิดพลาดได้ง่าย มันง่ายกว่าที่จะใช้ไฟล์ส่วนหัว

ภาษาสมัยใหม่สามารถหลีกเลี่ยงไฟล์ส่วนหัวได้อย่างไร?

โดยใช้รูปแบบไฟล์วัตถุอื่นที่มีข้อมูลประเภท ตัวอย่างเช่นรูปแบบไฟล์ Java * .classมี "descriptors" ซึ่งระบุประเภทของฟิลด์และพารามิเตอร์เมธอด

นี่ไม่ใช่สิ่งประดิษฐ์ใหม่ ก่อนหน้านี้ (1987) เมื่อ Borland เพิ่ม "units" ที่คอมไพล์แล้วลงใน Turbo Pascal 4.0 มันเลือกที่จะใช้*.TPUรูปแบบใหม่แทน Turbo C's *.OBJเพื่อลบความต้องการไฟล์ส่วนหัว


แม้ว่าที่น่าสนใจผมค่อนข้างแน่ใจว่าคุณสามารถตั้งเทอร์โบปาสคาลกับการส่งออกOBJไฟล์มากกว่าTPUs ...
CVn

7

Java มีอินเตอร์เฟสเพื่อกำหนดสัญญา สิ่งนี้ให้ระดับที่สูงขึ้นของนามธรรมจากสิ่งที่ผู้โทรต้องการและการใช้งานจริง เช่นผู้โทรไม่จำเป็นต้องรู้ชั้นเรียนการใช้งานเพียงแค่ต้องรู้สัญญาที่รองรับ

สมมติว่าคุณต้องการเขียนวิธีการที่ทำให้คีย์ / ค่าทั้งหมดช้าลงในแผนที่

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

วิธีนี้สามารถเรียก entrySet () บนอินเทอร์เฟซแบบนามธรรมซึ่งถูกลบออกจากคลาสที่ใช้งาน คุณสามารถเรียกวิธีนี้ด้วย

printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());

1
ยินดีต้อนรับสู่โปรแกรมเมอร์ที่นั่น Peter - ฉันคิดว่าฉันจำชื่อ :-) ฉันจะเพิ่มว่าบางคนจะยืนยันว่าชั้นฐานนามธรรมใน Java ยังกำหนดสัญญา - แต่นั่นอาจเป็นอาร์กิวเมนต์สำหรับเธรดแยกต่างหาก
Martijn Verburg

สวัสดี @MartijnVerburg นอกจากนี้ Nice ฉันคิดว่าคลาสนามธรรมทำให้ความแตกต่างระหว่างอินเทอร์เฟซที่ไม่มีการใช้งานและคลาสที่เป็นรูปธรรม วิธีการขยายจะเบลอความแตกต่างยิ่งขึ้น ฉันมักจะชอบที่จะใช้อินเทอร์เฟซถ้าทำได้เพราะมันง่ายกว่า
Peter Lawrey

จ้ะจาวากำลังจะเริ่มมุ่งหน้าไปตามเส้นทางสกาล่าของการมีหลายวิธีในการกำหนดสัญญาสาธารณะ - ฉันไม่แน่ใจว่าเป็นสิ่งที่ดีหรือยัง :-)
Martijn Verburg

-1 นี่เป็นไปได้ใน C ++ เช่นกันด้วย#define interface classวิ
Sjoerd

@Sererd ฉันไม่ทราบวิธี C ++ ที่ไม่จำเป็นต้องใช้virtualคำหลักเพื่อรับความแตกต่างอีกต่อไปและสิ่งนี้ไม่มีการลงโทษประสิทธิภาพถ้าคุณใช้เพียงหนึ่งหรือสองประเภทคอนกรีตเช่นพวกเขาอยู่ใน Java คุณช่วยชี้ให้ฉันดูเอกสารเกี่ยวกับวิธีการทำงานใน C ++ ได้หรือไม่?
Peter Lawrey

5

ส่วนหัวมีอยู่ค่อนข้างตรงไปตรงมาเป็นอุบัติเหตุทางประวัติศาสตร์ มันเป็นระบบที่แย่อย่างไม่น่าเชื่อไม่มีภาษาอื่นมีอะไรที่แย่มากและทุกคนที่ไม่ต้องจัดการกับพวกเขาควรชื่นชมยินดี


3

ส่วนหัวอยู่ที่นั่นเพื่อเปิดใช้งานการรวบรวมแยก โดย #including ส่วนหัวคอมไพเลอร์ไม่จำเป็นต้องรู้อะไรเกี่ยวกับโครงสร้างไบนารีของรหัส C ++ ที่คอมไพล์และสามารถปล่อยให้งานนั้นไปยังตัวเชื่อมโยงแยกต่างหาก Java ไม่ได้ใช้ตัวเชื่อมโยงแยกกับคอมไพเลอร์และเนื่องจากไฟล์. class ถูกกำหนดอย่างเคร่งครัดคอมไพเลอร์สามารถอ่านได้เพื่อกำหนดสมาชิกทั้งหมดด้วยประเภททั้งหมดโดยไม่จำเป็นต้องประกาศใหม่ในแต่ละหน่วยการรวบรวม

คุณสามารถรวมการใช้งานทั้งหมดในส่วนหัว C ++ ได้ แต่จะทำให้คอมไพเลอร์คอมไพล์ใหม่ทุกครั้งที่มี #included บังคับให้ตัวเชื่อมโยงเรียงลำดับแล้วละทิ้งสำเนาที่ซ้ำกัน


0

Java ส่งเสริมการแยกคำจำกัดความของคลาสและการนำไปใช้งานขึ้นอยู่กับว่าคุณกำลังมองหาที่ใด

เมื่อคุณเป็นผู้เขียนคลาส Java คุณจะเห็นนิยามของคลาสรวมถึงการนำไปใช้ในหนึ่งไฟล์ สิ่งนี้ช่วยให้กระบวนการพัฒนาง่ายขึ้นเนื่องจากคุณเพียงแค่ไปที่เดียวเพื่อรักษาคลาสคุณไม่ต้องสลับไปมาระหว่างสองไฟล์ (.h และ. cpp ตามที่คุณต้องการใน C ++) อย่างไรก็ตามเมื่อคุณเป็นผู้บริโภคในชั้นเรียนคุณจะจัดการกับคำจำกัดความเท่านั้นโดยใช้ไฟล์. class ที่บรรจุใน. jar หรือ. class แบบสแตนด์อโลน

C ++ ช่วยให้คุณสามารถแยกคำจำกัดความและการใช้งาน แต่มันเป็นเฉพาะกิจ ตัวอย่างเช่นไม่มีอะไรจะหยุดคุณเขียนวิธีการแบบอินไลน์ภายในไฟล์ส่วนหัวและสำหรับชั้นเรียนแม่แบบนี้เป็นสิ่งจำเป็น ไฟล์ส่วนหัวยังแสดงรายการตัวแปรสมาชิกใด ๆ ซึ่งจะปรากฏแก่ทุกคนที่ดูไฟล์ส่วนหัวแม้ว่าพวกเขาจะมีรายละเอียดการใช้งานของชั้นเรียนและไม่เกี่ยวข้องกับผู้บริโภค

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.