เมื่อเร็วๆ นี้ ฉันพบปัญหาในกระบวนการเรียนรู้อินเทอร์เฟซ PHP5 หนังสือเล่มนี้บอกว่ามันเป็นวิธีการใช้การสืบทอดหลายรายการ แต่ฉันยังไม่รู้วิธีใช้งาน มีข้อมูลน้อยมากเกี่ยวกับอินเทอร์เฟซ PHP บนอินเทอร์เน็ต ดังนั้นฉันจึงตรวจสอบบน Java โดยพื้นฐานแล้วพวกมันก็เหมือนกัน หลังจากอ่านบทความ "การชี้แจง Java (อินเทอร์เฟซและการสืบทอด)" ฉันก็รู้ทันทีว่าฉันเข้าใจผิดตั้งแต่ต้น สิ่งที่เรียกว่าการสืบทอดหลายรายการหมายถึงอินเทอร์เฟซที่สืบทอดคลาส ไม่ใช่คลาสที่สืบทอดอินเทอร์เฟซ
บทความกล่าวถึงนามธรรมของ OO เหมือนประโยคในบทความ "นามธรรมคือการลบส่วนของภาพ" ชัดเจนมาก เมื่อคิดถึงสิ่งที่เป็นนามธรรมฉันก็คิดเสมอว่ามันยากที่จะเข้าใจ แต่นามธรรม ตอนนี้มันเป็นเรื่องง่ายที่จะเข้าใจ ใช่ นี่คือสิ่งที่อินเทอร์เฟซและคลาสนามธรรมทำ
มีมุมมองอื่น ๆ อีกมากมายในบทความนี้ที่เป็นประโยชน์ต่อฉันมากดังรายการด้านล่าง:
ฉันคิดว่าแก่นแท้ของ OO คือนามธรรมของวัตถุ
หน้าที่ของอินเทอร์เฟซโดยสรุปคือการทำเครื่องหมายประเภทของคลาส การระบุประเภทของคลาสที่แตกต่างกันให้กับอินเทอร์เฟซที่แตกต่างกันสามารถจัดการได้ดีขึ้น
ประเด็นของการสืบทอดก็คือนามธรรม ไม่ใช่การใช้โค้ดซ้ำ
หลังจากอ่านบทความนี้ ตอนนี้ฉันเข้าใจวิธีการใช้อินเทอร์เฟซ คลาสนามธรรม และการสืบทอดแล้ว
ข้อความต้นฉบับมีดังนี้:
ชี้แจง Java (อินเทอร์เฟซและมรดก) พี่ชายของฉันซึ่งเป็นนักศึกษาระดับบัณฑิตศึกษาปีที่สองในคณะวิทยาการคอมพิวเตอร์ได้พูดคุยถึง Java กับฉัน เมื่อเราพบกันมีคำถามมากมายเกี่ยวกับอินเทอร์เฟซ ทำไมต้องใช้อินเทอร์เฟซ? เมื่อใดที่คุณควรใช้อินเทอร์เฟซ? ฉันดีใจที่พวกเขาไม่ได้ถามฉันว่าจะเชื่อมต่อกับ SQL Server โดยใช้ Java ได้อย่างไร หรือจะพัฒนาแอปพลิเคชัน J2EE อย่างไร คำถามดังกล่าวเป็นอันตรายถึงชีวิตและควรหลีกเลี่ยง ปีนี้คณะวิทยาการคอมพิวเตอร์มีโครงการรับปริญญา J2ME นักเรียนที่เลือกหัวข้อนี้ยังคงเรียนแพ็คเกจ java.util.* ด้วยความหน้าตาบูดบึ้งเมื่อปลายเดือนพฤษภาคมนี้และนี่... เฮ้อ
คนส่วนใหญ่เชื่อว่าจุดประสงค์ของอินเทอร์เฟซคือการแทนที่การสืบทอดหลายรายการ ดังที่เราทุกคนทราบกันดีว่า Java ไม่มีกลไกการสืบทอดหลายอย่างเช่น C++ แต่สามารถใช้หลายอินเทอร์เฟซได้ ที่จริงแล้วสิ่งนี้เป็นเรื่องที่ลึกซึ้งมาก อินเทอร์เฟซและการสืบทอดเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง อินเทอร์เฟซไม่มีความสามารถในการแทนที่การสืบทอดหลายรายการ และไม่มีข้อผูกมัดดังกล่าว หน้าที่ของอินเทอร์เฟซโดยสรุปคือการทำเครื่องหมายประเภทของคลาส การระบุประเภทของคลาสที่แตกต่างกันให้กับอินเทอร์เฟซที่แตกต่างกันสามารถจัดการได้ดีขึ้น ฉันคิดว่าแก่นแท้ของ OO คือนามธรรมของวัตถุ และอินเทอร์เฟซรวบรวมสิ่งนี้ได้ดีที่สุด ทำไมเราถึงพูดถึงรูปแบบการออกแบบสำหรับภาษาที่มีความสามารถเชิงนามธรรมเท่านั้น (เช่น c++, java, c# ฯลฯ) ก็เพราะว่ารูปแบบการออกแบบใดที่ศึกษาจริง ๆ แล้วจะเป็นนามธรรมอย่างสมเหตุสมผลได้อย่างไร (คำพูดอันโด่งดังของคาวบอยคือ "นามธรรมคือการลบส่วนของภาพออก" ซึ่งดูเหมือนจะเป็นเรื่องตลก แต่จริงๆ แล้วเป็นเรื่องจริง)
รูปแบบการออกแบบพื้นฐานที่สุดคือรูปแบบโรงงาน ในแอปพลิเคชันง่ายๆ ที่ฉันสร้างขึ้นเมื่อเร็วๆ นี้ ฉันต้องการพยายามทำให้โปรแกรมของฉันพกพาได้ระหว่างหลายฐานข้อมูล แน่นอนว่าสิ่งนี้เกี่ยวข้องกับปัญหามากมาย รวมถึงวิธีความเข้ากันได้ของ SQL จาก DBMS ที่แตกต่างกันทำให้ปวดหัว เราอาจลดความซับซ้อนของปัญหาก่อนและพิจารณาเฉพาะวิธีการเชื่อมต่อฐานข้อมูลที่แตกต่างกันเท่านั้น
สมมติว่าฉันมีคลาสจำนวนมาก ได้แก่ Mysql.java, SQLServer.java, Oracle.java และ DB2.java คลาสเหล่านี้เชื่อมต่อกับฐานข้อมูลที่แตกต่างกันตามลำดับ ส่งคืนอ็อบเจ็กต์การเชื่อมต่ออย่างสม่ำเสมอ และทั้งหมดมีวิธีการปิดสำหรับการปิดการเชื่อมต่อ คุณเพียงแค่ต้องเลือกคลาสที่แตกต่างกันสำหรับ DBMS ของคุณ และคุณสามารถใช้มันได้ แต่ผู้ใช้ของฉันจะใช้ฐานข้อมูลใด ฉันไม่รู้ สิ่งที่ฉันหวังคือปรับเปลี่ยนโค้ดให้น้อยที่สุดเท่าที่จะเป็นไปได้เพื่อตอบสนองความต้องการของเขา ฉันสามารถสรุปอินเทอร์เฟซต่อไปนี้:
แพ็คเกจ org.bromon.test;
ฐานข้อมูลอินเทอร์เฟซสาธารณะ
-
java.sql.Connection openDB (URL สตริง, ผู้ใช้สตริง, รหัสผ่านสตริง);
เป็นโมฆะปิด ();
}
อินเทอร์เฟซนี้กำหนดเพียงสองวิธีโดยไม่มีโค้ดที่มีความหมาย โค้ดเฉพาะจะได้รับจากคลาสที่ใช้อินเทอร์เฟซนี้ เช่น Mysql.java:
Package org.bromon.test;
นำเข้า java.sql.*;
Mysql คลาสสาธารณะใช้ DB
-
สตริงส่วนตัว url = "jdbc:mysql:localhost:3306/test";
ผู้ใช้สตริงส่วนตัว = "root";
รหัสผ่านสตริงส่วนตัว =””;
การเชื่อมต่อส่วนตัว;
การเชื่อมต่อสาธารณะ openDB (url, ผู้ใช้, รหัสผ่าน)
-
//รหัสเพื่อเชื่อมต่อกับฐานข้อมูล}
public void close()
-
//ปิดฐานข้อมูล}
}
แน่นอนสิ่งที่คล้ายกันคือ Oracle.java เป็นต้น อินเทอร์เฟซ DB จะจัดคลาสเหล่านี้ ในแอปพลิเคชัน เรากำหนดออบเจ็กต์ดังนี้:
org.bromon.test.DB myDB
เพื่อดำเนินการฐานข้อมูล และคุณไม่ต้องการ จริง ๆ แล้วผมใช้คลาสไหนอยู่ที่เรียกว่าหลักการ "เปิด-ปิด" แต่ปัญหาคืออินเทอร์เฟซไม่สามารถสร้างอินสแตนซ์ได้ myDB=new DB() รหัสดังกล่าวผิดอย่างแน่นอน เราทำได้เพียง myDB=new Mysql() หรือ myDB=new Oracle() ขออภัย ฉันยังต้องระบุคลาสที่ต้องการสร้างอินสแตนซ์ไม่มีประโยชน์ ดังนั้นเราจึงต้องมีโรงงาน:
package org.bromon.test;
DBFactory คลาสสาธารณะ
-
การเชื่อมต่อฐานข้อมูลแบบคงที่สาธารณะ getConn()
-
กลับ (Mysql ใหม่ ());
-
}
ดังนั้นโค้ดการสร้างอินสแตนซ์จึงกลายเป็น: myDB=DBFactory.getConn();
นี่คือโรงงานธรรมดาขั้นพื้นฐานที่สุด (โรงงาน) ในบรรดา 23 โหมด คลาสโรงงานมีหน้าที่รับผิดชอบในการสร้างอินสแตนซ์ของคลาสและลอจิกโปรแกรมอื่น ๆ ที่ทำงานบนอินเทอร์เฟซ DB โดยเฉพาะ ความรับผิดชอบได้ถูกส่งต่อไปยังคลาสโรงงานแล้ว แน่นอนว่า คุณยังสามารถกำหนดอินเทอร์เฟซของโรงงานต่อไปและส่งต่อความรับผิดชอบต่อไป ซึ่งพัฒนาไปสู่โรงงานที่เป็นนามธรรม
อินเทอร์เฟซจะไม่รับผิดชอบต่อการดำเนินการเฉพาะใดๆ ในระหว่างกระบวนการทั้งหมด หากโปรแกรมอื่นต้องการเชื่อมต่อกับฐานข้อมูล พวกเขาเพียงแค่สร้างอ็อบเจ็กต์ DB โดยไม่คำนึงว่าคลาสของโรงงานจะเปลี่ยนแปลงไปอย่างไร นี่คือสิ่งที่เกี่ยวกับอินเทอร์เฟซ - นามธรรม
ไม่จำเป็นต้องพูดว่าแนวคิดเรื่องมรดกนั้นง่ายต่อการเข้าใจ ทำไมต้องสืบทอด? เพราะคุณต้องการใช้รหัสซ้ำ? นี่ไม่ใช่เหตุผลอย่างแน่นอน ประเด็นของการสืบทอดคือนามธรรม ไม่ใช่การนำโค้ดกลับมาใช้ใหม่ หากอ็อบเจ็กต์ A มีเมธอด run() วัตถุ B ก็ต้องการที่จะมีเมธอดนี้เช่นกัน ดังนั้นจึงมีคนใช้ Class B ขยาย A นี่เป็นแนวทางที่ไร้เหตุผล หากคุณยกตัวอย่าง A ใน B และเรียกใช้เมธอด Run() ของ A จะสามารถบรรลุจุดประสงค์เดียวกันได้หรือไม่ ดังต่อไปนี้:
คลาสบี
-
A a=ใหม่ A();
ก.รัน();
}
นี่คือการใช้การรวมคลาสเพื่อนำโค้ดกลับมาใช้ใหม่ มันเป็นต้นแบบของโมเดลการมอบหมายและแนวทางปฏิบัติที่ GoF สนับสนุนมาโดยตลอด
แล้วประเด็นของมรดกคืออะไร? อันที่จริงสิ่งนี้มีสาเหตุมาจากเหตุผลทางประวัติศาสตร์ ภาษา OO ดั้งเดิมมีเพียงการสืบทอดและไม่มีอินเทอร์เฟซ ดังนั้นสิ่งที่เป็นนามธรรมสามารถทำได้ผ่านการสืบทอดเท่านั้น โปรดทราบว่าจุดประสงค์ดั้งเดิมของการสืบทอดคือสิ่งที่เป็นนามธรรม ไม่ใช่การใช้โค้ดซ้ำ (แม้ว่าการสืบทอดก็มีสิ่งนี้เช่นกัน ผลกระทบ) นี่เป็นหนึ่งในข้อผิดพลาดที่ร้ายแรงที่สุดของหนังสือ Java ที่ไม่ดีหลายเล่ม ฉันไม่ได้กำจัดเงาที่เกิดขึ้นโดยสิ้นเชิง เมื่อใดที่คุณควรใช้มรดก? ใช้ในคลาสนามธรรมเท่านั้น พยายามอย่าใช้ในสถานการณ์อื่น คลาสนามธรรมไม่สามารถสร้างอินสแตนซ์ได้ มีเพียงเทมเพลตซึ่งแสดงให้เห็นปัญหาเท่านั้น
ต้นตอของความชั่วร้ายในการพัฒนาซอฟต์แวร์ โดยเฉพาะในหมู่โปรแกรมเมอร์ C++ คือการทำซ้ำโค้ดแทนที่จะใช้โค้ดซ้ำ และการใช้การสืบทอดในทางที่ผิด จุดประสงค์ของการห้ามการสืบทอดหลายรายการใน Java คือการหยุดการใช้การสืบทอดในทางที่ผิด ซึ่งเป็นวิธีที่ชาญฉลาดมาก แต่หลายคนไม่เข้าใจ Java สามารถสะท้อนถึงการออกแบบได้ดีกว่า ซึ่งเป็นเหตุผลหนึ่งที่ทำให้ฉันหลงใหล