1. ภาพรวม
มีหลายกรณีในการดำเนินการตามสัญญาที่เราต้องการเลื่อนการใช้งานบางส่วนออกไปเพื่อให้เสร็จสมบูรณ์ในภายหลัง เราสามารถทำได้อย่างง่ายดายใน Java ผ่านคลาสนามธรรม
ในการกวดวิชานี้เราจะได้เรียนรู้พื้นฐานของการเรียนนามธรรมใน Java และในสิ่งที่กรณีที่พวกเขาจะมีประโยชน์
2. แนวคิดหลักสำหรับคลาสนามธรรม
ก่อนที่จะดำน้ำว่าจะใช้คลาสนามธรรมเมื่อใดเรามาดูลักษณะที่เกี่ยวข้องมากที่สุด :
- เรากำหนดคลาสนามธรรมด้วยตัวปรับนามธรรมที่นำหน้าคีย์เวิร์ดคลาส
- คลาสนามธรรมสามารถเป็นคลาสย่อยได้ แต่ไม่สามารถสร้างอินสแตนซ์ได้
- ถ้าคลาสหนึ่งกำหนดวิธีการที่เป็นนามธรรมอย่างน้อยหนึ่งคลาสก็จะต้องประกาศคลาสนั้นเป็นนามธรรม
- คลาสนามธรรมสามารถประกาศวิธีการทั้งนามธรรมและรูปธรรม
- คลาสย่อยที่ได้มาจากคลาสนามธรรมต้องใช้วิธีการนามธรรมของคลาสพื้นฐานทั้งหมดหรือเป็นนามธรรมเอง
เพื่อให้เข้าใจแนวคิดเหล่านี้ดีขึ้นเราจะสร้างตัวอย่างง่ายๆ
มาให้คลาสนามธรรมพื้นฐานของเรากำหนด API นามธรรมของเกมกระดาน:
public abstract class BoardGame { //... field declarations, constructors public abstract void play(); //... concrete methods }
จากนั้นเราสามารถสร้างคลาสย่อยที่ใช้วิธีการเล่น :
public class Checkers extends BoardGame { public void play() { //... implementation } }
3. เมื่อใดควรใช้คลาสนามธรรม
ตอนนี้เรามาวิเคราะห์สถานการณ์ทั่วไปสองสามอย่างที่เราควรชอบคลาสนามธรรมมากกว่าอินเทอร์เฟซและคลาสคอนกรีต:
- เราต้องการสรุปฟังก์ชันการทำงานทั่วไปบางอย่างไว้ในที่เดียว (การใช้โค้ดซ้ำ) ที่หลายคลาสย่อยที่เกี่ยวข้องจะแชร์
- เราจำเป็นต้องกำหนด API บางส่วนเพื่อให้คลาสย่อยของเราสามารถขยายและปรับแต่งได้อย่างง่ายดาย
- คลาสย่อยจำเป็นต้องสืบทอดเมธอดหรือฟิลด์ทั่วไปอย่างน้อยหนึ่งรายการโดยมีตัวแก้ไขการเข้าถึงที่มีการป้องกัน
โปรดทราบว่าสถานการณ์ทั้งหมดเหล่านี้เป็นตัวอย่างที่ดีของการยึดมั่นตามหลักการเปิด / ปิดโดยสมบูรณ์
ยิ่งไปกว่านั้นเนื่องจากการใช้คลาสนามธรรมเกี่ยวข้องกับประเภทพื้นฐานและประเภทย่อยโดยปริยายเราจึงใช้ประโยชน์จาก Polymorphism
โปรดทราบว่าการใช้โค้ดซ้ำเป็นเหตุผลที่น่าสนใจมากในการใช้คลาสนามธรรมตราบใดที่ความสัมพันธ์“ is-a” ภายในลำดับชั้นของคลาสยังคงอยู่
และ Java 8 เพิ่มริ้วรอยอื่นด้วยวิธีการเริ่มต้นซึ่งบางครั้งอาจเกิดขึ้นจากความจำเป็นในการสร้างคลาสนามธรรมทั้งหมด
4. ลำดับชั้นตัวอย่างของผู้อ่านไฟล์
เพื่อให้เข้าใจฟังก์ชันการทำงานที่คลาสนามธรรมนำมาสู่ตารางได้ชัดเจนยิ่งขึ้นลองดูตัวอย่างอื่น
4.1. การกำหนดคลาสบทคัดย่อพื้นฐาน
ดังนั้นหากเราต้องการมีโปรแกรมอ่านไฟล์หลายประเภทเราอาจสร้างคลาสนามธรรมที่สรุปสิ่งที่ใช้กันทั่วไปในการอ่านไฟล์:
public abstract class BaseFileReader { protected Path filePath; protected BaseFileReader(Path filePath) { this.filePath = filePath; } public Path getFilePath() { return filePath; } public List readFile() throws IOException { return Files.lines(filePath) .map(this::mapFileLine).collect(Collectors.toList()); } protected abstract String mapFileLine(String line); }
โปรดทราบว่าเราได้ทำการป้องกันfilePathเพื่อให้คลาสย่อยสามารถเข้าถึงได้หากจำเป็น ที่สำคัญเราได้ยกเลิกบางสิ่งบางอย่างไปแล้ว: วิธีแยกวิเคราะห์บรรทัดข้อความจากเนื้อหาของไฟล์
แผนของเรานั้นเรียบง่าย: ในขณะที่คลาสคอนกรีตของเราแต่ละคนไม่มีวิธีพิเศษในการจัดเก็บเส้นทางไฟล์หรือเดินผ่านไฟล์พวกเขาจะมีวิธีพิเศษในการแปลงแต่ละบรรทัด
เมื่อแรกเห็นBaseFileReaderอาจดูเหมือนไม่จำเป็น อย่างไรก็ตามเป็นรากฐานของการออกแบบที่สะอาดและขยายได้ง่าย จากนั้นเราสามารถดำเนินการรุ่นที่แตกต่างของผู้อ่านไฟล์ที่สามารถมุ่งเน้นตรรกะทางธุรกิจของพวกเขาที่ไม่ซ้ำกัน
4.2. การกำหนดคลาสย่อย
การใช้งานตามธรรมชาติอาจเป็นสิ่งที่แปลงเนื้อหาของไฟล์เป็นตัวพิมพ์เล็ก:
public class LowercaseFileReader extends BaseFileReader { public LowercaseFileReader(Path filePath) { super(filePath); } @Override public String mapFileLine(String line) { return line.toLowerCase(); } }
หรืออย่างอื่นอาจเป็นไฟล์ที่แปลงเนื้อหาของไฟล์เป็นตัวพิมพ์ใหญ่:
public class UppercaseFileReader extends BaseFileReader { public UppercaseFileReader(Path filePath) { super(filePath); } @Override public String mapFileLine(String line) { return line.toUpperCase(); } }
ดังที่เราเห็นจากตัวอย่างง่ายๆนี้แต่ละคลาสย่อยสามารถมุ่งเน้นไปที่พฤติกรรมเฉพาะของมันโดยไม่จำเป็นต้องระบุแง่มุมอื่น ๆ ของการอ่านไฟล์
4.3. การใช้คลาสย่อย
สุดท้ายการใช้คลาสที่สืบทอดมาจากนามธรรมก็ไม่ต่างจากคลาสคอนกรีตอื่น ๆ :
@Test public void givenLowercaseFileReaderInstance_whenCalledreadFile_thenCorrect() throws Exception { URL location = getClass().getClassLoader().getResource("files/test.txt") Path path = Paths.get(location.toURI()); BaseFileReader lowercaseFileReader = new LowercaseFileReader(path); assertThat(lowercaseFileReader.readFile()).isInstanceOf(List.class); }
เพื่อประโยชน์ของความเรียบง่ายแฟ้มเป้าหมายจะอยู่ภายใต้src / ทรัพยากร / หลัก / ไฟล์โฟลเดอร์ ดังนั้นเราจึงใช้ตัวโหลดคลาสของแอปพลิเคชันเพื่อรับเส้นทางของไฟล์ตัวอย่าง อย่าลังเลที่จะดูบทช่วยสอนของเราเกี่ยวกับตัวโหลดคลาสใน Java
5. สรุป
ในบทความนี้อย่างรวดเร็วเราได้เรียนรู้พื้นฐานของการเรียนนามธรรมใน Java และเมื่อจะใช้พวกเขาเพื่อให้บรรลุสิ่งที่เป็นนามธรรมและห่อหุ้มการดำเนินงานร่วมกันในที่เดียว
ตามปกติตัวอย่างโค้ดทั้งหมดที่แสดงในบทช่วยสอนนี้มีอยู่ใน GitHub