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

Cơ bản về lập trình hướng đối tượng

1. Lập trình hướng đối tượng (object oriented programming - OOP) là gì?

Lập trình hướng đối tượng (object oriented programming – OOP) là một trong những kỹ thuật lập trình rất quan trọng và sử dụng nhiều hiện nay, cho phép lập trình viên tạo ra các đối tượng trong code, trừu tượng hoá các đối tượng trong thực tế.

Đối tượng

Một đối tượng bao gồm 2 thông tin: thuộc tính và phương thức.

  • Thuộc tính: là những thông tin, đặc điểm của đối tượng. Ví dụ: con mèo có mắt, mũi, chân, đuôi...

  • Phương thức: là những thao tác, hành động mà đối tượng đó có thể thực hiện. Ví dụ: một con mèo có thể kêu, chạy, ăn, uống...

Lớp

Một lớp là một kiểu dữ liệu bao gồm các thuộc tính và các phương thức được định nghĩa từ trước. Đây là sự trừu tượng hóa của đối tượng. Khác với kiểu dữ liệu thông thường, một lớp là một đơn vị (trừu tượng) bao gồm sự kết hợp giữa các phương thức và các thuộc tính. Hiểu nôm na hơn là các đối tượng có các đặc tính tương tự nhau được gom lại thành một lớp đối tượng.

Sự khác nhau giữa đối tượng và lớp

Lớp - có thể hiểu nó như là khuôn mẫu, đối tượng - một thực thể thể hiện dựa trên khuôn mẫu đó. Ví dụ: lại nói về mèo, có thể hiểu là class (lớp) mèo có:

Các thông tin, đặc điểm: 4 chân, 2 mắt, có đuôi, có chiều cao, có cân nặng, màu lông…

Các hành động như: kêu, đi, ăn, ngủ…

Đối tượng thì chính là con mèo vàng ta đang nuôi trong nhà cũng mang đặc tính của lớp mèo.

2. Các nguyên lý cơ bản của OOP

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

  • Các dữ liệu và phương thức có liên quan với nhau được đóng gói thành các lớp để tiện cho việc quản lý và sử dụng. Tức là mỗi lớp được xây dựng để thực hiện một nhóm chức năng đặc trưng của riêng lớp đó.

  • Ngoài ra, đóng gói còn để che giấu che dấu những tính chất xử lý bên trong của đối tượng, những đối tượng khác không thể tác động trực tiếp làm thay đổi trạng thái chỉ có thể tác động thông qua các method public của đối tượng đó

Nhìn vào ví dụ trên. Ta chỉ có thể gọi đến phương thức tinhdientich() để lấy ra diện tích của hình chữ nhật, hoàn toàn không biết xử lí bên trong hàm như thế nào.

Tính kế thừa (Inheritance)

Nó cho phép xây dựng một lớp mới dựa trên các định nghĩa của lớp đã có. Có nghĩa là lớp cha có thể chia sẽ dữ liệu và phương thức cho các lớp con. Các lớp con không phải định nghĩa lại, ngoài ra có thể mở rộng các thành phần kế thừa và bổ sung thêm các thành phần mới. Tái sử dụng code, tránh bị lặp. Một số loại kế loại kế thừa thường gặp: đơn kế thừa, đa kế thừa, kế thừa đa cấp, kế thừa thứ bậc.

  • Đơn kế thừa: một class con kế thừa từ 1 class cha

  • Đa kế thừa: một class con kế thừa từ n class cha

  • Kế thừa đa cấp: class A kế thừa từ class B, class B kế thừa từ class C

  • Kế thừa thứ bậc: một class được nhiều class kế thừa

VD: 2 lớp Chó, Mèo

Mỗi lớp đều đại diện cho một loại động vật khác nhau nhưng lại có những thuộc tính, phương thức giống nhau như gọi có mắt, chân, ăn, uống, ngủ. Thay vì sao chép những thuộc tính này, ta nên đặt chúng vào một lớp chung gọi là lớp cha. Chúng ta có thể định nghĩa lớp cha – trong trường hợp này là Động vật và có những lớp con kế thừa từ nó, tạo ra một mối quan hệ cha/con.

Tính đa hình (Polymorphism)

Tính đa hình là một hành động có thể được thực hiện bằng nhiều cách khác nhau. Đây lại là một tính chất có thể nói là chứa đựng hầu hết sức mạnh của lập trình hướng đối tượng.

Hiểu một cách đơn giản hơn: Đa hình là khái niệm mà hai hoặc nhiều lớp có những phương thức giống nhau nhưng có thể thực thi theo những cách thức khác nhau.

Ví dụ như ở phần trên, mỗi một động vật kế thừa từ lớp cha vật nhưng có thể "Kêu" theo những cách khác nhau. Con chó sẽ kêu gâu gâu, Con mèo sẽ kêu meo meo.

Vậy trong ví dụ chó, mèo xem như là các đối tượng. 2 con vật có thể hiểu cùng kêu nhưng theo các cách khác nhau.

Trong code để thể hiện tính đa hình có 2 cách:

Method Overloading (compile time polymorphism)

Method Overriding (run time polymorphism)

Method Overloading : nạp chồng các method có cùng tên nhưng khác tham số

Method Overriding: Ghi đè lên phương thức của lớp cha

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

Trừu tượng có nghĩ là tổng quát hóa một cái gì đó lên, không cần chú ý chi tiết bên trong. Nó không màng đến chi tiết bên trong là gì và người ta vẫn hiểu nó mỗi khi nghe về nó.

Ví dụ: Máy nghe nhạc có thể dùng để phát nhạc, thì chức năng phát nhạc là đại diện cho trừu tượng (abstraction). Người dùng chỉ cần biết là nút bật thì nhạc tự động phát, không cần biết bên trong nó làm thế nào.

Ở đây trong lập trình OOP, tính trừu tượng nghĩa là chọn ra các thuộc tính, phương thức của đối tượng cần cho việc giải quyết bài toán đang lập trình. Vì một đối tượng có rất nhiều thuộc tính phương thức, nhưng với bài toán cụ thể không nhất thiết phải chọn tất cả.

Ví dụ: Bài toán quản lý sinh viên chúng ta chỉ cần quản lý các thông tin như

Họ tên Ngày sinh Giới tính … Điểm thi mà lại không cần quản lý thêm các thông tin:

Màu tóc Sở thích Chiều cao Tại vì chúng thực sự không cần thiết.

3. Các ưu điểm của lập trình hướng đối tượng

Dựa trên nguyên lý kế thừa, trong quá trình mô tả các lớp có thể loại bỏ những chương trình bị lặp, dư. Và có thể mở rộng khả năng sử dụng các lớp mà không cần thực hiện lại. Tối ưu và tái sử dụng code hiệu quả.

Đảm bảo rút ngắn thời gian xây dựng hệ thống và tăng năng suất thực hiện.

Sự xuất hiện của 2 khái niệm mới là lớp và đối tượng chính là đặc trưng của phương pháp lập trình hướng đối tượng. Nó đã giải quyết được các khuyết điểm của phương pháp lập trình hướng cấu trúc để lại. Ngoài ra 2 khái niệm này đã giúp biểu diễn tốt hơn thế giới thực trên máy tính.

Tài liệu tham khảo

https://topdev.vn/blog/oop-la-gi/

https://viblo.asia/p/4-dac-tinh-cua-lap-trinh-huong-doi-tuong-object-oriented-program-XL6lAA7Nlek


Tìm hiểu về lập trình hướng đối tượng (Object Oriented Programming)

Bài đăng này đã không được cập nhật trong 5 năm

Lập trình hướng đối tượng (OOP) là một trong những kỹ thuật lập trình rất quan trọng hiện nay. Nó được áp dụng ở hầu hết các ứng dụng thực tế xây dựng tại các doanh nghiệp. Hầu hết các ngôn ngữ lập trình và framework lập trình phổ biến hiện nay như Java, PHP, .NET, ruby đều hỗ trợ lập trình hướng đối tượng. Các lập trình viên đa phần đã được học về lập trình hướng đối tượng ở trường đại học nhưng các nguyên lý cơ bản của lập trình hướng đối tượng đôi khi lại không nắm rõ dẫn đến sử dụng sai, không đúng triết lý của lập trình hướng đối tượng.

Trong bài viết này, tôi sẽ tóm lược lại các nguyên lý cơ bản của lập trình hướng đối tượng nhằm giúp các bạn có được một cái nhìn tổng quát về OOP cũng như cách áp dụng nó.

Lập trình hướng đối tượng là gì? Lập trình hướng đối tượng là một kỹ thuật lập trình cho phép lập trình viên tạo ra các đối tượng trong code trừu tượng hóa các đối tượng thực tế trong cuộc sống. Hướng tiếp cận này hiện đang rất thành công và đã trở thành một trong những khuôn mẫu phát triển phần mềm, đặc biệt là các phần mềm cho doanh nghiệp.

Khi phát triển ứng dụng sử dụng OOP, chúng ta sẽ định nghĩa các lớp (class) để mô hình các đối tượng thực tế. Trong ứng dụng các lớp này sẽ được khởi tạo thành các đối tượng và trong suốt thời gian ứng dụng chạy, các phương thức (method) của đối tượng này sẽ được gọi.

Lớp định nghĩa đối tượng sẽ như thế nào: gồm những phương thức và thuộc tính (property) gì. Một đối tượng chỉ là một thể hiện của lớp. Các lớp tương tác với nhau bởi các public API: là tập các phương thức, thuộc tính public của nó.

Lớp và đối tượng

OOP có 3 nguyên lý cơ bản chúng ta sẽ cùng tìm hiểu chi tiết sau đây đó là:

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

Tính đóng gói tức là quy tắc yêu cầu trạng thái bên trong của một đối tượng được bảo vệ và tránh truy cập được từ code bên ngoài (tức là code bên ngoài không thể trực tiếp nhìn thấy và thay đổi trạng thái của đối tượng đó). Bất cứ truy cập nào tới trạng thái bên trong này bắt buộc phải thông qua một public API để đảm bảo trạng thái của đối tượng luôn hợp lệ bởi vì các public API chịu trách nhiệm thực hiện kiểm tra tính hợp lệ cũng như trình tự cập nhật trạng thái của đối tượng đó.

Nói chung trạng thái đối tượng không hợp lệ thường do: chưa được kiểm tra tính hợp lệ, các bước thực hiện không đúng trình tự hoặc bị bỏ qua nên trong OOP có một quy tắc quan trọng cần nhớ đó là phải luôn khai báo các trạng thái bên trong của đối tượng là private và chỉ cho truy cập qua các public/protected method/property. Khi sử dụng các đối tượng ta không cần biết bên trong nó làm việc như thế nào, ta chỉ cần biết các public API là gì và điều này đảm bảo những gì thay đổi đối tượng sẽ được kiểm tra bởi các quy tắc logic bên trong, tránh đối tượng bị sử dụng không chính xác.

Nguyên lý đóng gói như thế này ở đâu ta cũng có thể bắt gặp ví dụ như thiết kế viên thuốc, chúng ta chỉ biết nó chữa bệnh này, bệnh kia và một số thành phần chính còn cụ thể bên trong nó có những gì thì hoàn toàn không biết.

Tính kế thừa (Inheritance)

Khi bắt đầu xây dựng ứng dụng chúng ta sẽ bắt đầu việc thiết kế các lớp, thông thường chúng ta sẽ thấy có trường hợp một số lớp dường như có quan hệ với những lớp khác, chúng có những đặc tính khá giống nhau. VD: 3 lớp AndroidPhone, IPhone, WindowsPhone

Mỗi lớp đều đại diện cho một loại smartphone khác nhau nhưng lại có những thuộc tính giống nhau. Thay vì sao chép những thuộc tính này, sẽ hay hơn nếu ta đặt chúng ở một nơi có thể dùng bởi những lớp khác. Điều này được thực hiện bởi tính kế thừa trong OOP: chúng ta có thể định nghĩa lớp cha – base class (trong trường hợp này là Smartphone ) và có những lớp con kế thừa từ nó (derived class), tạo ra một mối quan hệ cha/con.

Bây giờ, các lớp con có thể kế thừa 3 thuộc tính từ lớp cha. Nếu các chức năng của lớp cha đã được định nghĩa đầy đủ thì lập trình viên sẽ không phải làm bất cứ việc gì ở lớp con. Còn nếu một lớp con muốn chức năng khác so với định nghĩa ở lớp cha thì nó có thể ghi đè (override) chức năng đã được định nghĩa trên lớp cha này.

Tính đa hình (Polymorphism)

Với đa số lập trình viên thì tính Kế thừa và Đóng gói trong OOP khá dễ hiểu còn tính Đa hình khi mới tiếp cận sẽ thấy khó hiểu hơn một chút. Tuy nhiên đây lại là một tính chất có thể nói là chứa đựng hầu hết sức mạnh của lập trình hướng đối tượng. Hiểu một cách đơn giản: Đa hình là khái niệm mà hai hoặc nhiều lớp có những phương thức giống nhau nhưng có thể thực thi theo những cách thức khác nhau.

Ví dụ như ở phần trên, mỗi một smartphone kế thừa từ lớp Smartphone nhưng có thể lưu trữ dữ liệu trên cloud theo những cách khác nhau:

AndroidPhone lưu trữ bằng Google Drive Iphone lưu trên iCloud WindowsPhone sử dụng SkyDrive.

Bởi vì tất cả đều là Smartphone nên nếu ta viết một hàm dùng kiểu Smartphone làm tham số thì khi gọi hàm ta có thể truyền vào một đối tượng kiểu AndroidPhone, Iphone hoặc WindowsPhone bởi vì chúng đều kế thừa từ lớp Smartphone nên được chấp nhận (hiểu nôm na một AndroidPhone, Iphone, WindowsPhone cũng là một Smartphone). Bên cạnh đó hàm này thậm chí không cần quan tâm smartphone nào được truyền vào do nó chỉ cần biết đối tượng đang xử lý ở đây là Smartphone với những public method/property đã được định nghĩa. Nếu các lớp con không định nghĩa lại (overrides) phương thức CloudStore() thì phương thức CloudStore() trên lớp cha (Smartphone) sẽ được gọi. Còn nếu lớp con override lại phương thức CloudStore() của lớp cha như ở hình trên thì phương thức CloudStore() trên lớp con sẽ được gọi mặc dù code trong hàm đang thao tác với đối tượng kiểu Smartphone.

Tính Đa hình như trên là một tính chất rất mạnh mẽ bởi vì nó mang lại cho code khả năng tổng quát hóa cao. Chúng ta không cần tạo ra phương thức cho mỗi kiểu kế thừa từ lớp cha Smartphone mà chỉ cần nhận một biến kiểu Smartphone và có thể làm việc với bất cứ lớp nào kế thừa từ nó. Điều duy nhất không làm được ở đây là sử dụng những phương thức mà chỉ được khai báo trên các lớp con. VD: nếu ta có một phương thức trên lớp IPhone gọi là OpenSiri() nhưng không được khai báo trên lớp Smartphone, khi đó muốn gọi nó sẽ bắt buộc phải ép kiểu từ Smartphone sang IPhone trước khi gọi.

Interface

Đa hình dựa trên Kế thừa không phải bao giờ cũng là lựa chọn tốt nhất. Ta thấy rõ ràng rằng 3 lớp bên dưới (Iphone, Laptop, FingerprintScanner) đều là những thứ có thể truy cập được bằng vân tay nhưng chúng thực hiện theo những cách khác nhau. Những lớp này có chung một hành động tạm gọi là định danh bằng sinh trắc học – BiometricAuth(). Nếu ta cố gắng gộp cả 3 lớp này vào thành 1 lớp chung sẽ không hay vì rất khó để tìm ra điểm chung tổng quát của chúng ngoài việc có thể truy cập bằng vân tay.

Do vậy thay vì sử dụng Kế thừa ở đây, ta có thể sử dụng một kỹ thuật khác đó là Interface. Interface đơn giản là một giao ước chỉ ra rằng code của bạn sẽ thực thi và hỗ trợ một public API cụ thể nào đó. Tuy nhiên các public API này được thực hiện như thế nào thì không được chỉ ra trên Interface mà sẽ được chỉ ra trên lớp thực thi interface này. Về cơ bản giao ước là một danh sách các public method/property mà chắc chắn sẽ được thực thi trong lớp của bạn.

Áp vào VD trên, ta có thể tạo ra một interface là IBiometricAuth với một phương thức là BiometricAuth(). Tiếp theo cho các lớp Iphone, Laptop, FingerprintScanner thực thi interface IBiometricAuth này như hình sau

Vì mỗi lớp trên đều thực thi interface IBiometricAuth nên ta có thể đảm bào rằng chúng đều có phương thức BiometricAuth() và khai báo của phương thức sẽ giống y như được định nghĩa trên interface IBiometricAuth. Tương tự Kế thừa dựa trên Đa hình, sử dụng Interface cho phép chúng ta khai báo phương thức nhận tham số kiểu IBiometricAuth nhưng chấp nhận bất cứ đối tượng nào truyền vào mà kiểu của nó thực thi interface IBiometricAuth này. Các lớp thực thi IBiometricAuth không cần phải có chung lớp cha ngoại trừ interface IBiometricAuth. Trong phương thức ở trên, ta có thể gọi bất cứ phương thức nào đã được định nghĩa trên interface IBiometricAuth của đối tượng truyền vào mà không cần quan tâm kiểu thực sự của nó là gì: Không cần quan tâm nó là Iphone, Laptop hay FingerprintScanner, chỉ cần biết nó hỗ trợ interface IBiometricAuth là có thể gọi được phương thức BiometricAuth(), hết sức linh hoạt và mềm dẻo.

Tóm lại

Trong bài viết này tôi đã tóm lược lại 3 nguyên lý cơ bản của lập trình hướng đối tượng sao cho đơn giản và dễ hiểu nhất. Mặc dù chúng rất cơ bản và hầu như ai học về lập trình cũng đã từng được học hoặc đọc nhưng hy vọng bài viết này sẽ mang đến một điều gì đó dễ dàng tiếp cận hơn cho các bạn, nhất là những lập trình viên mới tiếp cận OOP. Hãy chia sẻ phản hồi của bạn về bài viết và share cho bạn bè nếu bạn thấy nó hữu ích nhé. Nếu bạn có câu hỏi gì xin vui lòng comment dưới bài viết để chúng ta cùng thảo luận.