ทำไม Java ไม่ใช้การห่อหุ้มด้วยคลาสบางคลาส?


25

คำถามของฉันเกี่ยวข้องกับSystem.inและSystem.outชั้นเรียน (อาจมีคนอื่น ๆ เช่นที่อยู่ในไลบรารีมาตรฐาน) ทำไมถึงเป็นอย่างนั้น? นั่นเป็นวิธีปฏิบัติที่ไม่ดีใน OOP ใช่หรือไม่ ไม่ควรใช้เช่น: System.getIn()และSystem.getOut()? ฉันมีคำถามนี้มาตลอดและหวังว่าฉันจะหาคำตอบที่ดีได้ที่นี่

คำตอบ:


36

คำนิยามสำหรับinและoutฟิลด์ภายในSystemคลาสคือ:

public final static PrintStream out;
public final static InputStream in;

นี่คือค่าคงที่ พวกมันก็เป็นวัตถุด้วยเหมือนกัน แต่มันก็เป็นค่าคงที่ มันเป็นอย่างมากเหมือนกับคลาสคณิตศาสตร์:

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;

หรือในคลาสบูลีน:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

หรือในคลาส Color:

public final static Color white     = new Color(255, 255, 255);
public final static Color black     = new Color(0, 0, 0);
public final static Color red       = new Color(255, 0, 0);

เมื่อเข้าถึงค่าคงที่สาธารณะที่ไม่เปลี่ยนแปลงจะไม่มีข้อได้เปรียบที่สำคัญในการสรุปแคปเจอร์ - แนวคิดหรือประสิทธิภาพตาม มันอยู่ที่นั่น มันจะไม่เปลี่ยนแปลง

ไม่มีความแตกต่างระหว่างจริงเป็นและColor.whiteSystem.out


ฉันไม่ได้เห็นอย่างนั้นมันเป็นวัตถุคงที่ นั่นเป็นคำอธิบายที่ดีทีเดียว
Clawdidr

1
@Clawdidr ใน Java วันนี้อาจมีการพิจารณาใช้enumเพื่อเก็บไว้ ... แม้ว่าจะenumใหม่กับ Java 1.5 (ไม่ใช่ตัวเลือกใน 1.0 วัน)

เพิ่งออกมาจากความอยากรู้ (ไม่ใช่นักพัฒนา Java เอง): มีความแตกต่างระหว่างfinal staticและstatic final? ถ้าเป็นเช่นนั้นมันคืออะไร?
Marjan Venema

1
@MarjanVenema ไม่แตกต่างกันเพียงแค่ต้องการคำสั่งของตัวดัดแปลง การตรวจสอบการเปลี่ยนแปลงตัวตรวจสอบแสดงให้เห็นถึงการสั่งซื้อที่ต้องการ เห็นได้ชัดว่าใครก็ตามที่เขียนไฟล์ต้นฉบับสองไฟล์ในรุ่น jdk ที่ฉันไม่เห็นด้วยกับการสั่งซื้อ การประชุมเพียงมัน

:) และขอบคุณ Michael ที่สละเวลาในการตอบและลิงก์
Marjan Venema

5

เหตุผลที่แท้จริงคือสิ่งนี้เป็นปัญหาดั้งเดิม System.in,out,errคงเป็นส่วนหนึ่งของ Java 1.0 ... และอาจจะกลับมาเป็นจำนวนมากต่อไป เมื่อถึงเวลาที่ชัดเจนว่าการออกแบบมีปัญหามันก็สายเกินไปที่จะแก้ไข ที่ดีที่สุดของพวกเขาสามารถทำคือการเพิ่มSystem.setIn,setOut,setErrวิธีการใน Java 1.1 แล้วจัดการกับปัญหาข้อมูลทางภาษาที่1

สิ่งนี้คล้ายกับปัญหาที่ว่าทำไมมีSystem.arraycopyวิธีการแบบคงที่ที่ชื่อละเมิดข้อกำหนดการตั้งชื่อ Java


ไม่ว่าจะเป็น "การออกแบบที่ไม่ดี" หรือไม่ฉันคิดว่ามันเป็น มีสถานการณ์ที่การจัดการที่ไม่ใช่ OO ปัจจุบันเป็นปัญหาร้ายแรง (คิดว่า ... คุณจะรันโปรแกรมจาวาหนึ่งโปรแกรมภายในโปรแกรมอื่นได้อย่างไรเมื่อข้อกำหนดการสตรีม "มาตรฐาน IO" ของพวกเขาขัดแย้งกันคิดว่า ... รหัสการทดสอบหน่วยที่ส่งผลให้เกิดการเปลี่ยนกระแสข้อมูล)

อย่างไรก็ตามฉันสามารถเกี่ยวข้องกับการโต้แย้งว่าวิธีการทำสิ่งต่างในปัจจุบันสะดวกกว่าในหลายกรณี


1 - เป็นที่น่าสนใจที่จะทราบว่าSystem.in,out,errตัวแปรได้รับการกล่าวถึงเป็นพิเศษใน JLS ว่ามี "ความหมายพิเศษ" JLS บอกว่าถ้าคุณเปลี่ยนค่าของfinalเขตข้อมูลพฤติกรรมจะไม่ได้กำหนด ... ยกเว้นในกรณีของเขตข้อมูลเหล่านี้


2

ฉันเชื่อว่าอ็อบเจกต์ out นั้นไม่เปลี่ยนรูปแบบซึ่งทำให้ปลอดภัย

คลาสจำนวนมากใน JDK ไม่เคารพหลักการออกแบบเชิงวัตถุที่ดีที่สุด เหตุผลข้อหนึ่งสำหรับเรื่องนี้ก็คือความจริงที่ว่าพวกเขาเขียนเมื่อเกือบ 20 ปีก่อนเมื่อการวางแนววัตถุเป็นเพียงกระบวนทัศน์หลักและโปรแกรมเมอร์จำนวนมากก็ไม่คุ้นเคยกับพวกเขาเหมือนตอนนี้ ตัวอย่างที่ดีของการออกแบบ API ที่ไม่ดีคือ Date & Time API ซึ่งใช้เวลา 19 ปีในการเปลี่ยน ...


3
ฉันจะทำให้คำสั่งนั้นแข็งแกร่งยิ่งขึ้นตัวแปรสาธารณะขั้นสุดท้าย (คงที่หรือไม่ก็ตาม) มีความเหมือนกับ "ปลอดภัย" ในฐานะผู้ทะเยอทะยานและฉันจะชอบสิ่งที่ไม่สามารถเปลี่ยนแปลงได้มากกว่าคู่ setter / getter Setters เป็นเพียงความคิดที่ไม่ดีอยู่เสมอ (ไม่เปลี่ยนรูปเป็นสิ่งที่ดี - และถ้ามันไม่สามารถเปลี่ยนรูปแบบวิธีที่ "ตั้งค่า" มันอาจสมควรได้รับตรรกะทางธุรกิจเล็กน้อยเช่นกัน), ถ้าคุณต้องการผู้ทะเยอทะยาน ขั้นสุดท้าย แต่การทำไม่เป็นที่ต้องการ - อย่าขอคลาสจากข้อมูลขอให้ชั้นเรียนทำบางสิ่งกับข้อมูล
Bill K

@ บิลล์: ใช่เรียกอีกอย่างว่าLaw of Demeter (แม้ว่าจะไม่ใช่กฎหมาย แต่เป็นแนวทาง)
sleske

2

คำตอบนี้ยอดเยี่ยมและเป็นความจริง

ฉันต้องการเพิ่มว่าในบางกรณีการประนีประนอมถูกสร้างขึ้นเพื่อประโยชน์ในการใช้งาน

วัตถุประเภท String สามารถยกตัวอย่างได้โดยไม่ต้องใหม่เหตุการณ์เมื่อ String ไม่ใช่ดั้งเดิม:

String s = "Hello";

สตริงที่ไม่ใช่แบบดั้งเดิมควรมีอินสแตนซ์นี้:

String s = new String("Hello");          // this also works 

แต่คอมไพเลอร์อนุญาตให้ใช้ตัวเลือก OO ที่สั้นลงและน้อยลงเนื่องจาก String เป็นคลาสที่ใช้กันอย่างแพร่หลายมากที่สุดใน API

นอกจากนี้ยังสามารถเริ่มต้นอาร์เรย์ในแบบที่ไม่ใช่ OO

int i[] = {1,2,3};

แปลกพอวัตถุอย่างใดอย่างหนึ่งตัวอย่างของชั้นเรียนหรืออาร์เรย์ อาร์เรย์ที่มีความหมายเป็นประเภทที่แยกจากกันโดยสิ้นเชิง

อาร์เรย์มีlengthเขตข้อมูลสาธารณะที่ไม่คงที่ นอกจากนี้ยังไม่มีเอกสารเกี่ยวกับอาร์เรย์ของคลาสเป็นตัวอย่างของ (เพื่อไม่ให้สับสนกับคลาส Arrays หรือ java.reflect.Array)

int a = myArray.length;    // not length()

6
สิ่งนั้นแตกต่างกัน - new String("Hello")จะสร้างวัตถุสตริงใหม่เสมอ ในขณะที่String s = "Hello";จะใช้วัตถุ interned เปรียบเทียบ: "Hello" == "Hello"อาจเป็นจริงในขณะที่new String("Hello") == new String("Hello")เป็นเท็จเสมอ new String("Hello")มีเวลารวบรวมสิ่งที่เกิดขึ้นมายากลการเพิ่มประสิทธิภาพในครั้งแรกว่าเป็นไปไม่ได้ก็คือ ดูen.wikipedia.org/wiki/String_interning

@MichaelT ฉันเพิ่มความพิศวงในอาร์เรย์เพื่อเพิ่มความสมบูรณ์
Tulains Córdova

2
@Tarik ฉันถูกคัดค้าน "สตริงที่ไม่ใช่แบบดั้งเดิมควรจะ instanciated เช่นนี้: String s = new String("Hello");" ซึ่งไม่ถูกต้อง ตัวเลือกที่สั้นกว่า ( String s = "Hello";) ถูกต้องมากขึ้นเนื่องจากการฝึกงานในสายอักขระ

2
ด้วยStringไม่มีตัวเลือก "ถูกต้องมากขึ้น" ทั้งสองถูกต้อง แม้ว่าอย่างที่ MichaelT พูด แต่ตัวที่สั้นกว่าก็เป็นที่ต้องการเนื่องจากการใช้งานแบบ String
Andres F.

1
@AndresF ฉันเปลี่ยน "ถูกต้องน้อยลง" เป็น "less OO"
Tulains Córdova
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.