Đây câu trả lời làm một công việc tốt trong việc giải thích sự khác biệt giữa một lớp trừu tượng và một giao diện, nhưng nó không trả lời lý do tại sao bạn nên tuyên bố một.
Từ quan điểm thuần túy kỹ thuật, không bao giờ có yêu cầu khai báo một lớp là trừu tượng.
Hãy xem xét ba lớp sau:
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
Bạn không cần phải làm cho lớp Cơ sở dữ liệu trừu tượng, mặc dù có một vấn đề rõ ràng với việc triển khai: Khi bạn đang viết chương trình này, bạn có thể nhập new Database()
và nó sẽ hợp lệ, nhưng nó sẽ không bao giờ hoạt động.
Bất kể, bạn vẫn sẽ có được đa hình, miễn là chương trình của bạn chỉ thực hiện SqlDatabase
và các OracleDatabase
thể hiện, bạn có thể viết các phương thức như:
public void printTableNames(Database database) {
String[] names = database.getTableNames();
}
Các lớp trừu tượng cải thiện tình hình bằng cách ngăn nhà phát triển khởi tạo lớp cơ sở, bởi vì nhà phát triển đã đánh dấu nó là thiếu chức năng . Nó cũng cung cấp sự an toàn trong thời gian biên dịch để bạn có thể đảm bảo rằng bất kỳ lớp nào mở rộng lớp trừu tượng của bạn đều cung cấp chức năng tối thiểu để hoạt động và bạn không cần phải lo lắng về việc đặt các phương thức sơ khai (như cách trên) mà người thừa kế bằng cách nào đó có để biết một cách kỳ diệu rằng họ phải ghi đè một phương thức để làm cho nó hoạt động.
Giao diện là một chủ đề hoàn toàn riêng biệt. Một giao diện cho phép bạn mô tả những hoạt động có thể được thực hiện trên một đối tượng. Thông thường bạn sẽ sử dụng các giao diện khi viết phương thức, thành phần, v.v ... sử dụng dịch vụ của các thành phần, đối tượng khác, nhưng bạn không quan tâm loại đối tượng thực tế mà bạn nhận được dịch vụ là gì.
Hãy xem xét phương pháp sau:
public void saveToDatabase(IProductDatabase database) {
database.addProduct(this.getName(), this.getPrice());
}
Bạn không quan tâm đến việc database
đối tượng có kế thừa từ bất kỳ đối tượng cụ thể nào không, bạn chỉ quan tâm rằng nó có một addProduct
phương thức. Vì vậy, trong trường hợp này, một giao diện phù hợp hơn là làm cho tất cả các lớp của bạn xảy ra kế thừa từ cùng một lớp cơ sở.
Đôi khi sự kết hợp của hai công việc rất độc đáo. Ví dụ:
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
}
}
Lưu ý cách một số cơ sở dữ liệu kế thừa từ RemoteDatabase để chia sẻ một số chức năng (như kết nối trước khi viết một hàng), nhưng FileDatabase là lớp riêng biệt chỉ thực hiện IProductDatabase
.