นี้คำตอบไม่ได้งานที่ดีของการอธิบายความแตกต่างระหว่างระดับนามธรรมและอินเตอร์เฟซ แต่ก็ไม่ได้คำตอบว่าทำไมคุณควรประกาศอย่างใดอย่างหนึ่ง
จากมุมมองทางเทคนิคล้วนๆไม่จำเป็นต้องประกาศคลาสเป็นนามธรรม
พิจารณาสามคลาสต่อไปนี้:
class Database {
public String[] getTableNames() { return null; } //or throw an exception? who knows...
}
class SqlDatabase extends Database { } //TODO: override getTableNames
class OracleDatabase extends Database { } //TODO: override getTableNames
คุณไม่จำเป็นต้องทำให้คลาสฐานข้อมูลเป็นนามธรรมแม้ว่าจะมีปัญหาที่ชัดเจนเกี่ยวกับการใช้งาน: เมื่อคุณเขียนโปรแกรมนี้คุณสามารถพิมพ์new Database()
และมันจะใช้ได้ แต่มันจะไม่ทำงาน
ไม่ว่าคุณจะยังคงได้รับ polymorphism ตราบใดที่โปรแกรมของคุณสร้างSqlDatabase
และOracleDatabase
อินสแตนซ์เท่านั้นคุณสามารถเขียนวิธีการเช่น:
public void printTableNames(Database database) {
String[] names = database.getTableNames();
}
คลาสนามธรรมปรับปรุงสถานการณ์โดยการป้องกันการเป็นนักพัฒนาจากอินสแตนซ์ชั้นฐานเพราะนักพัฒนาได้ทำเครื่องหมายว่ามีฟังก์ชั่นที่ขาดหายไป นอกจากนี้ยังมีความปลอดภัยในการคอมไพล์เวลาเพื่อให้คุณสามารถมั่นใจได้ว่าคลาสใด ๆ ที่ขยายคลาสนามธรรมของคุณจะมีฟังก์ชั่นขั้นต่ำที่เปลือยเปล่าในการทำงานและคุณไม่จำเป็นต้องกังวลเกี่ยวกับการใส่วิธีการสตับ เพื่อที่จะรู้ได้อย่างน่าอัศจรรย์ว่าพวกเขาต้องแทนที่วิธีการเพื่อให้มันใช้งานได้
การเชื่อมต่อเป็นหัวข้อแยกกันโดยสิ้นเชิง อินเทอร์เฟซช่วยให้คุณอธิบายการดำเนินการที่สามารถทำได้บนวัตถุ โดยทั่วไปแล้วคุณจะใช้อินเทอร์เฟซเมื่อเขียนวิธีการส่วนประกอบ ฯลฯ ที่ใช้บริการของส่วนประกอบอื่น ๆ วัตถุ แต่คุณไม่สนใจว่าวัตถุประเภทจริงที่คุณได้รับบริการคืออะไร
พิจารณาวิธีการต่อไปนี้:
public void saveToDatabase(IProductDatabase database) {
database.addProduct(this.getName(), this.getPrice());
}
คุณไม่สนใจว่าdatabase
วัตถุนั้นจะสืบทอดมาจากวัตถุใด ๆ หรือไม่คุณเพียงแคร์ว่ามันมีaddProduct
วิธีการ ดังนั้นในกรณีนี้อินเทอร์เฟซที่เหมาะสมกว่าการทำให้คลาสทั้งหมดของคุณเกิดขึ้นจากการสืบทอดจากคลาสพื้นฐานเดียวกัน
บางครั้งการรวมกันของทั้งสองทำงานได้เป็นอย่างดี ตัวอย่างเช่น:
abstract class RemoteDatabase implements IProductDatabase {
public abstract String[] connect();
public abstract void writeRow(string col1, string col2);
public void addProduct(String name, Double price) {
connect();
writeRow(name, price.toString());
}
}
class SqlDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class OracleDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class FileDatabase implements IProductDatabase {
public void addProduct(String name, Double price) {
//TODO: just write to file
}
}
แจ้งให้ทราบว่าบางส่วนของฐานข้อมูลสืบทอดจาก RemoteDatabase เพื่อแบ่งปันการทำงานบางอย่าง (เช่นการเชื่อมต่อก่อนที่จะเขียนแถว) แต่ FileDatabase IProductDatabase
เป็นชั้นที่แยกต่างหากที่ดำเนินการเท่านั้น