Lập trình hướng đối tượng (OOP) là gì? Tại sao lập trình hướng đối tượng hay được hỏi khi phỏng vấn. Lập trình hướng đối tượng có những tính chất gì....
Một số các bạn vừa mới tiếp cận lập trình hướng đối tượng OOP sẽ cảm thấy ngợp hoặc mới lơ tơ mơ hiểu một chút một chút từng tính chất của nó.
Qua bài viết này với một chút kiến thức lẻ loi mình hi vọng sẽ giải đáp đầy đủ và chính xác lập trình hướng đối tượng trong C#.
Tại sao là phải học OOP ?
- Tính đóng gói: OOP cho phép bạn đóng gói dữ liệu và hành động của một đối tượng trong một đối tượng, giúp tạo ra một giải pháp tổng thể hơn và dễ dàng quản lý.
- Tính kế thừa: OOP cho phép bạn tạo ra một lớp mới từ một lớp đã tồn tại, giúp giảm thiểu sự lặp lại và tăng tính tái sử dụng mã.
- Tính đa hình: OOP cho phép một đối tượng có thể thể hiện nhiều hình dạng khác nhau, giúp cho việc phát triển chương trình trở nên linh hoạt hơn.
- Tính trừu tượng: OOP cho phép bạn xem một đối tượng chỉ theo những thuộc tính và phương thức chung, giúp tăng tính rõ ràng và dễ quản lý hơn.
Tính đóng gói (encapsulation)
Person
với các thuộc tính name
và age
. Bạn muốn cho phép các đối tượng Person
truy cập vào những thông tin này nhưng bạn không muốn cho phép chúng truy cập trực tiếp vào các giá trị của chúng. name
và age
là private
và tạo các phương thức publish
để truy cập vào chúng. Ví dụ:class Person { private string name; private int age; public string GetName() { return name; } public void SetName(string value) { name = value; } public int GetAge() { return age; } public void SetAge(int value) { age = value; } }
Với tính đóng gói này, bạn có thể truy cập vào các thuộc tính name
và age
chỉ thông qua các phương thức "public", giữ mã đồng bộ và bảo vệ dữ liệu khỏi sự truy cập trái phép.
Tính kế thừa (Inheritance)
Pet
với các thuộc tính như name
, age
, breed
. Bạn muốn tạo ra một lớp con Dog
kế thừa tất cả các thuộc tính của lớp cha Pet
và thêm mới thuộc tính coatPattern
.
class Pet { public string Name; public int Age; public string Breed; public Pet(string name, int age, string breed) { Name = name; Age = age; Breed = breed; } } class Dog : Pet { public string CoatPattern; public Dog(string name, int age, string breed, string coatPattern) : base(name, age, breed) { CoatPattern = coatPattern; } }
Trong ví dụ trên, lớp Dog
kế thừa tất cả các thuộc tính và hàm tạo của lớp cha Pet
. Nó cũng thêm mới thuộc tính coatPattern
để mô tả tạo mẫu của chó. Chúng ta nhận thấy rằng việc lặp lại code đã không cần thiết nữa, không cần phải tạo các trường name, age... cho class Dog. Áp dụng nó nếu ta muốn nuôi một con mèo nữa thì triển khai interface Pet là có thể kế thừa được tất cả các thuộc tính khiến code sạch sẽ dễ bảo trì hơn.
Tính đa hình (polymorphism)
Animal
với các phương thức như MakeSound()
để phát tiếng âm thanh của động vật. Chúng ta sẽ triển khai 2 con vật là Dog
và Cat
để phát ra tiếng kêu đặc trưng của 2 con vật. Chúng ta xem code ví dụ dưới đây nhé.public class Animal { public virtual void MakeSound() { Console.WriteLine("Animal sound"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("Woof!"); } } public class Cat : Animal { public override void MakeSound() { Console.WriteLine("Meow!"); } }
Trong ví dụ trên, chúng ta có một lớp Animal
với phương thức MakeSound
. Phương thức này được định nghĩa với từ khóa virtual và chúng ta sẽ cho phép lớp con kế thừa và ghi đè (override) nó.
Sau đó, hai lớp con Dog
và Cat
kế thừa từ lớp Animal
và ghi đè phương thức MakeSound
để cung cấp phản xạ cụ thể cho âm thanh của chúng.
Cuối cùng, khi chúng ta gọi phương thức MakeSound
trên một đối tượng Dog
hoặc Cat
, chương trình sẽ in ra "Woof!" hoặc "Meow!" tương ứng, chứ không phải "Animal sound". Điều này chính là tính đa hình: phương thức định nghĩa trong lớp cha có thể được ghi đè bởi lớp con để cung cấp phản xạ cụ thể hơn.
Tính trừu tượng (abstraction)
Shape
với một phương thức Draw
public abstract class Shape { public abstract void Draw(); }
Lớp Shape
là một lớp trừu tượng vì nó sử dụng từ khóa abstract để chỉ ra rằng nó là một lớp trừu tượng và phương thức Draw
là một phương thức trừu tượng vì nó cũng sử dụng từ khóa abstract.
Do đó, lớp Shape
không thể được khởi tạo trực tiếp, chúng ta cần tạo một lớp con để triển khai phương thức Draw
:
public class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a Circle"); } } public class Square : Shape { public override void Draw() { Console.WriteLine("Drawing a Square"); } }
Bây giờ, chúng ta có thể tạo một danh sách các hình vẽ và gọi phương thức Draw
trên từng hình:
List<Shape> shapes = new List<Shape> { new Circle(), new Square() }; foreach (var shape in shapes) { shape.Draw(); }
Kết quả khi chạy
Drawing a Circle Drawing a Square
Trong ví dụ trên, chúng ta đã tạo một lớp trừu tượng Shape
với một phương thức trừu tượng Draw
và các lớp con Square
và Circle
được kế thừa từ lớp trừu tượng Shape
. Phương thức trừu tượng Draw
được định nghĩa trong lớp trừu tượng Shape
, nhưng các lớp con Square
và Circle
đều có một phần mở rộng của nó để vẽ hình dạng tương ứng.
Tính trừu tượng cho phép chúng ta xác định các chức năng của một lớp mà không cần biết chi tiết cách hoạt động của chúng. Trong ví dụ trên, chúng ta có một lớp trừu tượng Shape
với phương thức trừu tượng Draw
, nhưng không biết cách hoạt động của nó cho đến khi chúng ta xem xét các lớp con như Square
và Circle
.
Vì vậy, tính trừu tượng cho phép chúng ta tách biệt giữa các phần tử của hệ thống và tập trung vào các chức năng của chúng, giúp cho mã dễ dàng quản lý và dễ dàng mở rộng và bảo trì trong tương lai.
Mong bài viết trên sẽ giúp các bạn được "thông não" về lập trình OOP. Nếu thấy hay hãy chia sẽ tới bạn bè để cùng nhau phát triển nhé.