Phần 3: “Quan hệ” giữa các đối tượng trong Objective-C


Phần 1: “Lần đầu” với lập trình Objective-C
Phần 2: “Khám phá” OOP trong Objective-C
Phần 3: “Quan hệ” giữa các đối tượng trong Objective-C
Phần 4: “Tự sướng A2Z” với một chương trình Objective-C hoàn thiện
Phần 5: “Vật lộn” với quản lý bộ nhớ trong Objective-C
Phần 6: Chuẩn bị “lên đỉnh” với iPhone application

Bài trước chúng ta đã nắm được những khái niệm cơ bản về lập trình hướng đối tượng (OOP), đã phân biệt được cách thức tổ chức các đối tượng trong chương trình Objective-C (tách riêng tệp interface và tệp lớp khởi tạo dựa trên interface đó). Trong bài này, chúng ta sẽ tìm hiểu sâu hơn về việc khởi tạo các đối tượng và tính thừa kế.

Con trỏ và khởi tạo đối tượng

Giả sử chúng ta muốn làm việc với một chuỗi ký tự, cụ thể là các thao tác nhập giá trị và in giá trị của chuỗi đó lên màn hình. Vậy chúng ta sẽ phải làm thế nào? Đoạn mã dưới đây sẽ minh họa điều trên:

pointer_initializing

Chương trình trên được tạo trong Xcode bằng cách vào File> New Project> Mac OS X> Application> Command Line> Type: Foundation (khá là đơn giản) và gõ nội dung của chương trình trên vào file main.m (lớp chạy chính của project).

Tiếp theo chúng ta sẽ tìm hiểu cụ thể cấu trúc của một chương trình viết bằng Objective-C. Chương trình Objective-C được xây dựng theo kiểu OOP nên bao gồm nhiều lớp (file có đuôi là .m), trong đó không thể thiếu được một lớp chạy chính đó là main.m (file này mặc định được tạo sẵn trong project mới).

Dòng đầu tiên của file main.m là dòng khai báo sử dụng các thư viện chứa các đối tượng có sẵn (API) trong Objective-C hoặc do ta tạo ra. Cụ thể ở ví dụ trên, chúng ta sẽ báo với compiler biết rằng trong chương trình sẽ sử dụng các API cơ bản của foundation framework (foundation.h)

#import <Foundation/Foundation.h>

Để làm việc với một xâu ký tự chúng ta phải tạo ra một con trỏ đến một đối tượng NSString (tham khảo bài 2) gọi là testString. Câu lệnh đầu tiên chỉ là câu lệnh khai báo biến con trỏ tham chiếu đến chuỗi ký tự, thực sự thì chuỗi ký tự chưa được khởi tạo và chưa sẵn sàng để chứa các ký tự. Câu lệnh thứ 2, chúng ta mới khởi tạo tạo ra đối tượng cụ thể có thể chứa chuỗi ký tự.

NSString *testString;
testString = [[NSString alloc] init];

Trên đây là cách viết tắt, còn cách viết đầy đủ thì câu lệnh thứ 2 ta sẽ phải tách làm 2 câu lệnh và cụ thể như sau:

NSString *testString;
testString = [NSString alloc];
[testString init];

Cách viết này có thể làm bạn bối rối vì đó là cách viết đầy đủ. Bạn có thể dùng theo cách viết ngắn gọn ở trên bằng cách lồng 2 ngoặc [] trên cùng một dòng lệnh. Tiếp tục sau khi đã khởi tạo thành công đối tượng xâu, chúng ta sẽ gán giá trị xâu cố định “Sample Text” cho nó. Cách thức thể hiện thao tác gán như sau:

testString = @"Sample Text";

Các bạn đã thấy, chúng tôi có thêm ký tự @ trước xâu ký tự là để báo với compiler biết rằng chuỗi văn bản sau @ là một NSString. Như vậy là đối tượng xâu đã có giá trị, vậy làm thế nào ta có thể in giá trị xâu đó ra màn hình? Ở đây ta sẽ không dùng những hàm IO của C mà chúng ta sẽ dùng một đối tượng có sẵn trong foundation framework. Đó là đối tượng NSLog với cú pháp như sau:

NSLog(@"testString: %@", testString);

Ở đây, đối tượng NSLog sẽ in một thông điệp ra màn hình console với cú pháp như trên. Phần trong nháy kép là giá trị xâu cố định và đi theo sau dấu @ và trong nháy kép là vị trí đặt giá trị của biến đối tượng xâu NSString đại diện với ký tự %@. Đó là quy định khi giá trị của một đối tượng xâu NSString ra màn hình console. Kết thúc phần nháy kép trong NSLog sẽ là là phần tiếp theo ngăn cách bởi dấu phẩy (,) chứa danh sách các biến con trỏ tham chiếu đến đối tượng chuỗi NSString, cụ thể mỗi một vị trí %@ sẽ in ra được một giá trị của một con trỏ xâu NSString theo đúng thứ tự. Để gỡ rối và chạy chương trình trong Xcode ta nhấn vào nút Run ở góc trái trên phần cửa sổ project và nhìn kết quả ở cửa sổ output bên dưới màn hình Xcode như hình bên dưới:

teststring_output

Kết quả cuối cùng, chương trình sẽ trả về 0, điều này báo với hệ điều hành rằng mọi thao tác trong chương trình đã kết thúc và không có vấn đề bất thường gì cả.

Thừa kế (Inheritance)

Ở phần trên các bạn đã thấy cách chúng tôi thao tác với NSString. Có phải chúng tôi sử dụng các phương thức init? Đúng NSMutableString, NSArray và trong thực tế, bất cứ lớp NS nào cũng sử dụng phương thức init. Có vẻ ở đây chúng ta lãng phí rất nhiễu mã lệnh để đưa các phương thức init trong mỗi lớp, điều đó có đúng không? Thực sự là không phải vậy, trong OOP có một khải niệm đó là tính thừa kế (Inherinance), nó cung cấp cho ta một khả năng đó là khi có nhiều lớp có tính chất và hành động giống nhau thì ta sẽ tạo ra một lớp chứa các tính chất và hành động chung đó (lớp này gọi là lớp cha), sau đó các lớp con sẽ thừa kế tới lớp cha và có thể dùng lại các tính chất cũng như hành động của lớp cha mà không phải viết lại bất cứ dòng lệnh nào. Và thực tế là ở trong foundation framework, các lớp thư viện đều có chung một nguồn gốc, có nghĩa là tất cả các lớp đều thừa kế tới một lớp gốc, đó là lớp NSObject. Tất nhiên quá trình thừa kế này có thể tiếp tục để các lớp con cháu, chắt sẽ thừa kế lớp con của lớp cha. Tuy nhiên sự thừa kế ở đây là sự thừa kế đơn. Có nghĩa là một lớp con chỉ thừa kế một lớp cha mà thôi. Chúng ta sẽ nhìn vào sơ đồ bên dưới để thấy được sự phân câp quan hệ giữa một số lớp cơ bản trong foundation framework. Chúng tôi xin lưu ý rằng: việc nắm vứng sơ đồ qua hệ các class là hết sức quan trọng trong công việc lập trình của bạn sau này.

Inheritance

Ở sơ đồ trên, các tác giả của Objective-C chỉ cần tạo trong NSObject một phương thức gọi là init và sau đó các lớp con sẽ thừa kế tới lớp NSObject để dùng lại phương thức init và để đảm bảo các đối tượng con sẽ được khởi tạo một cách đúng đắn giống nhau. Tuy nhiên trong quá trình thừa kế, các lớp con có thể ghi đè hay sửa đổi nội dung một số đặc tính hay phương thức của lớp cha. Đó gọi là thừa kế có cải tiến hay còn gọi là ghi đè phương thức (method overiding). Lý do của việc này theo phương pháp lập trình OOP khi các lớp con cùng thừa kế một lớp cha nhưng nó vẫn muốn đảm bảo tính đa dạng hay hay đa hình của các đối tượng con khi tạo ra có thể khác nhau chút xíu từ hình thức đến một số chi tiết hoạt động. Việc này hoàn toàn là hợp lệ và rất hiệu quả bời vì chúng ta hoàn toàn có thể tạo ra một lớp mới dựa trên những lớp đã có sẵn nhưng chúng ta cũng có thể thêm một số gia vị khác ở trên những lớp con đó và tạo nên sự đặc trưng riêng của những lớp do ta tạo nên. Việc này thực chất rất gần gũi với quan hệ giữa các đối tượng trong đời sống thực. Ví dụ bài toán quản lý phương tiện (Vehicle), tất cả các phương tiện sẽ đều có những tính chất chung giống nhau nên ta tạo ra một lớp vehicle đóng vai trò là lớp cha và sau đó ta sẽ tạo ra những lớp con như Car, Bus, Truck… sẽ có cùng chung đặc điểm của lớp vehicle nhưng lại có một số đặc điểm và hoạt động chi tiết khác nhau. Ví dụ sau sẽ minh họa lớp Bus thừa kế tới lớp Vehicle:

class_inheritance

Tổng kết

Cho đến thời điềm này, chúng tôi chắc các bạn đã nắm được một số nguyên tắc cơ bản của OOP trong Objective-C. Để có thể kiểm tra những kiến thức mình đã học, xin mời các bạn trả lời một số câu hỏi sau:

  • Sự khác biệt giữa một lớp và một đối tượng là gì?
  • Tại sao chúng ta sử dụng các lớp?
  • Tại sao phải sử dụng tính thừa kế?

Phần tiếp theo

Để có thể xây dựng được những chương trình OOP chọn vẹn từ A tới Z, xin mời bạn đọc tiếp bài học thứ 4…

Leave a comment