Lớp trừu tượng
Khi một lớp cha cần khai báo phương thức nhưng chưa xác định cách triển khai, ta có thể biến lớp này thành lớp trừu tượng bằng từ khóa abstract.
Điểm khác biệt chính giữa lớp trừu tượng và lớp thông thường: Lớp trừu tượng không thể khởi tạo trực tiếp. Lớp trừu tượng có thể chứa phương thức trừu tượng.
Mối quan hệ giữa lớp trừu tượng và phương thức trừu tượng: Lớp trừu tượng thường chứa phương thức trừu tượng, nhưng phương thức trừu tượng chỉ tồn tại trong lớp trừu tượng hoặc giao diện.
public class TemplateDemo {
public static void main(String[] args) {
BaseTemplate template = new ConcreteImplementation();
System.out.println(BaseTemplate.staticCounter);
BaseTemplate.staticUtility();
}
}
public abstract class BaseTemplate {
private int internalState;
public static int staticCounter = 42;
public BaseTemplate() {}
public abstract void processLogic();
public void standardOperation() {
System.out.println("Standard operation");
}
public static void staticUtility() {
System.out.println("Static utility method");
}
}
class ConcreteImplementation extends BaseTemplate {
@Override
public void processLogic() {
System.out.println("Implementation logic");
}
}
Giao diện
Tổng quan
Giao diện không phải là lớp mà là tập hợp các yêu cầu phương thức mà các lớp khác phải tuân thủ. Đây là sự mở rộng của lớp trừu tượng, giải quyết hạn chế kế thừa đa hình trong Java.
Từ Java 8 trở đi, giao diện có thể chứa phương thức thực thi, nhưng chỉ với static hoặc default.
public interface DeviceProtocol {
int MAX_VALUE = 100;
static void utilityMethod() {
System.out.println("Static method");
}
void activate();
default void defaultAction() {
System.out.println("Default action");
}
}
class DeviceImplementor implements DeviceProtocol {
@Override
public void activate() {}
public static void main(String[] args) {
DeviceProtocol utility = new DeviceImplementor();
DeviceProtocol.utilityMethod();
System.out.println(DeviceProtocol.MAX_VALUE);
}
}
abstract class AbstractDevice implements DeviceProtocol {}
Tính đa hình của giao diện
public interface Connectable {
void start();
void stop();
}
public class DigitalCamera implements Connectable {
@Override
public void start() {
System.out.println("Camera started");
}
@Override
public void stop() {
System.out.println("Camera stopped");
}
}
public class MobileDevice implements Connectable {
@Override
public void start() {
System.out.println("Device started");
}
@Override
public void stop() {
System.out.println("Device stopped");
}
}
public class SystemManager {
public void handleDevice(Connectable device) {
device.start();
device.stop();
}
}
public class InterfaceDemo {
public static void main(String[] args) {
DigitalCamera camera = new DigitalCamera();
MobileDevice phone = new MobileDevice();
SystemManager manager = new SystemManager();
manager.handleDevice(camera);
manager.handleDevice(phone);
Connectable[] devices = new Connectable[2];
devices[0] = new MobileDevice();
devices[1] = new DigitalCamera();
for (Connectable dev : devices) {
manager.handleDevice(dev);
}
}
}
So sánh giao diện và kế thừa
- Giải quyết vấn đề khác nhau: Kế thừa tập trung vào tái sử dụng mã, giao diện định nghĩa hợp đồng phương thức
- Linh hoạt hơn: Kế thừa tuân theo quan hệ is-a, giao diện tuân theo quan hệ like-a
- Giảm耦 hợp: Giao diện kết hợp với binding động tạo ra hệ thống dễ bảo trì
So sánh lớp trừu tượng và giao diện
- Mô hình thiết kế: Lớp trừu tượng thể hiện is-a (tuân theo nguyên tắc LSP), giao diện thể hiện like-a
- Số lượng kế thừa: Lớp chỉ có thể kế thừa một lớp trừu tượng, nhưng có thể triển khai nhiều giao diện
- Phạm vi truy cập: Thành viên giao diện chỉ có thể là
public, lớp trừu tượng có thể có nhiều mức độ truy cập - Phương thức: Lớp trừu tượng có thể chứa phương thức bình thường, giao diện chỉ có phương thức mặc định