fbpx

Lập trình hướng đối tượng là gì? Toàn bộ chi tiết về OOP

Lập trình hướng đối tượng là gì

Lập trình hướng đối tượng (tiếng Anh: Object-oriented programming, viết tắt: OOP) là một kỹ thuật lập trình được sử dụng phổ biến nhất hiện nay. OOP còn được xem là một cách viết code tiêu chuẩn của hầu hết các lập trình viên.

Ưu điểm của lập trình hướng đối tượng (OOP)

  • OOP giúp chương trình chạy nhanh hơn và dễ thực thi hơn
  • OOP cung cấp một cấu trúc hệ thống rõ ràng
  • OOP giúp code tuân theo nguyên lý DRY “Don’t Repeat Yourself”. Do đó việc bảo trì, chỉnh sửa và debug code trở nên dễ dàng hơn
  • OOP tăng khả năng tái sử dụng code, vì vậy chương trình sẽ ít code hơn và kéo theo vòng đời phát triển phần mềm ngắn hơn.

Tip: Nguyên lý “Don’t Repeat Yourself” (DRY) giúp giảm việc lặp lại code. Chúng ta nên tách những đoạn code có chung logic, đặt chúng vào chung một nơi để có thể tái sử dụng thay vì viết lại đoạn code tương tự.

1. Class và Object là gì?

Class (Lớp) và object (đối tượng) là hai khái niệm chính trong lập trình hướng đối tượng.

Hãy nhìn vào ví dụ bên dưới để thấy sự khác nhau giữa class và object:

Ví dụ Class và Object
Ví dụ Class và Object

Một ví dụ khác:

Ví dụ Class và Object
Ví dụ Class và Object

Ta có thể coi class như là một template (khuôn mẫu) cho nhiều object, và một object như là một instance (thực thể) của class.

Khi một object được tạo ra, nó kế thừa tất cả variable (biến số) và method (phương thức) của class tương ứng.

1.1 Khởi tạo Class và Object

Tạo một class Car với variable x:

public class Car {
  int x = 5;
}

Khởi tạo một object có tên là audi và in ra giá trị x:

public class Main {
  public static void main(String[] args) {
    Car audi = new Car();
    System.out.println(audi.x);
  }
}

Khởi tạo hai object từ class Car:

public class Main {
  public static void main(String[] args) {
    Car audi = new Car(); // Object 1
    Car vinfast = new Car(); // Object 2
    System.out.println(audi.x);
    System.out.println(vinfast.x);
  }
}

1.2 Class Attribute

Ở những ví dụ trước đó, chúng ta sử dụng định nghĩa variable (biến số) để gọi x. Bạn có thể gọi x là attribute (thuộc tính) của class. Hoặc bạn cũng có thể nói attribute là variable bên trong class.

Khởi tạo một class Car có hai attribute xy:

public class Car {
  int x = 5;
  int y = 3;
}

field cũng là một tên gọi khác của attribute

Bạn có thể truy cập attribute thông qua việc khởi tạo object

public class Main {
  public static void main(String[] args) {
    Car audi = new Car();
    System.out.println(audi.x);
  }
}

Bạn cũng có thể chỉnh sửa giá trị của attribute:

public class Main {
  public static void main(String[] args) {
    Car audi = new Car();
    audi.x = 40; // gán giá trị x = 40
    System.out.println(audi.x);
  }
}

Nếu bạn tạo ra nhiều object từ một class, bạn có thể thay đổi giá trị của attribute trong một object bất kỳ mà không làm thay đổi giá trị của attribute trong các object còn lại.

Ví dụ, khi gán giá trị của x trong audi bằng 25 thì giá trị của x trong vinfast không thay đổi:

public class Car {
  int x = 5;
  int y = 3;
}

public class Main {
  public static void main(String[] args) {
    Car audi = new Car(); // Object 1
    Car vinfast = new Car(); // Object 2
    audi.x = 25;
    System.out.println(audi.x); // Kết quả 25
    System.out.println(vinfast.x); // Kết quả 5
  }
}

1.3 Class Method

Method (phương thức) được khai báo bên trong class, method được dùng để mô tả một hành động cụ thể.

Tạo một method myMethod trong Car:

public class Car {
  public void myMethod() {
    System.out.println("Hello World!");
  }
}

myMethod() in ra đoạn text Hello World khi nó được gọi. Bên dưới là ví dụ gọi một method:

public class Main {
  public static void main(String[] args) {
    Car audi = new Car();
    audi.myMethod(); // Kết quả là "Hello World"
  }
}

1.4 Constructor

Constructor là một method đặc biệt được dùng để khởi tạo object. Constructor được thực thi khi một object được tạo ra. Constructor còn được dùng để thiết lập giá trị ban đầu cho các attribute của object:

public class Car {
  int x;

  // Tạo constructor
  public Car() {
    x = 5;  // Thiết lập giá trị ban đầu cho attribute x
  }
}

public class Main {
  public static void main(String[] args) {
    Car audi = new Car(); // Constructor được thực thi
    System.out.println(audi.x); // In ra giá trị của x là 5
  }
}

Lưu ý rằng constructor phải trùng với tên class, và không có return type (ví dụ như là void).

Cũng lưu ý rằng constructor được thực thi khi khởi tạo object.

Tất cả class điều có default constructor: Nếu bạn không tạo constructor bên trong class, thì class sẽ dùng default constructor. Tuy nhiên, sau đó bạn không thể thiết lập giá trị mặc định cho các attribute trong class.

Constructors có thể chứa các parameters (tham số), những parameters này được dùng để khởi tạo giá trị ban đầu cho các attribute.

Theo ví dụ bên dưới, constructor có parameter int y. Bên trong constructor, chúng ta khởi tạo giá trị của x bằng y (x=y). Khi chúng ta gọi constructor, chúng ta truyền một parameter (5) và x sẽ được gán giá trị bằng 5:

public class Car {
  int x;

  public Car(int y) {
    x = y;
  }
}

public class Main {
  public static void main(String[] args) {
    Car audi = new Car(5);
    System.out.println(audi.x); // Kết quả: 5
  }
}

1.5 Modifiers

Có lẽ bạn đã khá quen thuộc với từ khoá public, từ khoá này xuất hiện hầu hết trong tất cả các ví dụ của chúng ta:

Từ khoá public là một access modifier, nghĩa là nó được sử dụng để thiết lập mức độ truy cập cho class, attribute, method và constructor.

Chúng ta chia modifier thành hai nhóm:

  • Access Modifiers – kiểm soát mức độ truy cập
  • Non-Access Modifiers – không kiểm soát mức độ truy cập, nhưng có chức năng khác

Access Modifiers

Đối với class, bạn có thể dùng các modifiers như public hoặc default:

  • public: Class có thể được truy cập bởi các class khác.
  • default: Class chỉ được truy cập bởi các class cùng chung package (Định nghĩa này chỉ đúng đối với Java, tùy vào mỗi ngôn ngữ khác nhau thì sẽ có các định nghĩa khác nhau). Điều này được áp dụng khi bạn không chỉ định một modifier.

Đối với attribute, method và constructor, bạn có thể dùng một trong các modifiers bên dưới:

  • public: Code được truy cập bởi tất cả các class.
  • private: Code chỉ được truy cập bên trong class.
  • default: Code chỉ được truy cập khi cùng một package (Định nghĩa này chỉ đúng đối với Java, tùy vào mỗi ngôn ngữ khác nhau thì sẽ có các định nghĩa khác nhau). Điều này được áp dụng khi bạn không chỉ định một modifier.
  • protected: Code chỉ được truy cập khi cùng một package và subclass (Định nghĩa subclass sẽ được mô tả ở nội dung bên dưới).

Non-Access Modifiers

Đối với class, bạn có thể dùng các modifiers như final hoặc abstract:

  • final: Class không thể được kế thừa bởi các class khác.
  • abstract: Class không được dùng để tạo object (Định nghĩa abstract class sẽ được mô tả ở nội dung bên dưới). Để sử dụng abstract class, nó phải được kế thừa từ class khác.

Đối với attributes và methods, bạn có thể dùng một trong các modifiers bên dưới:

  • final: Attribute và method không thể được overridden(ghi đè) hoặc chỉnh sửa.
  • static: Attribute và method thuộc về class, chứ không phải thuộc về object.
  • abstract: Chỉ được sử dụng bên trong abstract class và các method bên trong abstract class. Abstract method không có body, ví dụ abstract void run();. Body của abstract method chỉ được định nghĩa bởi subclass sau khi kế thừa (Định nghĩa kế thừa sẽ được mô tả ở nội dung bên dưới).

2. Bốn tính chất của lập trình hướng đối tượng

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

Encapsulation đảm bảo dữ liệu nhạy cảm bị ẩn đi từ phía người dùng. Để thực hiện điều này, bạn phải:

  • Khai báo private cho các variable/attribute.
  • Khai báo public cho các getset method để truy cập và chỉnh sửa giá trị của các private variable/attribute.

Get và Set

Ở nội dung trước, bạn đã biết rằng private attribute chỉ có thể được truy cập bên trong class. Tuy nhiên, chúng ta có thể truy cập bằng cách khai báo getset method.

get method trả về giá trị của attribute, và set method dùng để gán giá trị cho attribute.

public class Person {
  private String name; // private = restricted access

  // Getter
  public String getName() {
    return name;
  }

  // Setter
  public void setName(String newName) {
    this.name = newName;
  }
}

Lợi ích của Encapsulation

  • Kiểm soát attribute và method tốt hơn.
  • Linh động: lập trình viên có thể thay đổi một phần code mà không ảnh hưởng đến những phần code còn lại.
  • Tăng tính bảo mật cho dữ liệu.

2.2 Inheritance (Tính kế thừa)

Có thể kế thừa toàn bộ attribute và method từ một class sang một class khác. Chúng ta chia khái niệm inheritance thành hai nhóm:

  • subclass (class con) – là class kế thừa class khác.
  • superclass (class cha) – là class được kế thừa.

Để kế thừa, chúng ta sử dụng từ khoá extends (đối với Java).

class Vehicle {
  protected String brand = "Ford";        // Vehicle attribute
  public void honk() {                    // Vehicle method
    System.out.println("Tuut, tuut!");
  }
}

class Car extends Vehicle {
  private String modelName = "Mustang";    // Car attribute
}

class Main {
  public static void main(String[] args) {

    // khởi tạo object myCar
    Car myCar = new Car();

    // kết quả: Tuut, tuut!
    myCar.honk();

    // kết quả: Ford Mustang
    System.out.println(myCar.brand + " " + myCar.modelName);
  }
}

Nếu chúng ta set attribute brand trong Vehicle thành private, class Car sẽ không thể truy cập nó.

Lợi ích của Inheritance

Tăng tính tái sử dụng code: sử dụng lại các attribute và method của class có sẵn khi bạn muốn tạo một class mới.

2.3 Polymorphism (Tính đa hình)

Như đã nói ở trên, Inheritance là kế thừa các attribute và method từ một class sang một class khác. Còn Polymorphism là sử dụng một method để thể hiện nhiều chức năng khác nhau.

Ví dụ, Animalsuperclass có method animalSound(). Subclass của AnimalPig, Dog.

class Animal {
  public void animalSound() {
    System.out.println("The animal makes a sound");
  }
}

class Pig extends Animal {
  public void animalSound() {
    System.out.println("The pig says: wee wee");
  }
}

class Dog extends Animal {
  public void animalSound() {
    System.out.println("The dog says: bow wow");
  }
}

Bây giờ chúng ta sẽ tạo object Pig và object Dog, sau đó gọi method animalSound() của 2 object:

class Main {
  public static void main(String[] args) {
    Animal myAnimal = new Animal();
    Animal myPig = new Pig();
    Animal myDog = new Dog();
    myAnimal.animalSound(); // Kết quả: The animal makes a sound
    myPig.animalSound(); // Kết quả: The pig says: wee wee
    myDog.animalSound(); // Kết quả: The dog says: bow wow
  }
}

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

Abstraction là quá trình ẩn các chi tiết cụ thể và hiển thị những thông tin cần thiết đến người dùng.

Abstraction có thể được thực hiện thông qua abstract classe hoặc interface.

Từ khoá abstract là một non-access modifier, được sử dụng cho class và method:

  • Abstract class: là một class đặc biệt không thể được dùng để khởi tạo object (Để sử dụng được abstract class, nó phải được kế thừa từ một class khác).
  • Abstract method: chỉ được sử dụng trong abstract class, và nó không có body. Body chỉ được khai báo trong subclass.

Trong một abstract class có thể có cả abstract method và các method thông thường khác.

abstract class Animal {
  public abstract void animalSound();
  public void sleep() {
    System.out.println("Zzz");
  }
}

Ở ví dụ trên, chúng ta không thể khởi tạo một đối tượng từ class Animal. Để sử dụng abstract class, nó phải được kế thừa từ một class khác.

// Abstract class
abstract class Animal {
  public abstract void animalSound();
  public void sleep() {
    System.out.println("Zzz");
  }
}

class Pig extends Animal {
  public void animalSound() {
    System.out.println("The pig says: wee wee");
  }
}

class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig();
    myPig.animalSound(); // Kết quả: The pig says: wee wee
    myPig.sleep(); // Kết quả: Zzz
  }
}

Một cách khác để thực hiện abstraction là dùng interface.

// interface
interface Animal {
  public void animalSound();
  public void run();
}

class Pig implements Animal {
  public void animalSound() {
    System.out.println("The pig says: wee wee");
  }

  public void sleep() {
    System.out.println("Zzz");
  }
}

class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig();
    myPig.animalSound(); // Kết quả: The pig says: wee wee
    myPig.sleep(); // Kết quả: Zzz
  }
}

Những lưu ý khi sử dụng Interface:

  • Như abstract class, interface không thể được dùng để khởi tạo object.
  • Method trong interface không có body – body chỉ được định nghĩa trong class implement interface.
  • Interface không có constructor.

Lợi ích khi sử dụng Interface

Sử dụng interface để thực hiện việc bảo mật – ẩn các chi tiết không cần thiết và chỉ hiển thị những chi tiết quan trọng của object.

3. Kết luận

Hi vọng sau bài viết này, các bạn sẽ biết rõ hơn về lập trình hướng đối tượng là gì và có thể áp dụng kỹ thuật này để xây dựng dự án của mình tối ưu và hiệu quả hơn hoặc củng cố kiến thức trước các buổi phỏng vấn.

Khóa Học Lập Trình Cơ Bản

Nếu bạn đang làm trái ngành và có dự định chuyển sang ngành lập trình.

Đây là Khóa Học Lập Trình Cơ Bản từ con số 0 kéo dài trong vòng 1 tháng, rất phù hợp với những bạn muốn kiểm chứng xem mình có phù hợp với nghề lập trình hay không.

Tùng ViO

Tùng ViO

Mình là Tùng ViO, hiện tại đang là Founder và cũng là giảng viên tại LetDiv. Rất hân hạnh được làm quen với bạn!