Lập trình hướng đối tượng (Object-Oriented Programming – OOP) là một mô hình lập trình mạnh mẽ dựa trên khái niệm “đối tượng” và “lớp” để tạo ra các phần mềm có cấu trúc rõ ràng, dễ mở rộng và bảo trì. OOP giúp mô phỏng các đối tượng và mối quan hệ của chúng trong thế giới thực vào phần mềm. Bốn tính chất chính của lập trình hướng đối tượng bao gồm Tính đóng gói (Encapsulation), Tính kế thừa (Inheritance), Tính đa hình (Polymorphism), và Tính trừu tượng (Abstraction). Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết từng tính chất và vai trò của chúng trong OOP.

Tính đóng gói (Encapsulation)

Giải thích

Tính đóng gói là khái niệm mà các dữ liệu (thuộc tính) và phương thức (hành vi) của một đối tượng được đóng gói lại với nhau, giấu đi những thông tin chi tiết về cách đối tượng hoạt động bên trong và chỉ cho phép truy cập những gì cần thiết. Bằng cách giới hạn quyền truy cập từ bên ngoài vào dữ liệu của đối tượng, bạn có thể bảo vệ tính toàn vẹn của dữ liệu và ngăn ngừa việc thay đổi trực tiếp từ bên ngoài.

Các mức độ truy cập trong Java (private, protected, public, và default) là những công cụ quan trọng để thực hiện tính đóng gói.

Ví dụ

class Person {
    private String name;
    private int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter cho thuộc tính name
    public String getName() {
        return name;
    }

    // Setter cho thuộc tính name
    public void setName(String name) {
        this.name = name;
    }

    // Getter cho thuộc tính age
    public int getAge() {
        return age;
    }

    // Setter cho thuộc tính age
    public void setAge(int age) {
        if(age > 0) {
            this.age = age;
        }
    }
}

Ở đây, thuộc tính nameage của lớp Person được đóng gói và chỉ có thể được truy cập gián tiếp thông qua các phương thức getset. Điều này đảm bảo rằng các thuộc tính không thể bị thay đổi trực tiếp mà không qua kiểm tra.

Lợi ích

  • Bảo vệ dữ liệu: Giúp bảo vệ dữ liệu bên trong đối tượng khỏi những thay đổi không mong muốn hoặc truy cập không hợp lệ từ bên ngoài.
  • Dễ bảo trì: Bạn có thể thay đổi các chi tiết cài đặt bên trong lớp mà không ảnh hưởng đến mã bên ngoài lớp.

Tính kế thừa (Inheritance)

Giải thích

Tính kế thừa là tính chất cho phép một lớp (subclass) thừa hưởng các thuộc tính và phương thức từ một lớp khác (superclass). Subclass có thể sử dụng các thuộc tính và phương thức của superclass mà không cần phải viết lại chúng. Ngoài ra, subclass có thể mở rộng hoặc ghi đè (override) các phương thức của superclass để tạo ra hành vi mới.

Ví dụ

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}

Trong ví dụ này, lớp Dog kế thừa từ lớp Animal và ghi đè phương thức makeSound() để thay đổi hành vi của nó.

Lợi ích

  • Tái sử dụng mã: Kế thừa giúp bạn tái sử dụng mã đã viết ở lớp cha, giảm sự trùng lặp và giúp phát triển phần mềm nhanh hơn.
  • Dễ mở rộng: Cho phép bạn dễ dàng mở rộng các chức năng mà không cần thay đổi mã gốc của lớp cha.

Tính đa hình (Polymorphism)

Giải thích

Tính đa hình là khả năng cho phép các đối tượng của các lớp khác nhau được xử lý thông qua cùng một giao diện hoặc lớp cha mà không cần biết loại đối tượng cụ thể. Điều này có nghĩa là cùng một phương thức có thể hoạt động theo nhiều cách khác nhau tùy thuộc vào đối tượng cụ thể mà nó được gọi.

Có hai loại đa hình chính trong Java:

  1. Đa hình tại thời gian biên dịch (Compile-time Polymorphism): Còn gọi là đa hình tĩnh, được thực hiện thông qua quá tải phương thức (method overloading).
  2. Đa hình tại thời gian chạy (Runtime Polymorphism): Còn gọi là đa hình động, được thực hiện thông qua ghi đè phương thức (method overriding) và sử dụng kiểu tham chiếu của lớp cha để gọi các phương thức từ lớp con.

Ví dụ

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Dog();  // Đa hình động
        myAnimal.makeSound();         // Gọi phương thức makeSound() của Dog

        myAnimal = new Cat();         // Thay đổi đối tượng
        myAnimal.makeSound();         // Gọi phương thức makeSound() của Cat
    }
}

Trong ví dụ này, cùng một tham chiếu kiểu Animal có thể chứa các đối tượng của Dog hoặc Cat, và phương thức makeSound() sẽ hoạt động khác nhau dựa trên đối tượng cụ thể.

Lợi ích

  • Linh hoạt: Tính đa hình giúp chương trình linh hoạt và mở rộng hơn, cho phép sử dụng chung các phương thức nhưng vẫn đảm bảo hành vi tùy biến.
  • Dễ bảo trì: Bạn có thể thay đổi hành vi của một phương thức mà không ảnh hưởng đến các đoạn mã sử dụng chung phương thức đó.

Tính trừu tượng (Abstraction)

Giải thích

Tính trừu tượng là tính chất giúp ẩn đi các chi tiết cài đặt phức tạp của một đối tượng và chỉ hiển thị các tính năng cần thiết mà người dùng hoặc các đối tượng khác cần biết. Điều này có thể được thực hiện thông qua các lớp trừu tượng (abstract class) và giao diện (interface).

Tính trừu tượng giúp tập trung vào các thao tác quan trọng mà không cần quan tâm đến cách chúng được thực hiện bên trong. Người lập trình chỉ cần biết cách sử dụng mà không cần hiểu rõ toàn bộ chi tiết kỹ thuật.

Ví dụ

abstract class Animal {
    public abstract void makeSound();  // Phương thức trừu tượng
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

Trong ví dụ trên, lớp Animal là một lớp trừu tượng có phương thức makeSound() được định nghĩa trừu tượng. Các lớp con DogCat phải cài đặt chi tiết của phương thức này.

Lợi ích

  • Giảm độ phức tạp: Giúp người dùng tập trung vào các chức năng chính thay vì các chi tiết cài đặt phức tạp.
  • Dễ mở rộng: Tính trừu tượng giúp dễ dàng mở rộng và thay đổi các thành phần mà không ảnh hưởng đến phần còn lại của hệ thống.

Tóm lại, các tính chất của lập trình hướng đối tượng bao gồm Tính đóng gói, Tính kế thừa, Tính đa hình, và Tính trừu tượng. Những tính chất này giúp lập trình viên tạo ra các hệ thống phần mềm dễ quản lý, bảo trì, và mở rộng. Hiểu rõ và áp dụng tốt các tính chất này là chìa khóa để phát triển phần mềm mạnh mẽ và hiệu quả.