Back to blog

Classes, Objects và Abstraction: Nền Tảng của OOP

oopclassesobjectsabstractioninterfacestypescriptpythonjava
Classes, Objects và Abstraction: Nền Tảng của OOP

Giới thiệu

Classes và objects là các khối xây dựng cơ bản của Lập trình Hướng Đối tượng. Hiểu cách thiết kế classes, tạo objects và sử dụng abstraction hiệu quả là điều cần thiết để viết code sạch, dễ bảo trì. Trong bài viết này, chúng ta sẽ khám phá các khái niệm này một cách sâu sắc và học khi nào nên sử dụng abstract classes so với interfaces.

Những gì bạn sẽ học

✅ Hiểu sự khác biệt giữa classes và objects
✅ Làm chủ abstraction với abstract classes và interfaces
✅ Biết khi nào dùng abstract classes vs interfaces
✅ Áp dụng constructors, static members và access modifiers đúng cách
✅ Xây dựng class hierarchies được thiết kế tốt


Classes: Bản Thiết Kế

Class là một bản thiết kế (blueprint) hoặc template định nghĩa cấu trúc và hành vi của objects. Nó chỉ định:

  • Dữ liệu gì object sẽ chứa (properties/attributes)
  • Hành động gì object có thể thực hiện (methods/functions)

Hãy nghĩ về class như bản vẽ kiến trúc cho một ngôi nhà. Bản vẽ mô tả ngôi nhà nên được xây như thế nào, nhưng nó không phải là ngôi nhà.

Cấu trúc của một Class

TypeScript:

class Car {
  // Properties (attributes) - dữ liệu
  brand: string;
  model: string;
  year: number;
  private mileage: number = 0;
 
  // Constructor - khởi tạo
  constructor(brand: string, model: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }
 
  // Methods (behaviors) - hành động
  drive(distance: number): void {
    this.mileage += distance;
    console.log(`Đang lái ${distance} km. Tổng: ${this.mileage} km`);
  }
 
  getInfo(): string {
    return `${this.year} ${this.brand} ${this.model}`;
  }
 
  getMileage(): number {
    return this.mileage;
  }
}

Python:

class Car:
    def __init__(self, brand: str, model: str, year: int):
        self.brand = brand
        self.model = model
        self.year = year
        self._mileage = 0
 
    def drive(self, distance: int) -> None:
        self._mileage += distance
        print(f"Đang lái {distance} km. Tổng: {self._mileage} km")
 
    def get_info(self) -> str:
        return f"{self.year} {self.brand} {self.model}"
 
    @property
    def mileage(self) -> int:
        return self._mileage

Objects: Instances của Classes

Object là một instance cụ thể được tạo từ một class. Nếu class là bản thiết kế, object là ngôi nhà thực sự được xây từ bản thiết kế đó.

Tạo Objects

// Tạo objects (instances) từ class Car
const myCar = new Car("Toyota", "Camry", 2023);
const yourCar = new Car("Honda", "Civic", 2022);
 
// Mỗi object có dữ liệu riêng
console.log(myCar.getInfo());   // "2023 Toyota Camry"
console.log(yourCar.getInfo()); // "2022 Honda Civic"
 
// Hành động chỉ ảnh hưởng object cụ thể đó
myCar.drive(100);
console.log(myCar.getMileage());   // 100
console.log(yourCar.getMileage()); // 0 (không bị ảnh hưởng)

Class vs Object: Sự Khác Biệt Chính

Khía cạnhClassObject
Định nghĩaBản thiết kế/templateInstance của class
Tồn tạiTồn tại lúc compileTồn tại lúc runtime
Bộ nhớKhông cấp phát bộ nhớBộ nhớ được cấp phát cho dữ liệu
TạoĐịnh nghĩa một lầnTạo nhiều lần

Constructors: Khởi Tạo Object

Constructor là method đặc biệt khởi tạo object khi nó được tạo. Nó thiết lập trạng thái ban đầu của object.

class User {
  private id: string;
  private email: string;
  private name: string;
 
  constructor(email: string, name: string) {
    this.id = `user_${Date.now()}`;
    this.email = email;
    this.name = name;
  }
 
  // Static factory method - thay thế cho constructor
  static createGuest(): User {
    return new User("[email protected]", "Guest User");
  }
 
  static createAdmin(email: string): User {
    const admin = new User(email, "Admin");
    return admin;
  }
}
 
// Sử dụng
const user1 = new User("[email protected]", "John Doe");
const guest = User.createGuest();
const admin = User.createAdmin("[email protected]");

Static vs Instance Members

Instance Members

  • Thuộc về mỗi object instance
  • Mỗi object có bản sao riêng
  • Truy cập thông qua object reference

Static Members

  • Thuộc về class
  • Chia sẻ giữa tất cả instances
  • Truy cập thông qua tên class
class Counter {
  // Static property - chia sẻ giữa tất cả instances
  private static totalCount: number = 0;
 
  // Instance property - riêng cho mỗi object
  private count: number = 0;
 
  constructor() {
    Counter.totalCount++;
  }
 
  // Instance method
  increment(): void {
    this.count++;
  }
 
  getCount(): number {
    return this.count;
  }
 
  // Static method
  static getTotalCount(): number {
    return Counter.totalCount;
  }
}
 
const counter1 = new Counter();
const counter2 = new Counter();
 
counter1.increment();
counter1.increment();
 
console.log(counter1.getCount());      // 2 (riêng instance)
console.log(Counter.getTotalCount());  // 2 (chia sẻ)

Abstraction: Ẩn Sự Phức Tạp

Abstraction là về việc ẩn các chi tiết implementation phức tạp và chỉ hiển thị các tính năng thiết yếu. Nó trả lời câu hỏi: "Object này làm gì?" mà không tiết lộ "Nó làm như thế nào?"

Hai Công Cụ cho Abstraction

  1. Abstract Classes: Implementation một phần với một số abstract methods
  2. Interfaces: Contract thuần túy không có implementation

Abstract Classes

Abstract class là class không thể được khởi tạo trực tiếp. Nó đóng vai trò là base cho các classes khác.

Khi nào sử dụng Abstract Classes

  • Bạn muốn chia sẻ code giữa các classes liên quan chặt chẽ
  • Bạn mong subclasses có common methods hoặc fields
  • Bạn cần constructors hoặc non-public members
abstract class Vehicle {
  protected brand: string;
  protected model: string;
  protected isRunning: boolean = false;
 
  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }
 
  // Concrete method - implementation chung
  startEngine(): void {
    if (this.isRunning) {
      console.log("Động cơ đã chạy rồi");
      return;
    }
    this.isRunning = true;
    console.log(`${this.brand} ${this.model} đã khởi động`);
  }
 
  stopEngine(): void {
    this.isRunning = false;
    console.log(`${this.brand} ${this.model} đã dừng`);
  }
 
  // Abstract methods - phải được implement bởi subclasses
  abstract calculateFuelEfficiency(): number;
  abstract getVehicleType(): string;
}
 
class Car extends Vehicle {
  private mpg: number;
 
  constructor(brand: string, model: string, mpg: number) {
    super(brand, model);
    this.mpg = mpg;
  }
 
  calculateFuelEfficiency(): number {
    return this.mpg;
  }
 
  getVehicleType(): string {
    return "Ô tô";
  }
}
 
class Motorcycle extends Vehicle {
  private engineCC: number;
 
  constructor(brand: string, model: string, engineCC: number) {
    super(brand, model);
    this.engineCC = engineCC;
  }
 
  calculateFuelEfficiency(): number {
    return Math.round(50 + (1000 - this.engineCC) / 20);
  }
 
  getVehicleType(): string {
    return "Xe máy";
  }
}
 
// Sử dụng
function printVehicleInfo(vehicle: Vehicle): void {
  console.log(`Loại: ${vehicle.getVehicleType()}`);
  console.log(`Hiệu suất nhiên liệu: ${vehicle.calculateFuelEfficiency()} MPG`);
  vehicle.startEngine();
  vehicle.stopEngine();
}
 
const car = new Car("Toyota", "Camry", 32);
const motorcycle = new Motorcycle("Honda", "CBR", 600);
 
printVehicleInfo(car);
printVehicleInfo(motorcycle);

Interfaces: Contract Thuần Túy

Interface định nghĩa contract mà classes phải tuân theo. Nó chỉ định methods nào class phải implement, nhưng không phải cách thực hiện.

Khi nào sử dụng Interfaces

  • Bạn muốn định nghĩa capability có thể thêm vào bất kỳ class nào
  • Các classes không liên quan cần implement cùng behavior
  • Bạn muốn hỗ trợ multiple inheritance of type
// Interface - định nghĩa contract
interface ILogger {
  log(message: string): void;
  error(message: string): void;
  warn(message: string): void;
}
 
interface IConfigurable {
  configure(options: Record<string, any>): void;
}
 
// Class có thể implement nhiều interfaces
class ConsoleLogger implements ILogger, IConfigurable {
  private prefix: string = "[LOG]";
 
  configure(options: Record<string, any>): void {
    if (options.prefix) {
      this.prefix = options.prefix;
    }
  }
 
  log(message: string): void {
    console.log(`${this.prefix} [INFO] ${message}`);
  }
 
  error(message: string): void {
    console.error(`${this.prefix} [ERROR] ${message}`);
  }
 
  warn(message: string): void {
    console.warn(`${this.prefix} [WARN] ${message}`);
  }
}
 
class FileLogger implements ILogger, IConfigurable {
  private filePath: string = "/var/log/app.log";
 
  configure(options: Record<string, any>): void {
    if (options.filePath) {
      this.filePath = options.filePath;
    }
  }
 
  log(message: string): void {
    console.log(`Ghi vào ${this.filePath}: [INFO] ${message}`);
  }
 
  error(message: string): void {
    console.log(`Ghi vào ${this.filePath}: [ERROR] ${message}`);
  }
 
  warn(message: string): void {
    console.log(`Ghi vào ${this.filePath}: [WARN] ${message}`);
  }
}
 
// Sử dụng interface cho dependency injection
class UserService {
  private logger: ILogger;
 
  constructor(logger: ILogger) {
    this.logger = logger;
  }
 
  createUser(email: string): void {
    this.logger.log(`Đang tạo user: ${email}`);
    // Logic tạo user
    this.logger.log(`User đã được tạo: ${email}`);
  }
}
 
// Dễ dàng đổi implementations
const consoleLogger = new ConsoleLogger();
const fileLogger = new FileLogger();
 
const devService = new UserService(consoleLogger);
const prodService = new UserService(fileLogger);

Abstract Classes vs Interfaces: So Sánh

Khía cạnhAbstract ClassInterface
ImplementationCó thể có concrete methodsKhông có implementation
StateCó thể có instance fieldsKhông có state
ConstructorsCó thể có constructorsKhông có constructors
InheritanceSingle inheritanceMultiple implementation
Quan hệQuan hệ "is-a"Capability "can-do"

Hướng Dẫn Quyết Định

Dùng Abstract Class khi:

  • Classes chia sẻ common base với shared implementation
  • Bạn cần constructors hoặc protected members
  • Có clear "is-a" hierarchy

Dùng Interface khi:

  • Nhiều classes không liên quan cần cùng capability
  • Bạn muốn định nghĩa contract/API
  • Bạn cần multiple inheritance

Access Modifiers

ModifierCùng ClassSubclassClasses Khác
public✅✅✅
protected✅✅❌
private✅❌❌
class Employee {
  public name: string;           // Truy cập mọi nơi
  protected department: string;  // Truy cập trong class và subclasses
  private salary: number;        // Chỉ truy cập trong class này
  readonly id: string;           // Không thể thay đổi sau khởi tạo
 
  constructor(name: string, department: string, salary: number) {
    this.id = `EMP_${Date.now()}`;
    this.name = name;
    this.department = department;
    this.salary = salary;
  }
}
 
class Manager extends Employee {
  getManagerInfo(): string {
    // Có thể truy cập public và protected từ parent
    return `${this.name} trong phòng ${this.department}`;
    // this.salary - Lỗi: private
  }
}

Ví Dụ Thực Tế: Hệ Thống Sản Phẩm E-commerce

// Interfaces cho capabilities
interface IPriceable {
  getPrice(): number;
  applyDiscount(percentage: number): void;
}
 
interface IShippable {
  getWeight(): number;
  getShippingCost(destination: string): number;
}
 
// Abstract base class
abstract class Product implements IPriceable, IShippable {
  protected name: string;
  protected basePrice: number;
  protected discount: number = 0;
  protected weight: number;
 
  constructor(name: string, basePrice: number, weight: number) {
    this.name = name;
    this.basePrice = basePrice;
    this.weight = weight;
  }
 
  getPrice(): number {
    return this.basePrice * (1 - this.discount / 100);
  }
 
  applyDiscount(percentage: number): void {
    this.discount = percentage;
  }
 
  getWeight(): number {
    return this.weight;
  }
 
  getShippingCost(destination: string): number {
    const baseRate = destination === "nội địa" ? 20000 : 100000;
    return baseRate + this.weight * 5000;
  }
 
  abstract getDescription(): string;
  abstract getCategory(): string;
}
 
// Concrete implementations
class Electronics extends Product {
  private warranty: number;
  private brand: string;
 
  constructor(name: string, price: number, weight: number, brand: string, warranty: number) {
    super(name, price, weight);
    this.brand = brand;
    this.warranty = warranty;
  }
 
  getDescription(): string {
    return `${this.brand} ${this.name} - Bảo hành ${this.warranty} tháng`;
  }
 
  getCategory(): string {
    return "Điện tử";
  }
}
 
class Clothing extends Product {
  private size: string;
  private material: string;
 
  constructor(name: string, price: number, weight: number, size: string, material: string) {
    super(name, price, weight);
    this.size = size;
    this.material = material;
  }
 
  getDescription(): string {
    return `${this.name} - Size ${this.size}, ${this.material}`;
  }
 
  getCategory(): string {
    return "Thời trang";
  }
}
 
// Shopping cart
class ShoppingCart {
  private items: { product: Product; quantity: number }[] = [];
 
  addItem(product: Product, quantity: number = 1): void {
    this.items.push({ product, quantity });
    console.log(`Đã thêm ${quantity}x ${product.getDescription()}`);
  }
 
  getTotal(): number {
    return this.items.reduce(
      (total, item) => total + item.product.getPrice() * item.quantity,
      0
    );
  }
 
  displayCart(): void {
    console.log("\n=== Giỏ Hàng ===");
    this.items.forEach((item) => {
      console.log(`${item.quantity}x ${item.product.getDescription()}`);
      console.log(`   Giá: ${item.product.getPrice().toLocaleString()}đ`);
    });
    console.log(`\nTổng: ${this.getTotal().toLocaleString()}đ`);
  }
}
 
// Sử dụng
const laptop = new Electronics("MacBook Pro", 45000000, 2.1, "Apple", 24);
const shirt = new Clothing("Áo Polo", 599000, 0.3, "M", "100% cotton");
 
laptop.applyDiscount(10);
 
const cart = new ShoppingCart();
cart.addItem(laptop, 1);
cart.addItem(shirt, 2);
cart.displayCart();

Tổng Kết

Classes và Objects

Khái niệmĐịnh nghĩa
ClassBản thiết kế định nghĩa cấu trúc và hành vi
ObjectInstance được tạo từ class với dữ liệu riêng
ConstructorMethod đặc biệt khởi tạo object
StaticMembers thuộc về class, không phải instances
InstanceMembers thuộc về mỗi object

Công Cụ Abstraction

Công cụSử dụng khi
Abstract ClassChia sẻ code giữa classes liên quan, cần constructors/state
InterfaceĐịnh nghĩa contracts, cho phép multiple inheritance

Best Practices

  1. Giữ classes tập trung: Một trách nhiệm cho mỗi class
  2. Dùng tên có ý nghĩa: Classes là danh từ, methods là động từ
  3. Ưu tiên composition: Dùng interfaces cho flexibility
  4. Ẩn implementation: Chỉ expose những gì cần thiết

Tiếp Theo Là Gì?

  1. Encapsulation và Information Hiding: Đi sâu vào bảo vệ dữ liệu
  2. Inheritance và Composition: Chọn cách tiếp cận đúng
  3. Polymorphism và Interfaces: Design patterns linh hoạt
  4. SOLID Principles: Thiết kế OOP sạch

Bài Tập Thực Hành

Bài Tập 1: Shape Hierarchy

Tạo abstract class Shape với concrete Circle, Rectangle, Triangle. Bao gồm:

  • Abstract method calculateArea()
  • Abstract method calculatePerimeter()
  • Concrete method getDescription()

Bài Tập 2: Payment System

Thiết kế hệ thống thanh toán với:

  • Interface IPaymentMethod với pay() và refund()
  • Abstract class BasePayment với common validation
  • Concrete classes: CreditCard, PayPal, BankTransfer

Có câu hỏi hoặc feedback? Đừng ngại liên hệ tại [email protected] hoặc kết nối với tôi trên X.

Chúc bạn code vui vẻ! 🚀

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.