+4

Object-oriented programming (lập trình hướng đối tượng)

Trong lập trình chúng ta có hai phương pháp lập trình chính là Lập trình hướng thủ tục (POP) và Lập trình hướng đối tượng (OOP). Nhưng đối với lập trình hướng thủ tục (POP) có khá nhiều vấn đề có thể xảy ra, đặc biệt là trong những dự án hay bài toán lớn như là khó bảo trì (Do code thường được viết thành các khối dài khó quản lý), Khó mở rộng (Do sự phức tạp trong chuyện thay đổi các phần của chương trình thì cũng sẽ thay đổi các phần liên quan), Khó tái sử dụng code(Code thường được viết cho một mục đích cụ thể và khó áp dụng cho các trường hợp khác.), … Vì thế OOP thường được sử dụng nhiều hơn và phổ biến hơn.

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

Hiểu đơn giản lập trình hướng đối tượng là một ký thuật, một phương pháp sử dụng các đối tượng (objects) làm nền để xây dựng lên ứng dụng.

Lập trình hướng đối tượng giải quyết được khá nhiều các vấn đề mà POP mang lại. Tổng quan về lợi ích của OOP có như sau:

  • Cách tiếp cận thực tế: OOP cho phép mô phỏng các thực thể và hành động trong đời thực thành mã code một cách trực quan. Ví dụ, ta có thể tạo lớp "Lập trình viên" với các phương thức "Phân tích yêu cầu", "Viết code" và "Triển khai sản phẩm", tương ứng với các hành động thực tế của một lập trình viên.
  • Dễ dàng mở rộng và bảo trì: OOP giúp code dễ dàng bảo trì và mở rộng hơn so với các phương pháp lập trình truyền thống. Nhờ tính mô đun hóa, code được chia thành các đối tượng độc lập, giúp việc sửa lỗi, bổ sung chức năng và tái sử dụng code trở nên dễ dàng hơn.
  • Code gọn gàng và sạch sẽ:OOP giúp code gọn gàng, dễ đọc và dễ hiểu hơn nhờ vào việc sử dụng các lớp, đối tượng và phương thức. Việc tổ chức code theo hướng đối tượng giúp giảm thiểu sự lặp lại và tăng tính nhất quán trong code.

2. Class và Object

Lớp (Class) và Đối tượng (Oject) trong OOP có thể coi như là một khung mẫu và những vật được sinh ra từ những khuôn mẫu trên.

Lớp (Class):

  • Là một khung mẫu để định nghĩa các đặc điểm và hành vi chung của một nhóm đối tượng trong thế giới thực.
  • Giống như một bản vẽ chi tiết mô tả cấu tạo và chức năng của một sản phẩm.
  • Có các thuộc tính (properties) và các phương thức (methods) tương ứng với thuộc tính và hành động thực tế bên ngoài

Thuộc tính (Property):

  • Là các đặc điểm của đối tượng được định nghĩa trong lớp.
  • Ví dụ: tên, tuổi, chuyên môn của một lập trình viên.

Phương thức (Method):

  • Là các hành vi mà đối tượng có thể thực hiện.
  • Ví dụ: lập trình, phân tích, thiết kế.

Đối tượng (Object):

  • Là một thể hiện cụ thể của lớp.
  • Giống như một sản phẩm hoàn chỉnh được tạo ra từ bản vẽ.

Ví dụ:

Lớp Developer (Lập trình viên):

  • Thuộc tính: tên, tuổi, chuyên môn.
  • Phương thức: lập trình, phân tích, thiết kế.

Các đối tượng (thể hiện) của lớp Developer:

  • BackendDeveloper (Lập trình viên back-end).
  • FrontendDeveloper (Lập trình viên front-end).
  • FullStackDeveloper (Lập trình viên full-stack).

3. Các Tính Chất trong OOP

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

Tính đóng gói (encapsulation) là một tính chất qua trọng trong lập trình hướng đối tượng (OOP) giúp che giấu các chi tiết triển khai bên trong của một đối tượng, chỉ cung cấp giao diện public để truy cập. Tính chất này giúp gom nhóm lại các thuộc tính (properties), phương thức (methods) và nhiều thành phần khác thành một đối tượng hay một đơn vị.

Tính đóng gói còn được hiểu là information hiding , đây cũng là một điều dễ nhầm lẫn với tính trừu tượng được đề cập ở phía sau.

Đối với Python, chúng ta có một quy ước theo cách đặt tên biến: 1 dấu các dưới để set một biến thành internal use (Khi này chúng ta vẫn có thể truy cập vào biến này nhưng bị cảnh báo), còn 2 dấu gạch dưới chân để set thnahf private (không thể phublic access)

class LopHoc:
    def __init__(self, ten_lop):
        self.__ten_lop = ten_lop
        self.__danh_sach_hoc_sinh = []  # Danh sách học sinh private
        self._diem_thi = {}  # Điểm thi protected

    def get_ten_lop(self):
        return self.__ten_lop

    def them_hoc_sinh(self, hoc_sinh):
        self.__danh_sach_hoc_sinh.append(hoc_sinh)

    def get_danh_sach_hoc_sinh(self):
        return self.__danh_sach_hoc_sinh

    def set_diem_thi(self, ma_hoc_sinh, diem_thi):
        self._diem_thi[ma_hoc_sinh] = diem_thi

    def get_diem_thi(self, ma_hoc_sinh):
        return self._diem_thi.get(ma_hoc_sinh)

3.2. Tính kế thừa (Inheritance)

Tính chất này có thể hiểu đơn giản là nó sẽ như việc thừa hưởng lại những gì mà người khác để lại. Kế thừa như là một cách để mà một lớp có thể thừa hưởng lại những thuộc tính method từ một lớp khác, từ đó ta có thể sử dụng hoặc là ghi đè (override) chúng.

Ví dụ đơn giản là có nhiều loại xe máy, chúng có nhiều đặc tính chung với nhau. Với những tính chất chung đó thì ta có thể để nhưng loại xe đó kế thừa chung 1 class cha là Motobike chứa các thuộc tính và phương thức chung thay vì phải viết lại.

Nhìn chung là với tính chất này sẽ giúp chúng ta tái sử dụng, tận dụng code có sẵn.

class MotoBike:
   def __init__(self, ten, hang_san_xuat, nam_san_xuat):
       self.ten = ten
       self.hang_san_xuat = hang_san_xuat
       self.nam_san_xuat = nam_san_xuat

   def chay(self):
       print(f"MotoBike {self.ten} đang chạy...")

   def dung(self):
       print(f"MotoBike {self.ten} đang dừng...")


class Honda(MotoBike):
   def __init__(self, ten, hang_san_xuat, nam_san_xuat, model):
       super().__init__(ten, hang_san_xuat, nam_san_xuat)
       self.model = model

   def chay(self):
       print(f"Honda {self.model} {self.ten} đang chạy...")

Ngoài ra trong tính kế thừa thì chúng còn có đơn kế thừa và đa kế thừa. Đơn kế thừa:

  • Cho phép một lớp kế thừa từ một lớp khác.

  • Lớp con kế thừa tất cả thuộc tính và phương thức public của lớp cha.

  • Ưu điểm:

      * Đơn giản, dễ hiểu và dễ sử dụng.
      * Giúp giảm thiểu code lặp lại.
      * Tăng khả năng tái sử dụng code.
    
  • Nhược điểm:

      * Hạn chế khả năng mở rộng.
      * Khó mô hình hóa các mối quan hệ phức tạp.
    

Đa kế thừa:

  • Cho phép một lớp kế thừa từ nhiều lớp khác.
  • Lớp con kế thừa tất cả thuộc tính và phương thức public của các lớp cha.
  • Ưu điểm: * Tăng khả năng mở rộng. * Cho phép mô hình hóa các mối quan hệ phức tạp.
  • Nhược điểm: * Phức tạp hơn đơn kế thừa. * Khó khăn trong việc giải quyết xung đột phương thức. * Khó bảo trì và sửa lỗi hơn.
class HinhChuNhat:
    def __init__(self, dai, rong):
        self.dai = dai
        self.rong = rong

    def tinh_dien_tich(self):
        return self.dai * self.rong

    def chu_vi(self):
        return 2 * (self.dai + self.rong)
class HinhVuong(HinhChuNhat):
    def __init__(self, canh):
        super().__init__(canh, canh)  # Kế thừa từ lớp cha

    def chu_vi(self):
        return 4 * self.canh  # Ghi đè phương thức chu_vi()

/code/ 
class Nguoi:
    def __init__(self, ten, tuoi):
        self.ten = ten
        self.tuoi = tuoi

    def thong_tin(self):
        print(f"Tên: {self.ten}")
        print(f"Tuổi: {self.tuoi}")

class SinhVien:
    def __init__(self, ten, tuoi, ma_sinh_vien):
        super().__init__(ten, tuoi)
        self.ma_sinh_vien = ma_sinh_vien

    def thong_tin_sinh_vien(self):
        print(f"Mã sinh viên: {self.ma_sinh_vien}")

class HocSinh(Nguoi, SinhVien):
    def __init__(self, ten, tuoi, ma_sinh_vien, lop_hoc):
        super().__init__(ten, tuoi, ma_sinh_vien)
        self.lop_hoc = lop_hoc

    def thong_tin_hoc_sinh(self):
        print(f"Lớp học: {self.lop_hoc}")

3.3. Tính đa hình (Polymorphism)

Tính đa hình cho phép cùng một phương thức được thực hiện theo các cách khác nhau tùy thuộc vào lớp con được sử dụng. Bởi lẽ dù có hai hay nhiều lớp sẽ có chung phương thức nhưng lại được implement theo các cách khác nhau – thực hiện 1 hành động theo nhiều cách khác nhau.

Ví dụ như ví dụ cùng là con vật có thể kêu nhưng từng con vật sẽ kêu theo một cách khác nhau.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError("Phương thức trừu tượng")

class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)  # Gọi phương thức khởi tạo của lớp cha

    def speak(self):
        return f"{super().speak()} Gâu gâu!"

class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)

    def speak(self):
        return f"{super().speak()} Meo meo!"

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

Tính trừu tượng cho phép tổng quát hóa một đối tượng. Nghĩa là ẩn đi những thông tin chi tiết bên trong, chỉ thể hiện ra những thông tin bên ngoài. Và nhìn vào thông tin bên ngoài đó ta có thể hiểu được đối tượng đó làm gì.

Giải thích dễ hiểu hơn nữa là giống như việc chúng ta làm quen tới mô hình nào đó, ta sẽ quan tâm tới đầu vào đầu ra của mô hình thay vì tìm hiểu luôn kiến trúc của mô hình.

class HinhHoc(ABC):
    @abstractmethod
    def tinh_dien_tich(self):
        pass

    @abstractmethod
    def chu_vi(self):
        pass

class HinhVuong(HinhHoc):
    def __init__(self, canh):
        self.canh = canh

    def tinh_dien_tich(self):
        return self.canh * self.canh

    def chu_vi(self):
        return 4 * self.canh

class HinhTron(HinhHoc):
    def __init__(self, ban_kinh):
        self.ban_kinh = ban_kinh

    def tinh_dien_tich(self):
        return pi * self.ban_kinh ** 2

    def chu_vi(self):
        return 2 * pi * self.ban_kinh

Reference


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí