Abstraction (Trừu tượng)
Bài trước: Polymorphism (Đa hình) — Đã học cách một đối tượng có thể có nhiều hình thái thông qua method overriding. Bài này sẽ học cách ẩn đi chi tiết triển khai và chỉ hiển thị chức năng thiết yếu thông qua abstraction.
Sau bài này, bạn sẽ:
- Hiểu khái niệm abstraction và cách nó giảm độ phức tạp của code
- Tạo và sử dụng abstract class với abstract methods và concrete methods
- Phân biệt abstract class với concrete class và biết khi nào dùng loại nào
- Áp dụng Template Method Pattern để định nghĩa skeleton của algorithm
- Nắm vững các quy tắc và hạn chế khi làm việc với abstract class
Abstraction là gì?
Abstraction (Trừu tượng hóa) là quá trình ẩn đi chi tiết triển khai phức tạp và chỉ hiển thị các chức năng thiết yếu cho người dùng.
Ví dụ thực tế
- Xe hơi: Bạn chỉ cần biết cách lái (gas, brake, steering) mà không cần hiểu động cơ hoạt động như thế nào
- Điện thoại: Bạn nhấn nút gọi mà không cần biết cách tín hiệu được truyền đi
- ATM: Bạn rút tiền mà không cần biết cơ chế xác thực và xử lý giao dịch bên trong
- Giảm độ phức tạp: Che giấu chi tiết phức tạp, chỉ hiện những gì cần thiết
- Tăng tính bảo mật: Ẩn implementation details
- Tập trung vào "What" thay vì "How": Người dùng chỉ cần biết làm gì, không cần biết làm như thế nào
- Dễ bảo trì: Thay đổi implementation không ảnh hưởng đến người dùng
Abstract Class
Abstract class là class không thể khởi tạo trực tiếp, thường chứa abstract methods (methods không có implementation).
Cú pháp
abstract class Animal {
// Abstract method - không có body
public abstract void makeSound();
public abstract void move();
// Concrete method - có implementation
public void sleep() {
System.out.println("Animal is sleeping...");
}
// Constructor
public Animal() {
System.out.println("Animal created");
}
}
class Dog extends Animal {
// Bắt buộc implement tất cả abstract methods
@Override
public void makeSound() {
System.out.println("Woof! Woof!");
}
@Override
public void move() {
System.out.println("Dog runs on four legs");
}
}
class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("Tweet! Tweet!");
}
@Override
public void move() {
System.out.println("Bird flies in the sky");
}
}
Sử dụng
public class TestAbstraction {
public static void main(String[] args) {
// ❌ Không thể khởi tạo abstract class
// Animal animal = new Animal(); // COMPILE ERROR
// ✅ Tạo objects của concrete classes
Animal dog = new Dog();
dog.makeSound(); // Woof! Woof!
dog.move(); // Dog runs on four legs
dog.sleep(); // Animal is sleeping...
Animal bird = new Bird();
bird.makeSound(); // Tweet! Tweet!
bird.move(); // Bird flies in the sky
bird.sleep(); // Animal is sleeping...
}
}
Abstract Methods vs Concrete Methods
| Abstract Method | Concrete Method |
|---|---|
| Không có body (chỉ có signature) | Có body (có implementation) |
| Phải được override bởi subclass | Có thể override hoặc không |
| Chỉ tồn tại trong abstract class | Có thể ở bất kỳ class nào |
Dùng từ khóa abstract | Không có từ khóa đặc biệt |
abstract class Shape {
protected String color;
// Constructor
public Shape(String color) {
this.color = color;
}
// Abstract methods - bắt buộc implement
public abstract double getArea();
public abstract double getPerimeter();
// Concrete method - đã có implementation
public void displayColor() {
System.out.println("Color: " + color);
}
// Concrete method
public void displayInfo() {
displayColor();
System.out.println("Area: " + getArea());
System.out.println("Perimeter: " + getPerimeter());
}
}
class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
@Override
public double getPerimeter() {
return 2 * (width + height);
}
}
Không thể Instantiate Abstract Class
abstract class Vehicle {
public abstract void start();
}
public class Test {
public static void main(String[] args) {
// ❌ COMPILE ERROR: Cannot instantiate the type Vehicle
// Vehicle vehicle = new Vehicle();
// ✅ Có thể dùng anonymous class
Vehicle vehicle = new Vehicle() {
@Override
public void start() {
System.out.println("Vehicle started");
}
};
vehicle.start();
}
}
- Abstract class không thể khởi tạo trực tiếp
- Abstract class có thể có constructor (dùng bởi subclass)
- Abstract class có thể có cả abstract và concrete methods
- Nếu class có ít nhất 1 abstract method → class phải là abstract
- Subclass phải implement tất cả abstract methods (hoặc tự nó cũng là abstract)
- Abstract methods không thể là
private,final, hoặcstatic
Khi nào dùng Abstract Class?
1. Khi có chung behavior cần chia sẻ
abstract class BankAccount {
protected String accountNumber;
protected double balance;
public BankAccount(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
}
// Chung cho tất cả accounts
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
}
}
// Mỗi loại account có cách withdraw khác nhau
public abstract void withdraw(double amount);
// Mỗi loại account có interest rate khác nhau
public abstract double calculateInterest();
public double getBalance() {
return balance;
}
}
class SavingsAccount extends BankAccount {
private static final double INTEREST_RATE = 0.04; // 4%
public SavingsAccount(String accountNumber, double balance) {
super(accountNumber, balance);
}
@Override
public void withdraw(double amount) {
if (balance - amount >= 1000) { // Minimum balance
balance -= amount;
System.out.println("Withdrew: $" + amount);
} else {
System.out.println("Cannot withdraw. Minimum balance: $1000");
}
}
@Override
public double calculateInterest() {
return balance * INTEREST_RATE;
}
}
class CheckingAccount extends BankAccount {
private double overdraftLimit;
public CheckingAccount(String accountNumber, double balance, double overdraftLimit) {
super(accountNumber, balance);
this.overdraftLimit = overdraftLimit;
}
@Override
public void withdraw(double amount) {
if (balance - amount >= -overdraftLimit) {
balance -= amount;
System.out.println("Withdrew: $" + amount);
} else {
System.out.println("Exceeded overdraft limit");
}
}
@Override
public double calculateInterest() {
return 0; // No interest for checking account
}
}
2. Khi muốn force subclass implement certain methods
abstract class Game {
// Template method
public final void play() {
initialize();
startPlay();
endPlay();
}
// Abstract methods - subclass phải implement
public abstract void initialize();
public abstract void startPlay();
public abstract void endPlay();
}
class Chess extends Game {
@Override
public void initialize() {
System.out.println("Chess Game Initialized");
}
@Override
public void startPlay() {
System.out.println("Chess Game Started");
}
@Override
public void endPlay() {
System.out.println("Chess Game Finished");
}
}
class Soccer extends Game {
@Override
public void initialize() {
System.out.println("Soccer Game Initialized");
}
@Override
public void startPlay() {
System.out.println("Soccer Game Started");
}
@Override
public void endPlay() {
System.out.println("Soccer Game Finished");
}
}
Template Method Pattern
Template Method Pattern định nghĩa skeleton của algorithm trong method, để subclass override một số bước cụ thể.
abstract class DataProcessor {
// Template method - final để không thể override
public final void process() {
readData();
processData();
saveData();
}
// Common implementation
private void readData() {
System.out.println("Reading data from source...");
}
// Abstract - subclass tự implement
protected abstract void processData();
// Hook method - subclass có thể override nếu muốn
protected void saveData() {
System.out.println("Saving data to default location");
}
}
class CSVDataProcessor extends DataProcessor {
@Override
protected void processData() {
System.out.println("Processing CSV data...");
System.out.println("- Parsing CSV format");
System.out.println("- Validating columns");
}
@Override
protected void saveData() {
System.out.println("Saving to CSV file");
}
}
class JSONDataProcessor extends DataProcessor {
@Override
protected void processData() {
System.out.println("Processing JSON data...");
System.out.println("- Parsing JSON format");
System.out.println("- Validating schema");
}
@Override
protected void saveData() {
System.out.println("Saving to JSON file");
}
}
class XMLDataProcessor extends DataProcessor {
@Override
protected void processData() {
System.out.println("Processing XML data...");
System.out.println("- Parsing XML format");
System.out.println("- Validating DTD/XSD");
}
}
// Demo
public class TemplateMethodDemo {
public static void main(String[] args) {
System.out.println("=== CSV Processing ===");
DataProcessor csvProcessor = new CSVDataProcessor();
csvProcessor.process();
System.out.println("\n=== JSON Processing ===");
DataProcessor jsonProcessor = new JSONDataProcessor();
jsonProcessor.process();
System.out.println("\n=== XML Processing ===");
DataProcessor xmlProcessor = new XMLDataProcessor();
xmlProcessor.process();
}
}
Ví dụ thực tế
Ví dụ 1: Vehicle System
abstract class Vehicle {
protected String brand;
protected String model;
protected int year;
public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
// Abstract methods
public abstract void start();
public abstract void stop();
public abstract double calculateFuelEfficiency();
// Concrete method
public void displayInfo() {
System.out.println(year + " " + brand + " " + model);
}
}
class Car extends Vehicle {
private String fuelType;
public Car(String brand, String model, int year, String fuelType) {
super(brand, model, year);
this.fuelType = fuelType;
}
@Override
public void start() {
System.out.println("Car engine starting with " + fuelType + "...");
}
@Override
public void stop() {
System.out.println("Car engine stopping...");
}
@Override
public double calculateFuelEfficiency() {
return 15.5; // km per liter
}
}
class ElectricCar extends Vehicle {
private int batteryCapacity;
public ElectricCar(String brand, String model, int year, int batteryCapacity) {
super(brand, model, year);
this.batteryCapacity = batteryCapacity;
}
@Override
public void start() {
System.out.println("Electric motor starting silently...");
}
@Override
public void stop() {
System.out.println("Electric motor stopping...");
}
@Override
public double calculateFuelEfficiency() {
return batteryCapacity / 10.0; // km per kWh
}
}
class Motorcycle extends Vehicle {
private int engineCapacity;
public Motorcycle(String brand, String model, int year, int engineCapacity) {
super(brand, model, year);
this.engineCapacity = engineCapacity;
}
@Override
public void start() {
System.out.println("Motorcycle " + engineCapacity + "cc engine starting...");
}
@Override
public void stop() {
System.out.println("Motorcycle engine stopping...");
}
@Override
public double calculateFuelEfficiency() {
return 45.0; // km per liter
}
}
Ví dụ 2: Payment System
abstract class Payment {
protected double amount;
protected String transactionId;
public Payment(double amount) {
this.amount = amount;
this.transactionId = generateTransactionId();
}
// Template method
public final boolean executePayment() {
if (!validatePayment()) {
System.out.println("Payment validation failed");
return false;
}
processPayment();
sendReceipt();
return true;
}
// Abstract methods
protected abstract boolean validatePayment();
protected abstract void processPayment();
// Concrete methods
protected String generateTransactionId() {
return "TXN" + System.currentTimeMillis();
}
protected void sendReceipt() {
System.out.println("Receipt sent for transaction: " + transactionId);
}
}
class CreditCardPayment extends Payment {
private String cardNumber;
private String cvv;
public CreditCardPayment(double amount, String cardNumber, String cvv) {
super(amount);
this.cardNumber = cardNumber;
this.cvv = cvv;
}
@Override
protected boolean validatePayment() {
System.out.println("Validating credit card...");
// Validate card number, CVV, expiry, etc.
return cardNumber.length() == 16 && cvv.length() == 3;
}
@Override
protected void processPayment() {
System.out.println("Processing credit card payment of $" + amount);
System.out.println("Card: **** **** **** " + cardNumber.substring(12));
}
}
class PayPalPayment extends Payment {
private String email;
private String password;
public PayPalPayment(double amount, String email, String password) {
super(amount);
this.email = email;
this.password = password;
}
@Override
protected boolean validatePayment() {
System.out.println("Validating PayPal account...");
// Validate email and password
return email.contains("@") && password.length() >= 6;
}
@Override
protected void processPayment() {
System.out.println("Processing PayPal payment of $" + amount);
System.out.println("PayPal account: " + email);
}
}
class BankTransferPayment extends Payment {
private String accountNumber;
private String bankCode;
public BankTransferPayment(double amount, String accountNumber, String bankCode) {
super(amount);
this.accountNumber = accountNumber;
this.bankCode = bankCode;
}
@Override
protected boolean validatePayment() {
System.out.println("Validating bank account...");
return accountNumber.length() >= 10 && bankCode.length() == 6;
}
@Override
protected void processPayment() {
System.out.println("Processing bank transfer of $" + amount);
System.out.println("Bank: " + bankCode);
System.out.println("Account: " + accountNumber);
}
@Override
protected void sendReceipt() {
super.sendReceipt();
System.out.println("Bank transfer confirmation sent via SMS");
}
}
Abstract Class vs Interface — So sánh chi tiết
| Tiêu chí | Abstract Class | Interface |
|---|---|---|
| Multiple inheritance | ❌ Chỉ extend 1 class | ✅ Implement nhiều interface |
| Constructor | ✅ Có | ❌ Không có |
| Fields | ✅ Mọi loại (instance, static) | ⚠️ Chỉ public static final |
| Methods | ✅ Abstract + concrete | ✅ Abstract + default + static + private (Java 8+) |
| Access modifiers | ✅ Đầy đủ (public, protected, private) | ⚠️ Chỉ public (default: public abstract) |
| State | ✅ Có (instance variables) | ❌ Không có (chỉ constants) |
| Khi nào dùng | IS-A relationship, shared code | Contract, capability, multiple inheritance |
| extends/implements | extends | implements |
Decision Matrix: Dùng Abstract Class hay Interface?
Dùng Abstract Class khi:
- Có IS-A relationship rõ ràng (Car IS-A Vehicle)
- Cần chia sẻ code (fields, concrete methods) giữa subclasses
- Các subclasses có nhiều điểm chung về state và behavior
- Cần constructor để khởi tạo state
- Muốn định nghĩa non-public methods (protected, package-private)
Dùng Interface khi:
- Định nghĩa contract/capability không liên quan hierarchy (Flyable, Serializable)
- Muốn multiple inheritance (class implement nhiều interface)
- Các classes không liên quan cần cùng behavior (Bird và Airplane đều Flyable)
- Chỉ cần method signatures, không cần state
- Muốn đạt loose coupling
Ví dụ so sánh
// ✅ Abstract Class phù hợp - có IS-A, shared state
abstract class Vehicle {
protected String brand; // Shared state
protected int year;
public Vehicle(String brand, int year) { // Constructor
this.brand = brand;
this.year = year;
}
// Shared behavior
public void displayInfo() {
System.out.println(brand + " " + year);
}
// Abstract behavior - subclass implement
public abstract void start();
}
class Car extends Vehicle {
public Car(String brand, int year) {
super(brand, year);
}
@Override
public void start() {
System.out.println("Car starting...");
}
}
// ✅ Interface phù hợp - capability không liên quan hierarchy
interface Flyable {
void fly();
void land();
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird flies by flapping wings");
}
@Override
public void land() {
System.out.println("Bird lands on tree");
}
}
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane flies with jet engines");
}
@Override
public void land() {
System.out.println("Airplane lands on runway");
}
}
// Bird và Airplane không liên quan nhưng đều Flyable
Template Method Pattern — Application của Abstract Class
Template Method Pattern định nghĩa skeleton của algorithm trong abstract class, để subclasses override specific steps.
Ví dụ: Data Mining Framework
abstract class DataMiner {
// Template method - định nghĩa skeleton (final để không override)
public final void mine(String path) {
openFile(path);
extractData();
parseData();
analyzeData();
sendReport();
closeFile();
}
// Concrete methods - common implementation
private void openFile(String path) {
System.out.println("Opening file: " + path);
}
private void closeFile() {
System.out.println("Closing file");
}
// Abstract methods - subclasses phải implement
protected abstract void extractData();
protected abstract void parseData();
// Hook methods - subclasses có thể override nếu muốn
protected void analyzeData() {
System.out.println("Analyzing data with default algorithm");
}
protected void sendReport() {
System.out.println("Sending report via email");
}
}
class PDFDataMiner extends DataMiner {
@Override
protected void extractData() {
System.out.println("Extracting data from PDF");
}
@Override
protected void parseData() {
System.out.println("Parsing PDF data");
}
@Override
protected void analyzeData() {
System.out.println("Analyzing PDF data with custom algorithm");
}
}
class CSVDataMiner extends DataMiner {
@Override
protected void extractData() {
System.out.println("Extracting data from CSV");
}
@Override
protected void parseData() {
System.out.println("Parsing CSV data");
}
// Dùng default analyzeData() và sendReport()
}
// Usage
public class TemplateMethodDemo {
public static void main(String[] args) {
DataMiner pdfMiner = new PDFDataMiner();
pdfMiner.mine("data.pdf");
System.out.println();
DataMiner csvMiner = new CSVDataMiner();
csvMiner.mine("data.csv");
}
}
Giống công thức nấu ăn: Recipe (abstract class) định nghĩa các bước (openFile → extract → parse → analyze → send → close), nhưng nguyên liệu cụ thể (PDF vs CSV) do chef (subclass) quyết định.
Abstract Class CAN Have Constructor — Tại sao?
Nhiều người nghĩ abstract class không thể có constructor vì không thể instantiate. Sai!
Abstract class constructor dùng để làm gì?
abstract class Animal {
protected String name;
protected int age;
// Constructor của abstract class
public Animal(String name, int age) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.name = name;
this.age = age;
System.out.println("Animal constructor called");
}
public abstract void makeSound();
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // Gọi constructor của Animal
this.breed = breed;
System.out.println("Dog constructor called");
}
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
// Test
Dog dog = new Dog("Buddy", 3, "Golden Retriever");
// Output:
// Animal constructor called
// Dog constructor called
Lợi ích của constructor trong abstract class
- Validation logic tập trung: Kiểm tra điều kiện chung cho tất cả subclasses
- Initialize common state: Khởi tạo fields chung
- Enforce invariants: Đảm bảo object luôn ở trạng thái hợp lệ
- Code reuse: Tránh duplicate initialization code
Abstract class can have constructors, và chúng được gọi khi subclass instance được tạo (thông qua super() call).
Bẫy 1: Abstract class cannot be final
// ❌ Compile error! abstract và final mâu thuẫn nhau
public abstract final class Shape { }
// Lý do: abstract class được thiết kế để extend
// final class không thể extend
// → Không thể vừa abstract vừa final
Bẫy 2: Abstract method cannot be private
abstract class Test {
// ❌ Compile error! abstract method không thể private
private abstract void method();
// Lý do: abstract method phải override ở subclass
// private method không visible ở subclass
// → Không thể vừa abstract vừa private
}
Bẫy 3: Abstract method cannot be static
abstract class Test {
// ❌ Compile error! abstract method không thể static
public static abstract void method();
// Lý do: static method bị hide (không override)
// abstract method phải override
// → Không thể vừa abstract vừa static
}
Bẫy 4: Abstract method cannot be final
abstract class Test {
// ❌ Compile error! abstract method không thể final
public final abstract void method();
// Lý do: final method không thể override
// abstract method phải override
// → Không thể vừa abstract vừa final
}
Tóm tắt quy tắc:
Abstract method PHẢI:
✅ public hoặc protected
✅ non-static
✅ non-final
Abstract class CÓ THỂ:
✅ Có constructor
✅ Có concrete methods
✅ Có instance fields
✅ Có static members
Abstract class KHÔNG THỂ:
❌ Là final
❌ Instantiate trực tiếp
Abstraction vs Encapsulation
| Abstraction | Encapsulation |
|---|---|
| Ẩn complexity | Ẩn data |
| Tập trung vào what object does | Tập trung vào how object does it |
| Đạt được qua abstract class/interface | Đạt được qua access modifiers |
| Design level concept | Implementation level concept |
| Ví dụ: Abstract class, Interface | Ví dụ: private fields, public getters/setters |
Ví dụ kết hợp
// Abstraction - ẩn complexity
abstract class BankAccount {
// Encapsulation - ẩn data
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
}
// Encapsulation - controlled access
protected double getBalance() {
return balance;
}
protected void setBalance(double balance) {
this.balance = balance;
}
// Abstraction - hide complexity, define "what"
public abstract void withdraw(double amount);
public abstract double calculateInterest();
// Concrete method - shared behavior
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
Skeletal Implementation Pattern
Skeletal Implementation (Abstract Skeletal Class) là pattern mà Java Collections Framework sử dụng rộng rãi: tạo abstract class implement interface, cung cấp sẵn phần lớn implementation — subclass chỉ cần override vài method cốt lõi.
Tại sao cần Skeletal Implementation?
Interface định nghĩa contract, nhưng nếu interface có 10 methods mà bạn chỉ cần customize 2 — bạn vẫn phải implement cả 10. Skeletal class giải quyết bằng cách implement sẵn 8 methods còn lại.
Java Collections dùng pattern này
| Interface | Skeletal Class | Bạn chỉ cần override |
|---|---|---|
List | AbstractList | get(index), size() |
Set | AbstractSet | iterator(), size() |
Map | AbstractMap | entrySet() |
Collection | AbstractCollection | iterator(), size() |
Ví dụ: Custom List chỉ với 2 methods
import java.util.AbstractList;
// Tạo immutable list chỉ cần override get() và size()
public class FixedSizeList extends AbstractList<String> {
private final String[] data;
public FixedSizeList(String... items) {
this.data = items.clone();
}
@Override
public String get(int index) {
return data[index]; // Chỉ cần implement get()
}
@Override
public int size() {
return data.length; // Và size()
}
// contains(), indexOf(), toString(), iterator()...
// đều được AbstractList implement sẵn!
}
// Sử dụng
FixedSizeList colors = new FixedSizeList("Red", "Green", "Blue");
System.out.println(colors.contains("Green")); // true — AbstractList xử lý
System.out.println(colors.indexOf("Blue")); // 2 — AbstractList xử lý
System.out.println(colors); // [Red, Green, Blue]
Joshua Bloch khuyên: "Prefer skeletal implementation classes over interfaces" khi muốn giảm effort cho người implement. Đây là lý do Java cung cấp AbstractList, AbstractMap,... thay vì bắt bạn implement toàn bộ List, Map từ đầu.
Abstract class không có abstract method nào — Hợp lệ!
Một abstract class không bắt buộc phải có abstract method. Dùng để ngăn instantiation trực tiếp:
// Hợp lệ! Không có abstract method nào
abstract class BaseEntity {
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
// ... getters/setters
}
// BaseEntity entity = new BaseEntity(); // ❌ Compile error — không cho phép
class User extends BaseEntity { } // ✅ OK — phải extend
class Product extends BaseEntity { } // ✅ OK
Câu hỏi: Abstract class phải có ít nhất 1 abstract method?
Đáp án: Sai! Abstract class có thể có 0 abstract methods. Mục đích: ngăn instantiation trực tiếp, buộc phải extend.
Bài tập thực hành
Bài 1: Employee Management System
Tạo abstract class Employee:
- Abstract methods:
calculateSalary(),calculateBonus() - Concrete methods:
displayInfo(),promote() - Implement 3 subclasses:
Manager,Developer,Intern - Mỗi loại có công thức tính lương và bonus khác nhau
Bài 2: Document Processing
Tạo abstract class Document:
- Abstract methods:
open(),save(),close() - Template method:
process()gọi các abstract methods theo thứ tự - Implement:
PDFDocument,WordDocument,ExcelDocument - Mỗi loại có cách xử lý riêng
Bài 3: Notification System
Tạo abstract class Notification:
- Abstract methods:
validate(),send() - Template method:
sendNotification()validate trước khi send - Concrete method:
logNotification() - Implement:
EmailNotification,SMSNotification,PushNotification
Bài 4: Shape Calculator
Tạo abstract class Shape3D:
- Abstract methods:
calculateVolume(),calculateSurfaceArea() - Concrete method:
displayInfo() - Implement:
Sphere,Cube,Cylinder,Cone - Test với array of shapes
Tổng kết
- Abstraction ẩn complexity, chỉ hiện essential features
- Abstract class không thể instantiate, chứa abstract methods
- Abstract methods không có body, subclass phải implement
- Template Method Pattern định nghĩa skeleton, subclass fill in details
- Dùng abstract class khi có shared behavior và common implementation
- Abstract class có thể có constructor, fields, concrete methods
| Đặc điểm | Abstract Class |
|---|---|
| Instantiation | ❌ Không thể |
| Constructor | ✅ Có |
| Abstract methods | ✅ Có |
| Concrete methods | ✅ Có |
| Fields | ✅ Có |
| Inheritance | Chỉ 1 class cha |
| Access modifiers | Đầy đủ (public, protected, private) |
Abstraction là nền tảng quan trọng giúp code dễ maintain, extensible, và tuân theo nguyên tắc Open/Closed Principle!