Chuyển tới nội dung chính

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.

Mục tiêu bài học

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
Mục đích của Abstraction
  • 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 MethodConcrete Method
Không có body (chỉ có signature)Có body (có implementation)
Phải được override bởi subclassCó thể override hoặc không
Chỉ tồn tại trong abstract classCó thể ở bất kỳ class nào
Dùng từ khóa abstractKhô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();
}
}
Quy tắc Abstract Class
  1. Abstract class không thể khởi tạo trực tiếp
  2. Abstract class có thể có constructor (dùng bởi subclass)
  3. Abstract class có thể có cả abstract và concrete methods
  4. Nếu class có ít nhất 1 abstract method → class phải là abstract
  5. Subclass phải implement tất cả abstract methods (hoặc tự nó cũng là abstract)
  6. Abstract methods không thểprivate, final, hoặc static

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 ClassInterface
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ùngIS-A relationship, shared codeContract, capability, multiple inheritance
extends/implementsextendsimplements

Decision Matrix: Dùng Abstract Class hay Interface?

Dùng Abstract Class khi:

  • 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");
}
}
💡 Cách nhớ Template Method Pattern

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

  1. Validation logic tập trung: Kiểm tra điều kiện chung cho tất cả subclasses
  2. Initialize common state: Khởi tạo fields chung
  3. Enforce invariants: Đảm bảo object luôn ở trạng thái hợp lệ
  4. Code reuse: Tránh duplicate initialization code
📖 Theo JLS §8.1.1.1

Abstract class can have constructors, và chúng được gọi khi subclass instance được tạo (thông qua super() call).

🔥 Bẫy OCP: Abstract Class Restrictions

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

AbstractionEncapsulation
Ẩn complexityẨn data
Tập trung vào what object doesTập trung vào how object does it
Đạt được qua abstract class/interfaceĐạt được qua access modifiers
Design level conceptImplementation level concept
Ví dụ: Abstract class, InterfaceVí 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

InterfaceSkeletal ClassBạn chỉ cần override
ListAbstractListget(index), size()
SetAbstractSetiterator(), size()
MapAbstractMapentrySet()
CollectionAbstractCollectioniterator(), 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]
Effective Java Item 20

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
OCP Trap

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

Key Points
  1. Abstraction ẩn complexity, chỉ hiện essential features
  2. Abstract class không thể instantiate, chứa abstract methods
  3. Abstract methods không có body, subclass phải implement
  4. Template Method Pattern định nghĩa skeleton, subclass fill in details
  5. Dùng abstract class khi có shared behaviorcommon implementation
  6. Abstract class có thể có constructor, fields, concrete methods
Đặc điểmAbstract Class
Instantiation❌ Không thể
Constructor✅ Có
Abstract methods✅ Có
Concrete methods✅ Có
Fields✅ Có
InheritanceChỉ 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!

Đọc thêm