Những khái niệm trong lập trình hướng đối tượng

Nếu trước đây bạn chưa từng sử dụng một ngôn ngữ lập trình hướng đối tượng nào, bạn sẽ cần phải tìm hiểu về một số khái niệm cơ bản trước khi bạn có thể bắt tay vào viết bất kỳ một dòng lệnh nào. Bài viết sẽ giới thiệu với bạn về các khái niệm đối tượng, lớp, thừa kế, giao diện, gói. Những khái niệm này có liên quan như thế nào trong thế giới thực, đồng thời cũng giới thiệu về cú pháp của ngôn ngữ lập trình Java.

Đối tượng là gì?

Các đối tượng là điểm cốt lõi để hiểu về công nghệ hướng đối tượng. Bây giờ hãy nhìn xung quanh và bạn sẽ thấy được rất nhiều ví dụ về đối tượng của thế giới thực: con chó, cái bàn, ti vi, xe đạp.

Đối tượng trong thế giới thực có chung hai đặc điểm: Tất cả đều có trạng tháihành vi. Chó có trạng thái (tên, màu sắc, loại, tình trạng đói hay no) và hành vi (sủa, tha đồ vật đến, vẫy đuôi). Xe đạp cũng có trạng thái (bánh răng, nhịp bàn đạp hiện tại, tốc độ hiện tại) và hành vi (thay đổi bánh răng, thay đổi nhịp bàn đạp, sử dụng phanh). Việc xác định trạng thái và hành vi của các đối tượng trong thế giới thực là một cách tuyệt vời để bắt đầu nghĩ đến các khái niệm của lập trình hướng đối tượng.

Bây giờ ta hãy dành ra một vài phút để quan sát các đối tượng của thế giới thực xung quanh bạn. Đối với mỗi đối tượng mà bạn nhìn thấy, hãy tự đặt ra cho mình hai câu hỏi: “Đối tượng này có thể ở trong những trạng thái nào?” và “Đối tượng này có thể thưc hiện những hành vi nào?” Hãy chắc chắn rằng bạn đã ghi lại những quan sát của mình. Khi làm thế, bạn sẽ nhận thấy rằng các đối tượng trong thế giới thực có sự khác nhau về độ phức tạp; chiếc đèn bàn có thể chỉ có 2 trạng thái (đang bật và đang tắt) và hai hành vi (bật và tắt), nhưng chiếc radio có thể có thêm các trạng thái khác (đang bật, đang tắt, âm lượng hiện tại, kênh hiện tại) và hành vi (bật, tắt, tăng âm lượng, giảm âm lượng, tìm kiếm, dò kênh và điều chỉnh). Bạn cũng sẽ nhận thấy rằng một số đối tượng này có thể chứa các đối tượng khác. Những quan sát về thế giới thực này đều được chuyển vào trong thế giới của lập trình hướng đối tượng.

Một đối tượng phần mềm.

Về mặt khái niệm thì các đối tượng phần mềm cũng tương tự như các đối tượng trong thế giới thực: Nó cũng bao gồm các trạng thái và hành vi liên quan. Một đối tượng lưu trữ trạng thái của nó trong các trường (có thể được gọi là biến trong một số ngôn ngữ lập trình) và thể hiện các hành vi của mình ra bên ngoài thông qua các phương thức (có thể được gọi là hàm ở trong một số ngôn ngữ lập trình). Các phương thức thao tác trên các trạng thái bên trong của một đối tượng và được dùng như là cơ chế chính cho sự giao tiếp giữa đối tượng-với-đối tượng. Việc ẩn đi trạng thái bên trong và bắt buộc tất các các tương tác đều phải được thực hiện thông qua các phương thức của một đối tượng được biết đến như là sự bao gói dữ liệu (data encapsulation) – một nguyên lý cơ bản của lập trình hướng đối tượng.

Hãy lấy một chiếc xe đạp làm ví dụ:

Chiếc xe đạp được mô hình hóa như là một đối tượng phần mềm.

Bằng cách mô tả các trạng thái (tốc độ hiện tại, nhịp đạp hiện tại, bánh răng hiện tại) và cung cấp các phương thức để thay đổi các trạng thái đó, thì một đối tượng vẫn kiểm soát được cách thức mà thế giới bên ngoài được phép sử dụng nó. Ví dụ, nếu chiếc xe đạp chỉ có 6 bánh răng, một phương thức để thay đổi bánh răng có thể từ chối bất cứ giá trị nào nhỏ hơn 1 hoặc lớn hơn 6.

Việt gói các đoạn mã vào trong từng đối tượng phần mềm riêng lẻ sẽ mang lại một số lợi ích, bao gồm:

  1. Mô-đun hóa: Mã nguồn của một đối tượng có thể được viết và duy trì độc lập với mã nguồn của các đối tượng khác. Một khi được tạo ra thì một đối tượng có thể dễ dàng được truyền đi bên trong hệ thống.
  2. Che giấu thông tin: Bằng cách chỉ tương tác với các phương thức thì các chi tiết được triển khai bên bên trong của một đối tượng sẽ được ẩn đi đối với thế giới bên ngoài.
  3. Tái sử dụng mã: Nếu một đối tượng đã tồn tại (có thể được viết bởi một nhà phát triển phần mềm khác), bạn có thể sử dụng đối tượng đó trong chương trình của mình. Điều này cho phép các chuyên gia triển khai/ kiểm thử/ gỡ lỗi các đối tượng phức tạp và có nhiệm vụ riêng biệt, rồi sau đó bạn có thể tin tưởng để chạy trong mã riêng của mình.
  4. Ghép nối và gỡ lỗi một cách dễ dàng: Nếu một đối tượng cụ thể phát sinh vấn đề, đơn giản bạn chỉ cần loại bỏ nó khỏi ứng dụng của mình và cắm một đối tượng khác vào để thay thế. Điều này cũng tương tự như khi sửa các vấn đề cơ khí trong thế giới thực. Nếu một chốt bị vỡ, hãy thay thế nó, không phải toàn bộ bộ phận máy.

Lớp là gì?

Trong thế giới thực, bạn thường thấy nhiều đối tượng riêng lẻ thuộc cùng một loại.Có thể có hàng nghìn chiếc xe đạp cùng tồn tại, tất cả chúng đều giống nhau về cách sản xuất và mẫu mã. Mỗi chiếc xe đạp đã được tạo ra từ một tập thiết kế chung, vì thế chúng giống nhau về thành phần cấu tạo. Trong thuật ngữ hướng đối tượng, chúng ta nói rằng chiếc xe đạp là một thể hiện của một lớp các đối tượng có tên gọi là xe đạp. Một lớp là một bản thiết kế mà từ đó các đối tượng cụ thể được tạo ra.

Dưới đây có thể là một cách triển khai của lớp xe đạp:

class Bicycle {

int cadence = 0;
int speed = 0;
int gear = 1;

void changeCadence(intnewValue) {
	cadence = newValue;
}

void changeGear(intnewValue) {
	gear = newValue;
}

void speedUp(int increment) {
	speed = speed + increment;
}

void applyBrakes(int decrement) {
	speed = speed - decrement;
}

void printStates() {
System.out.println("cadence:" +
			cadence + " speed:" +
				speed + " gear:" + gear);
    }
}

Cú pháp của ngôn ngữ Java có vẻ mới lạ với bạn, nhưng thiết kế của lớp này được dựa trên những thảo luận trước đó về đối tượng xe đạp. Các trường casdence, speed và gear biễu diễn trạng thái của đối tượng xe đạp, và các phương thức (changeCadence, changeGear, speedUp, v.v.) định nghĩa các tương tác của nó với thế giới xung quanh.

Bạn có thể thấy rằng lớp Bicycle không chứa phương thức main. Đó là bởi vì nó chưa phải là ứng dụng hoàn thiện; nó chỉ là bản thiết kế cho những chiếc xe đạp mà có thể được dùng trong một ứng dụng. Trách nhiệm của việc tạo và sử dụng các đối tượng xe đạp thuộc về một số lớp khác trong ứng dụng của bạn.

Dưới đây là lớp BicycleDemo, nó tạo ra hai đối tượng xe đạp riêng biệt và gọi các phương thức của chúng:

classBicycleDemo {
	public static void main(String[] args) {
		// Tạo hai đối tượng
		// của lớp Bicycle
		Bicycle bike1 = new Bicycle();
		Bicycle bike2 = new Bicycle();

		// Gọi tới các phương thức
		// của các đối tượng này
		bike1.changeCadence(50);
		bike1.speedUp(10);
		bike1.changeGear(2);
		bike1.printStates();

		bike2.changeCadence(50);
		bike2.speedUp(10);
		bike2.changeGear(2);
		bike2.changeCadence(40);
		bike2.speedUp(10);
		bike2.changeGear(3);
		bike2.printStates();
    }
}

Kết quả của đoạn mã trên là in ra giá trị mới nhất của tốc độ đạp xe, vận tốc và số đang được sử dụng của hai chiếc xe đạp:

cadence:50 speed:10 gear:2

cadence:40 speed:20 gear:3

Kế thừa là gì?

Các loại đối tượng khác nhau cũng thường có một số điểm chung giống nhau.Ví dụ như xe đạp leo núi, xe đạp đường trường, và xe đạp đôi (xe đạp Heat) đều có những đặc tính của xe đạp (tốc độc hiện tại, nhịp đạp hiện tại, bánh răng hiện tại). Tuy vậy mỗi loại lại có thêm các tính năng bổ sung để phân biệt giữa chúng: Xe đạp đôi có hai chỗ ngồi và hai bộ tay lái; xe đạp đường trường có tay lái cong xuống dưới; một số xe đạp leo núi có bộ vòng xích bổ sung, nhằm làm giảm tỉ lệ của các bánh răng.

Lập trình hướng đối tượng cho phép các lớp kế thừa các trạng thái và hành vi từ lớp khác. Trong ví dụ này lớp Bicycle là lớp cha (superclass) của các lớp MountainBike, RoadBike và TandemBike. Trong ngôn ngữ lập trình Java , mỗi lớp chỉ có một lớp cha trực tiếp, và mỗi lớp cha có vô số lớp con:

Hệ thống phân cấp của lớp Xe đạp.

Cú pháp tạo một lớp con rất đơn giản, hãy thêm từ khóa extends vào phần đầu của khai báo của lớp, theo sau nó là tên của lớp mà bạn muốn kế thừa:

class MountainBike extends Bicycle {

// định nghĩa các trường và phương thức mới

// mà một chiếc xe đạp leo núi sẽ có

}

Việc khai báo như trên sẽ giúp cho lớp MountainBike có tất cả thuộc tính và hành vi của lớp Bicycle, nó cho phép tập trung viết các đoạn mã cho những tính năng mà chỉ duy nhất nó có. Việc này còn làm cho các đoạn mã của lớp con có thể được đọc dễ dàng hơn.
Tuy nhiên, bạn cần quan tâm đến việc viết tài liệu tương ứng cho mỗi trạng thái và hành vi của lớp cha, bởi vì mã của chúng sẽ không xuất hiện trong mã nguồn của các lớp con.

 Giao diện là gì?

Như các bạn đã được học, các đối tượng xác định sự tương tác của mình với thế giới bên ngoài thông qua các phương thức mà nó cung cấp. Các phương thức tạo nên một giao diện của đối tượng đối với thế giới bên ngoài; ví dụ: các nút điều khiển đằng trước tivi là giao diện giữa bạn với hệ thống dây điện của tivi . Bạn nhấn nút “power” để bật hoặc tắt tivi.

Hình thức phổ biến nhất của giao diện là một nhóm các phương thức với các phần thân rỗng. Nếu ta xác định các hành vi của một chiếc xe đạp dưới hình thức một giao diện thì nó thể trở thành như sau:

interface Bicycle {
	// số vòng quay của bánh xe trong mỗi phút
	void changeCadence(int newValue);

	void changeGear(int newValue);

	void speedUp(int increment);

	void applyBrakes(int decrement);
}

Để triển khai giao diện này, tên lớp của bạn sẽ cần thay đổi (thành tên một thương hiệu xe đạp cụ thể, ví dụ như ACMEBicycle), và bạn sử dụng từ khóa implements trong phần khai báo của lớp.

class ACMEBicycle implements Bicycle {
	// các phần khác của lớp
	// như đã được triển khai trước đó
}

Việc triển khai một giao diện sẽ giúp cho một lớp trở nên chính quy hơn về phương diện các hành vi mà nó cam kết cung cấp. Các giao diện tạo nên một bản quy ước giữa một lớp với thế giới bên ngoài, bản quy ước này được trình biên dịch đảm bảo tuân thủ tại thời điểm xây dựng. Nếu một lớp muốn triển khai một giao diện thì tất cả các phương thức được định nghĩa bởi giao diện đó phải xuất hiện trong mã nguồn của lớp này trước khi nó được biên dịch thành công.
Lưu ý: Để biên dịch được lớp ACMEBicycle thì bạn cần phải thêm từ khóa public vào trước các phương thức được triển khai của giao diện. Bạn có thể tìm hiểu về những lý do cho điều này trong hai bài học “Lớp và các đối tượng” và “Giao diện và Thừa kế”.

Gói là gì?

Một gói (package) là một vùng không gian được dùng để tổ chức một nhóm các lớp và các giao diện có liên quan với nhau. Về mặt khái niệm bạn có thể coi các gói giống như những thư mục khác nhau trên máy tính của bạn. Bạn có thể lưu các trang HTML trong một thưc mục, ảnh ở một thư mục khác, và các đoạn mã hoặc ứng dụng ở một thư mục khác nữa. Bởi vì các phần mềm được viết bằng ngôn ngữ Java có thể bao gồm hàng trăm hoặc hàng nghìn các lớp riêng biệt, nên cần thiết phải lưu trữ mọi thứ ngăn nắp bằng cách đặt các lớp và giao diện có liên quan với nhau vào trong gói các gói.

Nền tảng Java cung cấp một thư viện khổng lồ các lớp (một tập các gói) phù hợp cho việc sử dụng trong các ứng dụng của riêng bạn. Thư viện này được biết đến với tên gọi “Giao diện lập trình ứng dụng” (Application Programming Interface) hoặc ngắn gọn là “API”. Các gói này đại diện cho các công việc phổ biến nhất gắn với các mục đích lập trình chung. Ví dụ: đối tượng String chứa các đặc điểm và hành vi của các chuỗi kí tự; một đối tượng File cho phép lập trình viên dễ dàng tạo, xóa, kiểm tra, so sánh hoặc chỉnh sửa một tệp tin nào đó trong hệ thống tệp tin, một đối tượng Socket cho phép tạo và sử dụng các kết nối mạng. Các đối tượng GUI khác nhau điều khiển các nút, hộp chọn (checkbox) và các những thứ khác liên quan tới giao diện đồ họa người dùng. Có hàng nghìn lớp để bạn lựa chọn. Điều này cho phép bạn – người lập trình viên – tập trung vào việc thiết kế các ứng dụng thay vì tập trung vào cơ sở hạ tầng cần thiết để nó hoạt động.

Mô tả API của nền tảng Java (Java Platform API Specification) chứa danh sách đầy đủ của tất cả các gói, các giao diện, các lớp, các trường, và các phương thức được cung cấp bởi nền tảng Java SE. Hãy mở trang web này bằng trình duyệt của bạn và đánh dấu nó lại. Đối với một lập trình viên, nó sẽ trở thành một tài liệu tham khảo quan trọng nhất.

Nguồn docs.oracle.com

Theo Trans-team: PhúDĐ, NgọcNT, ĐạtDĐ, SơnĐH, HàĐT, ChàmNM – Tạp chí lập trình