Tài liệu Phương pháp lập trình: PHƯƠNG PHÁP LẬP TRÌNH 
(30 tiết LT) 
Nội dung 
• Chương 1. Mở đầu 
• Chương 2. Lập trình hàm 
• Chương 3. Lập trình cấu trúc 
• Chương 4. Lập trình hướng đối tượng 
• Chương 5. Lập trình hướng đối tượng trong một số ngôn ngữ 
• Chương 6. Các phương pháp lập trình trong F# 
Chương 1. Mở đầu 
Một số khái niệm 
• Mô hình tính toán (computational model) là một tập của các giá trị và 
toán tử. 
• Sự tính tính (computation) là việc áp dụng một chuổi các toán tử trên 
một giá trị để có một giá trị khác. 
• Chương trình (program) là việc chỉ định một sự tính toán. 
• Ngôn ngữ lập trình (programming language) là k{ hiệu để viết các 
chương trình. 
• Cú pháp (syntax) của một ngôn ngữ lập trình xác định cấu trúc hoặc 
dạng của chương trình. 
• Ngữ nghĩa (semantics) của một ngôn ngữ lập trình mô tả quan hệ giữa 
một chuowng trình và mô hình tính toán. 
• Tính hữu dụng (Pragmatics) của một ngôn ngữ lập trình mô tả các mức 
độ thành công mà một ngôn ngữ lập trình đáp ứng các m...
                
              
                                            
                                
            
 
            
                 223 trang
223 trang | 
Chia sẻ: Khủng Long | Lượt xem: 1522 | Lượt tải: 0 
              
            Bạn đang xem trước 20 trang mẫu tài liệu Phương pháp lập trình, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
PHƯƠNG PHÁP LẬP TRÌNH 
(30 tiết LT) 
Nội dung 
• Chương 1. Mở đầu 
• Chương 2. Lập trình hàm 
• Chương 3. Lập trình cấu trúc 
• Chương 4. Lập trình hướng đối tượng 
• Chương 5. Lập trình hướng đối tượng trong một số ngôn ngữ 
• Chương 6. Các phương pháp lập trình trong F# 
Chương 1. Mở đầu 
Một số khái niệm 
• Mô hình tính toán (computational model) là một tập của các giá trị và 
toán tử. 
• Sự tính tính (computation) là việc áp dụng một chuổi các toán tử trên 
một giá trị để có một giá trị khác. 
• Chương trình (program) là việc chỉ định một sự tính toán. 
• Ngôn ngữ lập trình (programming language) là k{ hiệu để viết các 
chương trình. 
• Cú pháp (syntax) của một ngôn ngữ lập trình xác định cấu trúc hoặc 
dạng của chương trình. 
• Ngữ nghĩa (semantics) của một ngôn ngữ lập trình mô tả quan hệ giữa 
một chuowng trình và mô hình tính toán. 
• Tính hữu dụng (Pragmatics) của một ngôn ngữ lập trình mô tả các mức 
độ thành công mà một ngôn ngữ lập trình đáp ứng các mục tiêu của mô 
hình tính toán và tiện ích của nó đối với các lập trình viên. 
• Một chương trình (program) có thẻ xem như là một hàm: 
 Output = Program(Input) 
 Program = Mô hình của một miền bài toán 
 Thực hiện một chương trình = Sự mô phỏng của miền bài toán 
Dữ liệu 
• Dữ liệu (Data): Trong mọi trường hợp các đối tượng dữ lêệu 
được xem như là trung tâm của các chương trình. 
• Các giá trị của dữ liệu được phân thành 2 nhóm riêng biệt: giá 
trị đơn (primitive) và hỗn hợp (compound). 
• Các giá trị đơn (primitive values) thường là các giá trị số, logic, 
ký tự.. Liên hợp với các giá trị đơn thường là các toán tử (như 
các toán tử số học, toán tử logic..) 
• Các giá trị hỗ hợp (composite values) thường là các mảng 
(arrays), các bản ghi (records), các giá trị định nghĩa đệ quy 
(recursively defined values). Liên hợp với các giá trị hỗn hợp 
thường là các toán tử khởi tạo giá trị, các toán tử truy xuất mỗi 
thành phần của giá trị. 
Các loại giá trị dữ liệu 
• Các giá trị Boolean (biểu diễn giá trị logic, nói chỉ sử 
dụng 1 bit dữ liệu) 
• Các giá trị nguyên (Integer) (miền giá trị phụ thuộc 
vào số byte được sử dụng để lưu trữ). 
• Các số tự nhiên (Natural number). 
• Các số hữu tỷ (Rational number) (được biễu diễn bởi 
cặp số nguyên) 
• Các giá trị thực (Real number) 
• Các giá trị ký tự (Character) 
• Các giá trị liệt kê (Enumeration) 
• Các kiểu dữ liệu trừu tượng (Abstract data types) 
(thường sử dụng kiểu con trỏ để tham chiếu) 
Một vài mô hình tính toán 
Mô hình hàm (Functional Model) 
• Mô hình hàm bao gồm một tập các giá trị, các hàm và các toán 
tử trên hàm. 
• Một chương trình là một tập các định nghĩa của các hàm 
• Sự tính toán là sự ứng dụng của hàm (giá trị của biểu thức) 
Lập trình hàm 
Ví dụ: Công thức cho độ lệch chuẩn là: 
Người ta áp dụng hai hàm bậc cao là map và fold. Hàm map áp dụng 
một hàm cho mỗi phần tử trong danh sách và hàm fold làm giảm một 
danh sách bằng cách áp dụng một hàm cho phần tử đầu tiên của 
danh sách, kết quả của hàm là phần còn lại của danh sách. Chương 
trình hàm viết như sau: 
 sd(xs) = sqrt(v) 
 where 
 n = length( xs ) 
 v = fold( plus, map(sqr, xs ))/n - sqr( fold(plus, xs)/n) 
 
 
N
i
N
i
ii NxNxxsd
1
2
1
2 //)(
Mô hình Logic (Logic Model) 
• Bao gồm một tập các giá trị, các định nghĩa về quan hệ và các 
luật suy diễn. 
• Chương trình logic bao gồm các định nghĩa về các quan hệ và 
một sự tính toán là một phép thử. 
• Ví dụ 1: Chu vi của đường tròn: 
 circle(R, C) if Pi = 3.14 and C = 2 * pi * R. 
 Biểu diễn quan hệ giữa R và C 
 Ví dụ 2: 
 human(Socrates) 
 human(Penelope) 
 mortal(X) if human(X) 
 Để xác định xem Socrates hoặc Penelope có mortal, người ta 
xác định: ¬mortal(Y) 
1a. human(Socrates) Sự kiện (Fact) 
1b. human(Penelope) Sự kiện (Fact) 
2. mortal(X) if human(X) Luật (Rule) 
3. ¬mortal(Y) Giả sử (Assumption) 
4a. X = Y Từ 2 và 3 
4b. ¬human(Y) 
5a. Y = Socrates Từ 1 và 4 
5b. Y = Penelope 
6. Mâu thuẩn 5a, 4b, và 1a; 5b, 4b và 1b 
Cách suy diễn: 
Các giá trị (values) 
Các quan hệ (relations) 
Suy luận logic (logical 
inference) 
Program = Tập các định nghĩa quan hệ 
Computation = thiết lập phép thử (suy luận từ 
các định nghĩa) 
Lập trình logic: 
Mô hình mệnh lệnh (Imperative Model) 
• Mô hình mệnh lệnh bao gồm một tập các giá trị chứa trạng thái và toán tử ấn định 
để thay đổi trạng thái đó. Trạng thái là tập các cặp (tên – giá trị) (name-value) của 
các hằng và biến. 
• Chương trình là chuổi các ấn định và sự tính toán là chuổi các trạng thái. Mỗi bước 
trang tính toán là kết quả của một toán tử được ấn định. 
• Chuổi trạng thái: 
 S0-O0  S1 - ...  Sn-1 -On-1  Sn 
Các ô nhớ (memory cells) 
Các giá trị (values) 
Các lệnh (commands) 
Program = Chuổi tuần tự các lệnh 
Computation = Chuổi các thay đổi về trạng thái 
Lập trình mệnh lệnh 
• Ví dụ: Tính chu vi đường tròn: 
 constant pi = 3.14 
 input (R) 
 C := 2 * pi * R 
 Output (C) 
• Việc tính toán yêu cầu xác định giá trị của R và pi từ một trạng thái và sau đó thay 
đổi trạng thái C với một giá trị mới. 
 constant pi = 3.14 
 R _|_, C = _|_, pi=3.14 
 input (R) 
 R x, C = _|_, pi=3.14 
 C := 2 * pi * R 
 R x, C = 2 × x × pi, pi=3.14 
 Output (C) 
 R x, C = 2 × x × pi, pi=3.14 
 ( _|_ ký hiệu giá trị chưa được định nghĩa) 
• Mô hình mệnh lệnh thường được gọi là mô hình thủ tục (procedural model) vì các 
nhóm toán tử được trừu tượng hoá thành các thủ tục. 
Mẫu hình lập trình 
• Một mẫu hình lập trình (programming paradigm) là một kiểu 
lập trình mà nó là kiểu có tính mẫu hình trong tiến hành về 
công nghệ phần mềm. 
 Chẳng hạn: Lập trình hàm, lập trình logic, Lập trình cấu trúc 
 Lập trình hướng đối tượng 
• Một mẫu hình lập trình cung cấp và xác định quan điểm mà 
người lập trình về sự thực thi của chương trình. 
 Ví dụ: trong lập trình hướng đối tượng, các lập trình viên có thể xem một 
chương trình như là một tập họp của các đối tượng có tính tương tác, 
trong khi đó, trong lập trình hàm, một chương trình có thể được xem như 
là một chuỗi các đánh giá của các hàm vô hướng. 
• Các nhóm khác nhau trong công nghệ phần mềm đề xướng 
các phương pháp khác nhau, các ngôn ngữ lập trình khác 
nhau (tức là các mẫu hình lập trình khác nhau). 
 • Một số ngôn ngữ được thiết kế để hỗ trợ một mẫu hình đặc thù 
 Ví dụ: 
 - Haskell: hỗ trợ lập trình hàm. 
 - Pascal: hỗ trợ lập trình cấu trúc 
 - Java: hỗ trợ lập trình hướng đối tượng 
 - Số ngôn ngữ khác lại hỗ trợ nhiều mẫu hình (Python,Common, Lisp). 
• Quan hệ giữa các mẫu hình lập trình và các ngôn ngữ lập trình có 
thể phức tạp vì một ngôn ngữ có thể hỗ trợ nhiều mẫu hình lập 
trình. 
 Ví dụ: C++ được thiết kế để hỗ trợ các phần tử của lập trình thủ tục, lập trình 
hướng đối tượng. 
• Mặc dù vậy, những người thiết kế và những người lập trình quyết 
định làm thế nào để xây dựng một chương trình dùng các phần tử 
của mẫu hình. 
Ví dụ: Người ta có thể viết một chương trình hoàn toàn theo kiểu lập trình thủ 
tục trong C++, cũng có thể viết chương trình hoàn toàn hướng đối tượng, hay 
viết chương trình có các phần tử của cả hai mẫu hình. 
Nhìn lại một số ngôn ngữ lập trình đã học 
• Assembler 
• Turbo–pascal 
• C/C++ 
• Java 
• .NET 
Chương 2 
Lập trình hàm 
• Mô hình tính toán dựa trên các khái niệm toán học của hàm 
• Mỗi hàm có thể không đối số hoặc nhiều đối số. Kết quả của 
hàm là một giá trị xác định hoặc dự báo. 
• Ngôn ngữ lập trình hàm: FP, Haskell, Gopher 
1. Lập trình hàm 
• Số tự nhiên: Toán học: 1,2 
 Khoa học tính toán: 0,1,2 
• Ví dụ về một hàm đơn giản: Hàm tính giai thừa: 
 fact(n) = 1 x 2 x 3 x n với fact(0) =1 
• Có thể được viết theo các dạng: 
• Dễ thấy rằng, định nghĩa sau là tương đương với 2 định nghĩa trước (có thể chứng 
minh bằng quy nạp) 
2. Định nghĩa của hàm 
3. Nhìn qua dạng thức định nghĩa hàm trong 
lập trình hàm: 
• Xét hàm fact(n) với định nghĩa toán học: 
Cách 1: Đ/n thông qua biểu thức 
 fact1 :: Int -> Int 
 fact1 n = if n==0 then 1 else n* fact1 (n-1) 
-Dòng 1: Xác định kiểu cho hàm fact1 theo cú pháp object :: type 
 fact1 được định nghĩa như là một hàm (ký hiệu ->) với một tham 
 số có kiểu Int (thứ nhất) và kiểu trả lại của hàm là Int (thứ hai) 
 (Gopher không định nghĩa tập tự nhiên nên tạm sử dụng kiểu Int) 
-Dòng 2: Khai báo nội dung của hàm fact1, nó có dạng: 
 fname params = body 
 Trong đó: fname: Tên của hàm 
 params: các tham số của hàm (có thể không có) 
 body : một biểu thức xác định giá trị của hàm. 
 ví dụ trên là biểu thức if – then - else 
Cách 2: Lập các trường hợp 
• Lập các trường hợp cho việc kiểm tra các phương trình điều kiện. Nếu 
trường hợp nào đúng thì thực hiện việc lấy giá trị theo trường hợp đó. 
• Khi thực hiện, hàm sẽ được duyệt từ trên xuống dưới các trường hợp 
để kiểm tra điều kiện 
• Có thể thay otherwise bằng điều kiện n>=1 để khẳng định chỉ xét trong 
phạm vi số tự nhiên 
Cách 3: Đối sánh mẫu 
• Được gọi là cách đối sánh mẫu (pattern matching). Gopher sẽ đối sánh 
giá trị của tham số với mẫu giá trị sau fact3 để xác định giá trị cần tìm 
• Hàm sẽ được duyệt từ trên xuống dưới để tìm mẫu đúng 
• Xác định chỉ xét trong phạm vi số tự nhiên 
Cách 4: Sử dụng hàm thư viện 
- 1..n được gọi là biểu thức danh sách (list). Nó phát sinh các số 
nguyên từ 1 đến n. 
- Hàm product là hàm nhân. Nó thực hiện việc nhân tất cả các 
phần tử trong danh sách [1..n] 
Đánh giá: Nhiều người cho rằng fact5 và fact6 là tốt hơn các 
cách xây dựng khác. 
4. Lập trình hàm bằng Gopher 
Khởi động Gopher 
 Gopher 
File nguồn: 
 - Là file script có phần mở rộng là .hs, .has, .gs, .gof chứa các định nghĩa về 
hàm, toán tử, kiểu 
 - file với phần mở rộng .prelude (hoặc .pre) là file thư viện chứa các hàm, 
toán tử, kiểu được định nghĩa sẵn 
Chú thích trong file nguồn: 
 {- và -} hoặc - - 
Ví dụ về file nguồn facts.gs cho gopher 
Một số lệnh thông dịch của gopher 
Ví dụ về thực hiện tính biểu thức và hàm thư viện trên dòng lệnh 
bằng gopher 
Khoảng thời gian cần tính và 
số ô nhớ được sử dụng 
Ví dụ về việc nộp và thực hiện các hàm trong chương trình 
• Nộp file chương trình facts.gs nói trên 
• Liệt kê tên hàm trong chương trình 
• Gọi thực hiện các hàm trong chương trình 
5. Cơ bản về lập trình hàm 
bằng Gopher 
5.1. Các kiểu định nghĩa sẵn (built-in types) 
Giá trị: True, False 
Toán tử : && (and) || (or) và not 
-Bao trong cặp dấu nháy đơn „ : „a‟, „A‟ 
- Kí tự escape : „\n‟ , „\t‟ , „\\‟ , „\‟‟ 
- Viết dụng mã : „\65‟ , „\x41‟ đều cho „A‟ 
- Nếu t1 và t2 là các kiểu thì tham số kiểu là t1 và hàm có kiểu là t2 
- Tên hàm và biến bắt đầu bởi một chữ cái (a..z) thường, sau đó có thể 
là chử cái (hoa, thường), chử số, dấu nháy đơn („), dấu gạch dưới (_). 
- Hàm có thể có nhiều tham số. Ví dụ hàm cộng 2 số nguyên: 
 f x y = x +y ( add x y) 
 được hiểu như là : f x + y = (f x) + y 
 Vì vậy tham số được khai báo: 
Hoặc 
 Kiểu danh sách là kiểu có cấu trúc đầu tiên của Gopher, nó là một chuổi 
các phần tử cũng kiểu giá trị. 
 Khai báo [t] là khai báo kiểu danh sách có các phẩn tử có giá trị kiểu t 
 Ví dụ [Int] 
 Một danh sách có thể rỗng. Được ký hiệu [] 
 Một danh sách khác rỗng có một phần tử đầu danh sách (head element) 
và phần đuôi của danh sách (tail) 
 Toán tử : (dấu hai chấm) biểu diễn phần tử đầu và phần đuôi của danh 
sách. Nó có dạng head:tail 
 Ví dụ: [] 2: [] 2: (3: []) 
 5: (3: (2 : []) có thể viết : 5:3:2:[] hoặc [5,3,2] 
• Có 2 hàm thực hiện trên kiểu danh sách là head và tail: 
Ví dụ: head [5,3,2] cho lại 5 
 tail [5,3,2] cho danh sách [3,2] 
• Là kiểu đã được định nghĩ như là mảng (danh sách) các ký tự: 
• Hằng chuổi có thể được viết các dạng khác nhau: 
• Vì là danh sách nên có thể áp dụng các hàm head và tail: 
Ví dụ về hàm tính độ dài chuổi (len) 
Hàm thực hiện như sau: 
Có thể mở rộng hàm len để tính số phần 
tử của một danh sách với: 
 len :: [a] -> Int 
- t1, t2,, tn là các kiểu (có thể khác nhau) 
- Không giống như list, tuple có các phần tử không nhất thiết là 
cùng kiểu và số phần tử của tuple là cố định. Nó tương tự như kiểu 
Record trong Turbo – Pascal 
- Ví dụ về tuple: 
5.2. Lập trình với list 
• Tổng của một list (sumlist) 
- Cho danh sách gồm các phần tử nguyên v1, v2, v3, v4,  ,vn 
- Tính tổng S = v1+ v2 + v3+  + vn 
- Xây dựng hàm sumlist như sau: 
- Có thể thay dòng thứ 3 bởi: 
• Hàm tính độ dài của một list (length’) 
Trong hàm này, ký hiệu (_) được hiểu như là “không quan tâm” 
đến giá trị của phần tử head 
• Bỏ đi các phần tử trùng lặp trong list 
Để ý rằng, dòng thứ 2 chỉ thực hiện khi danh sách có không ít 
hơn 2 phần tử 
• Một số mẫu khác 
Dưói đây là một số mẫu tham số, đối số tương ứng và kết quả của 
việc thử 
5.3. Các toán tử infix 
Các toán tử infix, infixl, infixr được sử dụng để định nghĩa thứ tự thực 
hiện các toán tử trong biểu thức. 
 - infix : tự do 
 - infixl : từ trái sang phải 
 - infixr : từ phải sanf trái 
Ví dụ về các toán tử infix chuẩn: 
5.4. Các kiểu đệ quy 
• Nối danh sách (toán tử ++) 
 - Tức là nối danh sách thứ hai vào sau danh sách thứ nhất để được một danh 
sách 
 - Hàm nối 2 danh sách được ký hiệu theo kiểu toán tử ++ . 
 - Đặt infix mức 5 cho toán tử ++ (mở rộng thêm infixr 5): 
 infixr 5 ++ 
- Định nghĩa toán tử ++ 
Ví dụ thực hiện toán tử ++ 
- Dễ thấy rằng, độ phức tạp tính tính là O(n) 
- Nếu xs , ys , zs là các danh sách hữu hạn thì: 
 (xs ++ ys) ++ zs = xs ++ (ys ++ zs) 
- Và: 
 [] ++ xs = xs = xs ++ [] 
Nhận xét: 
• Đảo các phẩn tử của list (rev) 
Trong đó toán tử ++ chính là toán tử nối chuổi ở trên 
Ví dụ: 
- Độ phức tập tính toán là O(n2) 
- Nếu xs và ys là các danh sách hữu hạn thì: 
 rev (xs + ys) = rev xs + rev ys 
- Và: rev ( rev xs) = xs 
Nhận xét: 
• Đảo với đệ quy phần đuôi của list 
• Các định nghĩa cục bộ (let và where) 
let 
Let thường được sử dụng trong các tập định nghĩa lồng nhau. Nó có 
dạng: 
Let được sử dụng bất kỳ ở đâu xuất hiện một biểu thức. 
Ví dụ: Hàm f sau đây lập một danh sách mà mỗi phần tử là bình phương 
của danh sách tăng từ 1 (1,2,3). 
 square là hàm một biến 
 one là một hàm không tham số (nó là một hằng = 1) . 
 (y: ys) biểu thị một mẫu đối sánh của đối số xs của hàm f 
 Tham chiếu y hoặc ys của đối số xs của f có thể gây lỗi khi xs là rỗng 
 Các định nghĩa cục bộ square, one, y và ys có phạm vi sử dụng trong 
biểu thức sau in 
 Các định nghĩa cục bộ có thể truy xuất các thành phần thuộc phạm vi 
của nó (outer) . Chẳng hạn: định nghĩa (y:ys) truy xuất xs 
 Các định nghĩa cục bộ có thể đệ quy và gọi mỗi định nghĩa khác. 
 Mệnh đề let trình diễn theo dạng bottom-up. Nó xác định các thành phần 
trước khi được sử dụng 
where 
-Tương tự như let nhưng theo kiểu top-down. 
- where sử dụng linh hoạt hơn let. Nó cho phép định nghĩa các thành 
phần trong các mệnh đề trong khi let chỉ định nghĩa các thành phần 
trong một biểu thức (sau in) . 
- Ví dụ: 
Trong ví dụ này where định nghĩa cho các thành phần trong cả 3 mệnh 
đề . Lưu ý rằng, where được bắt đầu cùng cột với (=) 
Một ví dụ sử dụng định nghĩa cục bộ: Số Fibonacci 
Số Fibonacci: f(0) = 0 
 f(1) = 1 
 f(n+2) = f(n) + f(n+1) 
Định nghĩa hàm fib theo công thức trên: 
Trong định nghĩa trên, hàm fib sử dụng n+2 mẫu để đối sánh các số 
tự nhiên >=2 
Định nghĩa sau đây được coi là có hiệu suất cao hơn khi sử dụng đệ quy 
với việc cộng 2 số: 
Ví dụ thực hiện hàm: 
-Việc thực hiện fib n qua 
O(fib(n)) reduction. 
-Việc thực hiện fib‟ n chỉ 
qua O(n) reduction 
5.5. Các toán tử list khác 
• Chọn một phần tử của list (!!) 
Toán tử !! trong biểu thức xs !! n cho phép chọn phần tử thứ n 
trong danh sách xs. Phần tử head có vị trí 0 
Nó là toán tử chuẩn và được định nghĩa như là: 
• Ngắt một list (take và drop) 
take và drop là hai toán tử chuẩn 
take n xs : cho lại n phần tử đầu tiên của danh sách xs. Nó được định 
nghĩa như là: 
drop n xs : bỏ đi n phần tử đầu tiên của danh sách xs. Nó được định 
nghĩa như là: 
Ví dụ: 
• Liên hợp list (zip) 
zip là toán tử chuẩn, cho phép hợp hai danh sách thành một danh 
sách theo kiểu ghép cặp các phần tử tương ứng. 
Ví dụ: 
zip được định nghĩa như là zip‟ dưới đây: 
5.6. Một ví dụ: Xây dựng tập số hữu tỷ 
• Số hữu tỷ 
Toán học: số hữu tỷ được định nghĩa như là: x/y với y0 
Gopher : Định nghĩa như là một tuple: 
 Type Rat = (Int, Int) 
Ví dụ: (1,7), (-1,-7), (3,21) đều biểu diễn số 1/7 
• Chuẩn hoá số hữu tỷ 
- Chuyển (a,b) thành (x,y) trong đó x,y là nguyên tố cùng nhau 
- Quy ước (0,y) = (0,1) 
- Hàm chuẩn hoá số hữu tỷ: 
Việc xây dựng hàm normRat dựa vào các hàm: 
signum: hàm chuẩn lấy dấu của một số -1 (âm) , 0 (không), 1 (dương). 
Nó được định nghĩa như là: 
a được khai báo là một đối tượng thuộc lớp Num và lớp Ord 
gcd: hàm chuẩn cho lại ước số chung lớn nhất. Nó được định nghĩa như 
là: 
abs: hàm chuẩn cho lại trị tuyệt đối của một số 
error: hàm chuẩn hiển thị thông báo lỗi lên màn hình 
showRat: hàm hiển thị số hữu tỷ lên màn hình (xem sau) 
• Các phép toán trên số hữu tỷ 
• Hiển thị số hữu tỷ 
Trong đó hàm show là một hàm chuẩn. Nó convert một số 
nguyên thành dạng chuổi 
6. Các hàm bậc cao 
6.1. Hàm map 
Xét hai hàm sau đây: 
Bình phương mỗi phần tử trong list: 
Độ dài mỗi list con trong list: 
Hàm map cho phép thực hiện một hàm f lên mỗi phần tử của một 
danh sách. Nó được định nghĩa như là: 
Nếu áp dụng hàm map cho 2 hàm ví dụ trên, ta có: 
6.2. Hàm filter 
Xét hai hàm sau đây: 
Hàm cho lại một list các số chẳn từ một list 
Hàm cho lại một list bằng cách gấp đôi phần tử dương của một list 
Hàm chuẩn filter với một hàm điều kiện p (kiểu a->Bool) và một danh sách 
kiểu [a] cho lại một danh dách gồm các phần tử trong danh sách [a] thỏa 
hàm điều kiện p 
Nó được định nghĩa như là: 
Áp dụng hàm filter, các hàm trên có thể định nghĩa lại như sau: 
6.3. Các hàm Fold 
Xét hai hàm sau đây: 
Hàm nối các chuổi thành một chuổi 
Hàm cho lại tổng các phần tử trong list 
Áp dụng hàm foldr , các hàm trên được định nghĩa như sau: 
Trong đó: f là một toán tử nhị phân có dạng (a->b->b). Foldr thực hiện kiểu 
như: 
Hàm foldr được định nghĩa như sau: 
Hàm foldr 
Áp dụng khác: 
Hàm foldl 
Nó thực hiện kiểu như: 
Ví dụ áp dụng: 
6.4. Tính bộ phận 
Xét hai hàm sau: 
Cả hai đêu cho cùng kết quả nhưng khác nhau về đối số: 
 add: đối số là một 2-tiple và cho lại một Int 
 add‟: đối số là một Int và cho lại một hàm (Int->Int) 
Do đó: 
 add 3  Lỗi 
 add‟ 3  được hiểu là gán 3 cho đối số của nó 
 ((add‟ 3) 4) -> gán x, y với 3 và 4 và được 3+4 
Tức: 
Hàm add‟ định nghĩa như trên là tương tự như hàm chuẩn (+) (toán tử +) 
 Khi viết: (+) 3 có thể hiểu “3 là cộng với” 
Các toán tử khác như (<), (*) .. cũng tương tự 
Ví dụ: 
Chú ý rằng: (*) 2 được hiểu là 2 nhân với 
 (<) 0 được hiểu là 0 là bé hơn 
6.5. Toán tử section 
Rõ ràng, cách viết ( (<) 0 ) như trên rất khó nhớ nó là True khi phần 
tử so sánh là dương. 
Hàm chuẩn flip định nghĩa dưới đây dùng để đảo 2 đối số: 
Ví dụ: 
Gopher cung cấp toán tử section sau đây: 
 là toán tử , e là một biểu thức 
Thì : 
được hiểu là 
được hiểu là 
Ví dụ: Tăng đối số lên 1 
Kiểm tra một số dương 
Hàm chia đôi 
Hàm nghịch đảo 
lập phương các phần tử 
6.6. Hàm hợp 
Toán tử (.) đưưọc sử dụng để viết các hàm hợp 
Ví dụ: 
Được viết: 
Tham số của doit được hiểu là ngầm định 
Các hàm hợp thực hiện từ phải sang trái. Hàm id như là 
phần tử định danh 
Ví dụ 2: Hàm nhân đôi các phần tử dương trong danh sách 
Hàm count có 2 tham số: một số nguyên n và một danh sách các 
danh sách. Hàm cho lại số danh sách có độ dài là n 
Ví dụ 1: Hàm cho lại số danh sách có độ dài là n 
Có thể viết lại: 
Ví dụ 3: Các hàm cho phần tử cuối và phần khởi đầu danh sách (tức trừ 
phần tử cuối) 
Có thể viết lại: 
Ví dụ 4: Các hàm khác 
6.7. Biểu thức Lambda 
Xét hàm sau: 
Gopher cho phép định nghĩa các hàm không tên (được gọi là biểu 
thức lambda) theo cú pháp: 
Ví dụ: hàm sq trên có thể viết lại 
 ( \x -> x*x ) 
Mẫu x biểu diễn cho đối số của hàm không định tên và kết quả của hàm 
này là x*x. 
Và ta có thể viết lại : 
 squareAll‟ :: [Int] -> [Int] 
 squareAll‟ xs = map (\x -> x*x) xs 
Tương tự có cho các hàm có nhiều tham số. Chẳng hạn 
 Biểu thức (\x y -> (x+y)/2) 
Biểu thị hàm không định tên với 2 đối số và kết quả trả lại là trung bình công 
của 2 đối số 
Ví dụ khác: 
Hàm không tên (\n _ -> n+1) với đối số đầu là n, đối số thứ 2 là 
bất kỳ, cho lại một giá trị bằng cách tăng n lên 1 
6.8. Thêm các toán tử ngắt danh sách 
Ngoài toán tử chuẩn take và drop, còn có các toán tử span, break, 
takeWhile, dropWhile, takeUntil, dropUntil 
Ví dụ về hàm takeWhile và dropWhile 
Chú ý: mẫu xs@(x:xs‟) đối sánh một danh sách khác rỗng với phần 
head và tail tương ứng x và xs‟ 
Ví dụ: Cắt các ký tự rỗng đầu một chuổi 
 dropWhile ( (==) „ „) “ functional programming” 
6.8. Thêm các toán tử liên hợp danh sách 
6.9. Một ví dụ: Bổ sung các toán tử quan hệ cho tập 
số hữu tỷ 
7. Các vấn đề khác về danh sách 
7.1. Chuổi 
Một chuổi số học là một chuổi của các phần tử kiểu liệt kê (tức thuộc 
lớp Enum). Các kiểu như Int, Float, Char thuộc Enum 
• [m..n] dẫn suất một ds các phần tử từ m đến n theo bước 1 
Dẫn xuất này phát sinh từ hàm chuẩn enumFromTo m n 
• [m, m’..n] dẫn suất một ds các phần tử từ m’ đến n với bước m’-m 
Dẫn xuất này phát sinh từ hàm chuẩn enumFromThenTo m m’ n 
• [m..] , [m, m’] tương tự như các trường hợp trên nhưng với ds vô 
hạn 
Các dẫn xuất này phát sinh từ hàm chuẩn enumFrom và enumFromThen 
Một chuổi hình học là một chuổi mà các phần tử thuộc kiểu có thứ tự và 
là kiểu số (nghĩa là thuộc lớp Ord và Num) 
Ví dụ: 
7.2. Bao hàm 
Danh sách bao hàm có dạng: 
Trong đó: expression là biểu thức bất kỳ. Qualifier là hạn định phát sinh 
cho việc phát sinh danh sách bao hàm. 
Có 3 loại là pháty sinh (generator), lọc (filter) và định nghĩa cục bộ (local 
definition) 
Phát sinh: 
Một phát sinh là một qualifier có dạng pat <-exp trong đó exp laf 
một biểu thức list-valued . Nó trích mỗi phần tử của exp đối sánh 
với mẫu (pat) theo thứ tự xuất hiện của nó trong danh sách exp. 
Phần tử nào không thỏa thì bị bỏ qua. 
Ví dụ: 
Lọc: 
Biểu thức giá trị kiểu Bool cũng có thể được sử dụng như là một 
qualifier. Chỉ có giá trị nào làm cho biểu thức là True mới được 
phát sinh cho danh sách bao hàm. 
Ví dụ: 
Định nghĩ cục bộ: 
Một qualifier có dạng pat = expr đưa vào một định nghĩa cục bộ 
cho danh sách bao hàm: 
Chú ý: n==2 là lọc, n=2 là định nghĩa cục bộ 
Một số ví dụ khác: 
Vài ví dụ áp dụng: 
Ví dụ 1: Chuổi bao gồm các ký tự trống 
Ví dụ 2: Kiểm tra một số nguyên là số nguyên tố 
Ví dụ 3: Bình phương của các số nguyên tố 
Hàm sqPrimes sau đây cho lại danh sách mà mỗi phần tử là bình 
phương của số nguyên tố từ m đến n: 
Có thể định nghĩa khác cho hàm này: 
Ví dụ 4: Nhân đôi các phần tử dương 
Ví dụ 5: Nối một danh sách của danh sách của danh sách 
Hàm này cũng có thể định nghĩa: 
Ví dụ 6: Tìm vị trí xuất hiện trong danh sách 
Tìm tất cả các vị trí 
Tìm vị trí xuất hiện đầu tiên: 
8. Các kiểu dữ liệu khác 
8.1. Kiểu tự định nghĩa 
Ví dụ: 
Cú pháp: 
Trong đó: 
- Tên kiểu phải viết hoa chữ cái đầu 
- a1, a2,an là các biễu diễn kiểu cho n tham số của kiểu dữ liệu 
- constr1, constr2constrn là các cấu tử dữ liệu 
Cấu tử dữ liệu có thể là giá trị kiểu liên hợp 
Trong định nghĩa này, Grayscale ngầm định định nghĩa một hàm 
cấu tử dạng Int -> Color‟ 
Ví dụ khác: 
Biểu diễn một điểm (tọa độ) có kiểu a. Cấu tử Pt ngầm định định 
nghĩa một hàm dạng a->a->Point a 
Biểu diễn một tập. Để ý rằng Set được sử dụng trang cả cấu tử 
kiểu lẫn cấu tử dữ liệu 
Hàm sau đây sẽ chuyển một danh sách vào một Set 
Hàm chuẩn nub loại bỏ các phần tử double trong danh sách. 
Định nghĩa kiểu kết quả: 
Định nghĩa dạng ma trận 
Định nghĩa kiểu kết quả với giá trị trả lại là maxint trong trường hợp 
chia cho 0: 
Hoặc sử dung biểu thức case: 
8.2. Kiểu dữ liệu đệ quy 
Kiểu dữ liệu cũng có thể được định nghĩa dạng đệ quy. 
Ví dụ: Kiểu sau đây định nghĩa một cây nhị phân với một giá trị trêm 
mỗi node: 
Mỗi cây có thể là empty hoặc là một Node trrong đó mỗi node gồm một 
giá trị kiểu a và hai cây con “trái” và “phải” 
Đề nghị đọc thêm tài liệu về kiểu dữ liệu đệ quy 
Các tài liệu đề nghị đọc thêm 
• Functional Programming - Jeroen Fokker 
 (có bài tập) 
• Áp dụng các hàm họ take trong gofer - 
Nguyễn Lương Thục 
Chương 3 
Lập trình cấu trúc 
1. Khái niệm 
• Lập trình cấu trúc (structured programming) (hay còn gọi là lập 
trình modul (modular programming) là tập con của lập trình thủ 
tục (procedural programming) trong đó cấu trúc logic trong một 
chương trình dễ viết, dễ đọc, dễ hiểu, dễ kiểm lỗi và dễ hiệu 
chỉnh hơn. 
• Một số ngôn ngữ như Ada, Pascal, C, dBASE, FoxBase... được 
thiết kế với các đặc trưng đảm bảo cho việc thể hiện cấu trúc 
logic của chương trình. 
• Mặc dù vậy, một vài ngôn ngữ vẫn đưa vào một số phần tử 
không tuân thủ hoàn toàn logic cấu trúc của nó. (ví dụ lệnh goto 
trong Pascal) 
• Lập trình có cấu trúc thường sử dụng một mô hình thiết kế 
từ trên xuống (top-down): 
– Cấu trúc tổng thể của chương trình được phân thành các 
phần con riêng biệt. 
– Một hàm hoặc một tập các hàm sẽ được coded cho từng 
module hoặc các module con. Điều này có nghĩa là code có 
thể được nộp vào bộ nhớ hiệu dụng hơn và các module đó có 
thể sử dụng lại cho các chương trình khác. 
– Sau khi một module đã được kiểm thử, nó sẽ được tích hợp 
vào cấu trúc chương trình tổng thể. 
• Lập trình cấu trúc có các đặc trưng sau: 
– Tính đơn thể 
– Cấu trúc điều khiển 
– Tính vào/ra đơn. 
Tính đơn thể (modules) : 
– Các vấn đề trong lập trình được phân rã thành những phần nhỏ hơn gọi 
là các đơn thể. 
– Mỗi đơn thể, được gọi là một chương trình con, sẽ thực hiện một 
nhiệm vụ định trước trong chương trình. 
– Ðiểm lợi chính của kỹ thuật này là nó đơn giản hóa việc phát triển 
chương trình vì mỗi đơn thể chương trình có thể được phát triển một 
cách độc lập. 
– Khi các đơn thể được lắp ghép lại, chúng tạo thành một chương trình 
hoàn chỉnh tạo ra được kết quả mong muốn. 
Cấu trúc điều khiển (control structure) : 
Trong lập trình cấu trúc, người ta sử dụng 3 cấu trúc điều khiển 
để tạo nên logic của chương trình. 
• Cấu trúc tuần tự (sequence), 
• Cấu trúc chọn (selection) 
• Cấu trúc lặp (iteration). 
Bất kz một thuật toán nào, chỉ cần 3 cấu trúc điều khiển này là 
biểu diễn được nó. 
Vào/ra đơn (single entry/exit) : 
Đây là một khái niệm quan trọng trong lập trình cấu trúc. Vào/ 
ra đơn nghĩa là chỉ có một điểm vào và một điểm ra đối với mỗi 
cấu trúc trong 3 cấu trúc ở trên. 
2. Lập trình cấu trúc 
• Cấu trúc chung của một chương trình 
• Cú pháp, bộ ký hiệu, định danh, bộ từ khóa 
• Các loại dữ liệu đơn 
• Hằng, biến, toán tử, biểu thức 
• Tập lệnh (lệnh đơn, lệnh có cấu trúc) 
• Chương trình con 
• Dữ liệu kiểu mảng 
• Dữ liệu kiểu chuổi 
• Dữ liệu kiểu cấu trúc 
• Biến kiểu File 
• Con trỏ, biến con trỏ, biến cấp phát động 
• Một số vấn đề khác 
3. Ưu và nhược điểm 
Ưu điểm lập trình cấu trúc: 
 Việc chia nhỏ một chương trình lớn thành các chương trình con giúp cho 
lập trình viên dễ nhận biết và quản lí chương trình tốt hơn. 
Nhược điểm trong lập trình cấu trúc: 
• Vấn đề thiếu đóng gói: Đóng gói (encapsulation) được hiểu là việc nhóm các 
khái niệm liên quan thành một đơn vị. Điều ngày cho phép tham chiếu đến 
nó thông qua một tên gọi. Nói cách khác là lập trình viên chỉ cần nhớ việc 
giao tiếp với nó mà không cần nhớ nội dung bên trong nó. 
• Vấn đề lặp lại code: Vì code được viết có thể xuất hiện trong các phần khác 
nhau của chương trình, nó có thể sinh ra vấn đề tại vị trí của nó. Do 
chương trình có các biến, điều đó có nghĩa là chúng có thể có các giá trị 
khác nhau tại các phần khác nhau của chương trình. Do đó, cần phải kiểm 
tra để phát hiện lỗi và điều này sẽ mất nhiều thời gian. 
• Vấn đề thiếu sự che dấu thông tin: Thông tin ẩn liên qua đến việc thay đổi 
thiết kế chương trình. Nó cho phép bảo vệ các phần khác của chương 
trình không bị thay đổi khi phải thiết kế lại ở một phần nào đó. 
Chương 4 
Lập trình hướng đối tượng 
(object-oriented programming) 
• Là phương pháp lập trình hỗ trợ công nghệ đối tượng. 
• OOP được xem là giúp tăng năng suất, đơn giản hóa độ phức 
tạp khi bảo trì cũng như mở rộng phần mềm bằng cách cho 
phép lập trình viên tập trung vào các đối tượng phần mềm ở 
bậc cao hơn. 
• Những đối tượng trong một ngôn ngữ OOP là các kết hợp giữa 
mã và dữ liệu mà chúng được nhìn nhận như là một đơn vị duy 
nhất. 
• Mỗi đối tượng có một tên riêng biệt và tất cả các tham chiếu 
đến đối tượng đó được tiến hành qua tên của nó. Như vậy, mỗi 
đối tượng có khả năng nhận vào các thông báo, xử lý dữ liệu 
(bên trong của nó), và gửi ra hay trả lời đến các đối tượng khác 
hay đến môi trường. 
3.1. Giới thiệu 
• Đối tượng: Các dữ liệu và chỉ thị được kết hợp vào một đơn vị đầy đủ tạo nên 
một đối tượng. Đơn vị này tương đương với một chương trình con và vì thế 
các đối tượng sẽ được chia thành hai bộ phận chính: phần các phương thức 
(method) và phần các thuộc tính (property). 
• Trong thực tế, các phương thức của đối tượng là các hàm và các thuộc tính 
của nó là các biến, các tham số hay hằng nội tại của một đối tượng (hay nói 
cách khác tập hợp các dữ liệu nội tại tạo thành thuộc tính của đối tượng). 
• Các phương thức là phương tiện để sử dụng một đối tượng trong khi các 
thuộc tính sẽ mô tả đối tượng có những tính chất gì. Các phương thức và các 
thuộc tính thường gắn chặt với thực tế các đặc tính và sử dụng của một đối 
tượng. 
Trong thực tế, các đối tượng thường được trừu tượng hóa qua việc định nghĩa 
của các lớp (class). 
• Tập hợp các giá trị hiện có của các thuộc tính tạo nên trạng thái của một đối 
tượng. 
Mỗi phương thức hay mỗi dữ liệu nội tại cùng với các tính chất được định 
nghĩa (bởi người lập trình) được xem là một đặc tính riêng của đối tượng. 
3.2. Các tính chất cơ bản 
Tính trừu tượng (abstraction): 
 Là khả năng của chương trình bỏ qua hay không chú ý đến một số khía cạnh 
của thông tin mà nó đang trực tiếp thực hiện, nghĩa là nó có khả năng tập 
trung vào những cốt lõi cần thiết. 
 Mỗi đối tượng có thể hoàn tất các công việc một cách nội bộ, liên lạc với 
các đối tượng khác mà không cần cho biết làm cách nào đối tượng tiến 
hành được các thao tác. Tính chất này thường được gọi là sự trừu tượng 
của dữ liệu. 
 Tính trừu tượng còn thể hiện qua việc một đối tượng ban đầu có thể có 
một số đặc điểm chung cho nhiều đối tượng khác như là sự mở rộng của 
nó. 
 Tính trừu tượng này thường được xác định trong khái niệm gọi là lớp trừu 
tượng hay lớp cơ sở trừu tượng. 
Tính đóng gói (encapsulation) và che dấu thông tin (information hiding): 
• Tính chất này không cho phép người sử dụng các đối tượng thay đổi 
trạng thái nội tại của một đối tượng. 
• Chỉ có các phương thức nội tại của đối tượng cho phép thay đổi trạng 
thái của nó. 
• Việc cho phép môi trường bên ngoài tác động lên các dữ liệu nội tại của 
một đối tượng theo cách nào đó là hoàn toàn tùy thuộc vào người viết 
mã. Đây là tính chất đảm bảo sự toàn vẹn của đối tượng. 
Tính đa hình (polymorphism): 
• Thể hiện thông qua việc gửi các thông điệp (message). Việc gửi các thông 
điệp này có thể so sánh như việc gọi các hàm bên trong của một đối tượng. 
• Các phương thức dùng trả lời cho một thông điệp sẽ tùy theo đối tượng mà 
thông điệp đó được gửi tới sẽ có phản ứng khác nhau. 
• Người lập trình có thể định nghĩa một đặc tính (chẳng hạn thông qua tên 
của các phương thức) cho một loạt các đối tượng gần nhau nhưng khi thi 
hành thì dùng cùng một tên gọi mà sự thi hành của mỗi đối tượng sẽ tự 
động xảy ra tương ứng theo đặc tính của từng đối tượng mà không bị nhầm 
lẫn. 
Ví dụ khi định nghĩa hai đối tượng "hinh_vuong" và "hinh_tron" thì có một 
phương thức chung là "chu_vi". Khi gọi phương thức này thì nếu đối tượng 
là "hinh_vuong" nó sẽ tính theo công thức khác với khi đối tượng là 
"hinh_tron". 
Tính kế thừa (inheritance): 
• Đặc tính này cho phép một đối tượng có thể có sẵn các đặc tính mà đối 
tượng khác đã có thông qua kế thừa. 
• Điều này cho phép các đối tượng chia sẻ hay mở rộng các đặc tính sẵn có 
mà không phải tiến hành định nghĩa lại. 
3.3. Một số khái niệm 
Lớp (class) 
• Một lớp có thể được hiểu là khuôn mẫu để tạo ra các đối tượng. Trong 
một lớp, người ta thường dùng các biến để mô tả các thuộc tính và các 
hàm để mô tả các phương thức của đối tượng. 
• Khi đã định nghĩa được lớp, ta có thể tạo ra các đối tượng từ lớp này. Để 
việc sử dụng được dễ dàng, thông qua hệ thống hàm tạo (constructor), 
người ta dùng lớp như một kiểu dữ liệu để tạo ra các đối tượng. 
Lớp con (subclass) 
• Lớp con là một lớp thông thường nhưng có thêm tính chất kế thừa một 
phần hay toàn bộ các đặc tính của một lớp khác. Lớp mà chia sẽ sự kế 
thừa gọi là Lớp cha (parent class). 
Lớp trừu tượng hay lớp cơ sở trừu tượng (abstract class) 
• Lớp trừu tượng là một lớp mà nó không thể thực thể hóa thành một đối tượng 
thực dụng được. Lớp này được thiết kế nhằm tạo ra một lớp có các đặc tính tổng 
quát nhưng bản thân lớp đó chưa có ý nghĩa (hay không đủ ý nghĩa) để có thể tiến 
hành viết mã cho việc thực thể hóa. 
• Thí dụ: Lớp "hinh_thang" được định nghĩa không có dữ liệu nội tại và chỉ có các 
phương thức (hàm nội tại) "tinh_chu_vi", "tinh_dien_tich". Nhưng vì lớp 
hinh_thang này chưa xác định được đầy đủ các đặc tính của nó (cụ thể các biến 
nội tại là tọa độ các đỉnh nếu là đa giác, là đường bán kính và toạ độ tâm nếu là 
hình tròn, ...) nên nó chỉ có thể được viết thành một lớp trừu tượng. Sau đó, người 
lập trình có thể tạo ra các lớp con chẳng hạn như là lớp "tam_giac", lớp 
"hinh_tron", lớp "tu_giac",.... Và trong các lớp con này người viết mã sẽ cung cấp 
các dữ liệu nội tại (như là biến nội tại r làm bán kính và hằng số nội tại Pi cho lớp 
"hinh_tron" và sau đó viết mã cụ thể cho các phương thức "tinh_chu_vi" và 
"tinh_dien_tich"). 
Phương thức (method) 
• Phương thức của một lớp thường được dùng để mô tả các hành vi của đối 
tượng (hoặc của lớp). 
• Khi thiết kế, người ta có thể dùng các phương thức để mô tả và thực hiện 
các hành vi của đối tượng. 
• Mỗi phương thức thường được định nghĩa là một hàm, các thao tác để thực 
hiện hành vi đó được viết tại nội dung của hàm. 
• Một số loại phương thức đặc biệt: 
 Hàm tạo (constructor) là hàm được dùng để tạo ra một đối tượng, cài đặt 
các giá trị ban đầu cho các thuộc tính của đối tượng đó. 
 Hàm hủy (destructor) là hàm dùng vào việc làm sạch bộ nhớ đã dùng để lưu 
đối tượng và hủy bỏ tên của một đối tượng sau khi đã dùng xong. 
Thuộc tính (attribute) 
• Thuộc tính của một lớp bao gồm các biến, các hằng, hay tham số nội tại 
của lớp đó. Ở đây, vai trò quan trọng nhất của các thuộc tính là các biến vì 
chúng sẽ có thể bị thay đổi trong suốt quá trình hoạt động của một đối 
tượng. 
• Các thuộc tính có thể được xác định kiểu và kiểu của chúng có thể là các 
kiểu dữ liệu cổ điển hay đó là một lớp đã định nghĩa từ trước. 
Quan hệ giữa lớp và đối tượng 
• Lớp trong là cách phân loại các thực thể dựa trên những đặc điểm chung 
của các thực thể đó. Do đó lớp là khái niệm mang tính trừu tượng hóa rất 
cao. 
• Một đối tượng là thực thể hóa (instantiate) của một lớp đã được định nghĩa. 
• Công cộng (public) 
 Là một tính chất được dùng để gán cho các phương thức, các biến nội tại, hay các 
lớp mà khi khai báo thì người lập trình đã cho phép các câu lệnh bên ngoài cũng 
như các đối tượng khác được phép dùng đến nó. 
• Riêng tư (private) 
 Là sự thể hiện tính chất đóng mạnh nhất (của một đặc tính hay một lớp). Khi dùng 
tính chất này gán cho một biến, một phương pháp thì biến hay phương pháp đó chỉ 
có thể được sử dụng bên trong của lớp mà chúng được định nghĩa. 
• Bảo vệ (protected) 
 Chỉ có trong nội bộ của lớp đó hay các lớp có quan hệ đặc biệt mới được phép gọi 
đến hay dùng đến các thành phần được bảo vệ. Có sự khác nhau trong các ngôn 
ngữ lập trình. 
• Đa kế thừa (multiple inheritance) 
 Một lớp con có khả năng kế thừa trực tiếp cùng lúc nhiều lớp khác. 
3.4. Các nguyên lý thiết kế hướng đối 
tượng 
1. Xác định lớp đối tượng 
• Đặt câu hỏi "Ta đang nói về cái gì" chứ chưa đặt vấn đề "Ta muốn làm gì", nghĩa 
là hướng đến đối tuợng trước. Để xác định đối tượng cần phải xác dịnh lớp các 
đối tượng. 
• Việc xem xét lớp đối tượng đáng quan tâm có thể dựa trên nguyên lý sau: 
 Nếu ta nói về vấn đề nào đó bằng cách quy cho nó các tính chất hay nếu phải 
thao tác trên các vấn đề này thì cần phải xác định lớp đối tượng cho nó. 
 2. Tự lập và cục bộ: 
• Trước hết phải nghĩ dưới dạng đối tượng rồi sau đó mới nghĩ đến các phép 
tóan được áp dụng trên nó. 
• Phải nghĩ đến việc yêu cầu các đối tượng thực hiện các hành động của 
chúng chứ không đi tìm những hàm tòan cục tác động lên nó. 
3. Hợp thành và làm mịn: 
• Các đối tượng được hợp thành để tạo ra các đối tượng lớn hơn bằng cách 
sử dụng kế thừa. Đây là việc làm tự nhiên. 
• Làm mịn tức là mô tả một phần mềm theo những phần mềm đã có, không 
phải mô tả chúng theo các thành phần mà bằng cách tạo ra phần mềm 
mới từ việc kế thừa các đặc trưng của các phần mềm tổng quát hơn đã có, 
và chỉ định nghĩa những phần nào là mới trong phần mềm đang tạo ra. 
4. Các bước trong thiết kế hướng đối tượng: 
(1) Xác định thực thể trong miền: 
 Làm thế nào để xác định chính xác lớp đối tượng. Có các cánh sau: 
C1: Xem xét một lõi nhỏ các lớp ban đầu được coi là "hiễn nhiên", rồi trong quá trình thực 
hiện, có thể thêm vào các lớp mới bằng cách gộp nhón các lớp hiện có. 
C2: Vét cạn tất cả, trong quá trinh thực hiện sẽ lọai bỏ dần. 
C3: Gộp nhóm các tính chất và các quan hệ mà chưa rút ra ngay các lớp. Sau đó phân lọai 
các tính chất và từ đó xác định đối tượng. 
(2) Cấu trúc miền thông qua việc phân tích các tính chất và quan hệ: 
 Khi đã định xong các lớp, cần phải biết cách tổ chức và liên kết chúng lại 
(3) Xác định các thao tác: 
 Đầu tiên các định các thao tác mà các dối tượng cần thíết phải có sau đó mới nâng 
dần lên ở dạng trừu tượng hơn. Chẳng hạn có thể có thể năng thao tác của một đối 
tượng X lên cho lớp cơ sở được nó kế thừa và có thể lớp cơ sở thao tác này là "không 
làm gì cả". 
(4) Mô tả chính xác các thao tác: 
(5) Cho phép thực hiện 
3.5. Hạn chế của phương pháp lập 
trình OOP 
• OOP mô phỏng đối tượng trong thế giới thực (sinh - họat động - tử ) nhưng không 
thể làm cho đối tượng tiến hóa. Nếu OOP đưa vào các dự đoán trước cho yêu cầu 
tiến triển thì phần mềm sẽ nặng nề. 
• Thực tế, một hệ thống là sự đan xen nhau giữa các yêu cầu. Giả sử cần phải thêm 
một yêu cầu mới cho một object, OOP buộc phải bổ sung thêm code cho object 
đó. Điều này dẫn đến sự chồng chéo giữa các yêu cầu và làm cho hệ thống trở nên 
lộn xộn, khó bảo trì, phát triển. Khi bổ sung thêm một yêu cầu, OOP buộc phải 
thay đổi các yêu cầu về nghiệp vụ liên quan. 
3.6. Nhìn qua về OOP trong C++ 
Các lớp (Class) 
class class_name 
{ access_specifier_1: 
 member1; 
 access_specifier_2: 
 member2; 
 ... 
} object_names; 
• private: các thành phần của lớp chỉ 
được truy xuất bởi các thành phần 
khác bên trong của chính lớp lớp đó 
hoặc từ các lớp là friend. 
• protected: Các thành phần chỉ 
được truy xuất từ các thành phần 
khác cùng lớp và từ các lớp friend, 
hoặc các lớp dẫn xuất từ lớp này. 
• public: các thành phần được truy 
xuất từ bất kỳ ở đâu đối tượng hiển 
diện. 
Lưu ý: Khia báo private có thể không cần 
từ khoá private 
Ví dụ: 
#include 
using namespace std; 
class CRectangle { 
 int x, y; 
 public: 
 void set_values (int,int); 
 int area () {return (x*y);} 
}; 
void CRectangle::set_values (int a, int b) { 
 x = a; 
 y = b; 
} 
int main () { 
 CRectangle rect, rectb; 
 rect.set_values (3,4); 
 rectb.set_values (5,6); 
 cout << "rect area: " << rect.area() << endl; 
 cout << "rectb area: " << rectb.area() << endl; 
 return 0; 
} 
Constructor và destructor 
 • Cấu tử: 
- Cấu tử ngầm định 
- Khi nào cần tạo cấu tử? 
#include 
class CRectangle { 
 int width, height; 
 public: 
 CRectangle (int,int); 
 int area () {return (width*height);} 
}; 
CRectangle::CRectangle (int a, int b) { 
 width = a; 
 height = b; 
} 
int main () { 
 CRectangle rect (3,4); 
 CRectangle rectb (5,6); 
 cout << "rect area: " << rect.area() << endl; 
 cout << "rectb area: " << rectb.area() << endl; 
 return 0; 
} 
• Các cấu tử overloading 
#include 
class CRectangle { 
 int width, height; 
 public: 
 CRectangle (); 
 CRectangle (int,int); 
 int area (void) {return (width*height);} 
}; 
CRectangle::CRectangle () { 
 width = 5; 
 height = 5; 
} 
CRectangle::CRectangle (int a, int b) { 
 width = a; 
 height = b; 
} 
int main () { 
 CRectangle rect (3,4); 
 CRectangle rectb; 
 cout << "rect area: " << rect.area() << endl; 
 cout << "rectb area: " << rectb.area() << endl; 
 return 0; 
} 
Lưu ý: 
CRectangle rectb; // đúng 
CRectangle rectb(); // sai 
• Cấu tử ngầm định: 
• Nếu không khai báo cấu tử, thì C++ sử dụng cấu tử ngầm định: 
 class CExample { 
 public: 
 int a,b,c; 
 void multiply (int n, int m) { a=n; b=m; c=a*b; }; 
 }; 
Khi đó, có thể khai báo: CExample ex; 
• Nếu có khai báo cấu tử: 
 class CExample { 
 public: int a,b,c; 
 CExample (int n, int m) { a=n; b=m; }; 
 void multiply () { c=a*b; }; 
 }; 
Khi đó: 
 CExample ex (2,3); //đúng 
 CExample ex; //sai 
• Cấu tử copy: 
Nếu khai báo một cấu tử là: 
 CExample::CExample (const CExample& rv) 
 { a=rv.a; b=rv.b; c=rv.c; } 
Khi đó: 
CExample ex (2,3); 
CExample ex2 (ex); // cấu tử copy, dữ liệu sẽ được sao chép từ ex 
• Hũy tử: 
- Hũy tử ngầm định 
- Khi nào cần tạo hũy tử? 
#include 
class CRectangle { 
 int *width, *height; 
 public: 
 CRectangle (int,int); 
 ~CRectangle (); 
 int area () {return (*width * *height);} 
}; 
CRectangle::CRectangle (int a, int b) { 
 width = new int; 
 height = new int; 
 *width = a; 
 *height = b; 
} 
CRectangle::~CRectangle () { 
 delete width; 
 delete height; 
} 
int main () { 
 CRectangle rect (3,4), rectb (5,6); 
 cout << "rect area: " << rect.area() << endl; 
 cout << "rectb area: " << rectb.area() << endl; 
 return 0; 
} 
Con trỏ đến class 
Có thể khai báo các con trỏ đến các 
class đã định nghĩa. Ví dụ: 
 CRectangle * p; 
#include 
class CRectangle { 
 int width, height; 
 public: 
 void set_values (int, int); 
 int area (void) {return (width * height);} 
}; 
void CRectangle::set_values (int a, int b) { 
 width = a; 
 height = b; 
} 
int main () { 
 CRectangle a, *b, *c; 
 CRectangle * d = new CRectangle[2]; 
 b= new CRectangle; 
 c= &a; 
 a.set_values (1,2); 
 b->set_values (3,4); 
 d->set_values (5,6); 
 d[1].set_values (7,8); 
 cout << "a area: " << a.area() << endl; 
 cout area() << endl; 
 cout area() << endl; 
 cout << "d[0] area: " << d[0].area() << endl; 
 cout << "d[1] area: " << d[1].area() << endl; 
 delete[] d; 
 delete b; 
 return 0; 
} 
a area: 2 
*b area: 12 
*c area: 2 
d[0] area: 30 
d[1] area: 56 
• Lưu ý cách viết: 
Biểu thức Có thể hiểu 
*x Trỏ bởi x 
&x Điạ chỉ của x 
x.y Thành phần y của đối tượng x 
x->y Thành phần y của đối tượng được trỏ bởi x 
(*x).y Thành phần y của đối tượng được trỏ bở x (như trên) 
x[0] Đối tượng đầu tiên được trỏ bởi x 
x[1] Đối tượng thứ hai được trỏ bởi x 
x[n] Đối tượng thứ (n+1) được trỏ bởi x 
Các toán tử overloading 
Có thể sử dụng cú pháp sau để tạo ra các toán tử overloading: 
 type operator sign (parameters) 
Các toán tử có thể overloading: 
+ - * / = += -= *= /= > 
>= == != = ++ -- % & ^ ! | 
~ &= ^= |= && || %= [] () , ->* -> new 
delete new[] delete[] 
Ví dụ: 
#include 
class CVector { 
 public: 
 int x,y; 
 CVector () {}; 
 CVector (int,int); 
 CVector operator + (CVector); 
}; 
CVector::CVector (int a, int b) { 
 x = a; 
 y = b; 
} 
CVector CVector::operator+ (CVector param) 
{ 
 CVector temp; 
 temp.x = x + param.x; 
 temp.y = y + param.y; 
 return (temp); 
} 
int main () { 
 CVector a (3,1); 
 CVector b (1,2); 
 CVector c; 
 c = a + b; 
 cout << c.x << "," << c.y; 
 return 0; 
} 
Để ý rằng: 
 c = a + b; 
Là tương đương với: 
 c = a.operator+ (b); 
• Từ khoá this 
Thừ khoá this biểu diễn một con 
trỏ trỏ đến đối tượng mà ở đó 
hàm thành phần được thực 
hiện. Nó là một con trỏ trỏ đến 
chính đối tượng hiện tại. 
#include 
class CDummy { 
 public: 
 int isitme (CDummy& param); 
}; 
int CDummy::isitme (CDummy& param) 
{ 
 if (¶m == this) return true; 
 else return false; 
} 
int main () { 
 CDummy a; 
 CDummy* b = &a; 
 if ( b->isitme(a) ) 
 cout << "yes, &a is b"; 
 return 0; 
} 
Các thành phần static 
• Được xem là thành phần “tổng thể” giữa các đối tượng thuộc lớp. 
#include 
class CDummy { 
 public: 
 static int n; 
 CDummy () { n++; }; 
 ~CDummy () { n--; }; 
}; 
int CDummy::n=0; 
int main () { 
 CDummy a; 
 CDummy b[5]; 
 CDummy * c = new CDummy; 
 cout << a.n << endl; 
 delete c; 
 cout << CDummy::n << endl; 
 return 0; 
} 
Lưu ý: 
Khởi tạo giá trị ban đầu: 
 int CDummy::n=0; 
Cách gọi: 
 cout << a.n; 
 cout << CDummy::n; 
Một số ví dụ khác 
• Xây dựng lớp số phức 
• Xây dựng lớp các tam thức bậc 2 
• Xây dựng lớp hồ sơ sinh viên 
 Quan hệ bạn bè (friendship) 
• Các hàm friend 
Các thành phần được khai báo là private hoặc protect không thể truy xuất đên nó từ các thành 
phần bên ngoài lớp ngoại trừ các thành phần được khai báo sẵn là friend trong lớp. 
Các thành phần friend phải được khai báo prototype trong lớp và bắt đầu với từ khoá friend: 
#include 
class CRectangle { 
 int width, height; 
 public: 
 void set_values (int, int); 
 int area () {return (width * height);} 
 friend CRectangle duplicate (CRectangle); 
}; 
void CRectangle::set_values (int a, int b) { 
 width = a; 
 height = b; 
} 
CRectangle duplicate (CRectangle rectparam) 
{ 
 CRectangle rectres; 
 rectres.width = rectparam.width*2; 
 rectres.height = rectparam.height*2; 
 return (rectres); 
} 
int main () { 
 CRectangle rect, rectb; 
 rect.set_values (2,3); 
 rectb = duplicate (rect); 
 cout << rectb.area(); 
 return 0; 
} 
• Các lớp friend 
#include 
class CSquare; 
class CRectangle { 
 int width, height; 
 public: 
 int area () 
 {return (width * height);} 
 void convert (CSquare a); 
}; 
class CSquare { 
 private: 
 int side; 
 public: 
 void set_side (int a) 
 {side=a;} 
 friend class CRectangle; 
}; 
void CRectangle::convert (CSquare a) 
{ 
 width = a.side; 
 height = a.side; 
} 
int main () { 
 CSquare sqr; 
 CRectangle rect; 
 sqr.set_side(4); 
 rect.convert(sqr); 
 cout << rect.area(); 
 return 0; 
} 
Kế thừa (Inheritance) giữa các lớp 
 Cú pháp khai báo lớp kế thừa: 
 class derived_class_name: public base_class_name 
public protected private 
Các thành phần cùng lớp Được Được Được 
Các thành phần của lớp dẫn xuất Được Được Không 
Không phải là thành phần thuộc lớp 
nào 
Được Không Không 
Nhắc lại tính chất của các thành phần trong class: 
public: Các thành phần kế thừa được “đưa vào” vùng public của lớp dẫn xuất 
protected: Các thành phần kế thừa được “đưa vào” vùng protected của lớp dẫn xuất 
private: Các thành phần kế thừa được “đưa vào” vùng private của lớp dẫn xuất 
#include 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b;} 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle rect; 
 CTriangle trgl; 
 rect.set_values (4,5); 
 trgl.set_values (4,5); 
 cout << rect.area() << endl; 
 cout << trgl.area() << endl; 
 return 0; 
} 
Những gì được kế thừa từ lớp cơ sở? 
• Một lớp dẫn xuất kế thừa các thành phần của lớp cơ sở, trừ các thành phần sau: 
– Các cấu tử và huỹ tử của nó 
– Toán tử operator=() 
– Các friend 
• Mặc dù các cấu tử và hũy tử của lớp cơ sở không được kế thừa nhưng cấu tử mặc 
định (tức cấu tử không tham số), hũy tử mặc định sẽ luôn luôn được gọi khi một 
đối tượng của lớp dẫn xuất được tạo hoặc bị hũy bỏ. 
• Nếu lớp cơ sở không định nghĩa cấu tử mặc định hoặc muốn một cấu tử overload 
được gọi khi một đối tượng của lớp dẫn xuất được tạo thì có thể chỉ ra nó trong 
mỗi cấu tử của lớp dẫn xuất theo cú pháp: 
 derived_constr_name (parameters) : base_constr_name (parameters) 
 { 
 ... 
 } 
#include 
class me { 
 public: 
 me () 
 { cout << “me: không tham số\n"; } 
 me (int a) 
 { cout << “me: có tham số\n"; } 
}; 
class congai : public me { 
 public: 
 congai (int a) 
 { cout << “congai: có tham số\n\n"; } 
}; 
class contrai : public me { 
 public: 
 contrai (int a) : me (a) 
 { cout << “contrai: có tham số\n\n"; } 
}; 
int main () { 
 congai g (0); 
 contrai t (0); 
 return 0; 
} 
me: không tham số 
congai: có tham số 
me: có tham số 
contrai: có tham số 
Để ý rằng: 
- Cấu tử congai(int a): cấu tử 
mặc định của me sẽ được gọi 
- Cấu tử contrai(int a): cấu tử 
overload me(int a) sẽ được gọi 
Kế thừa bội (Multiple inheritance) 
• Một lớp dẫn xuất có thể được kế thừa từ nhiều lớp cơ sở 
• Các cấu tử và hũy tử của lớp dẫn xuất cũng có thể thiết lập tương tự như trên. 
#include 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b;} 
 }; 
class COutput { 
 public: 
 void output (int i); 
 }; 
void COutput::output (int i) { 
 cout << i << endl; 
 } 
class CRectangle: public CPolygon, public COutput { 
 public: 
 int area () 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon, public COutput { 
 public: 
 int area () 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle rect; 
 CTriangle trgl; 
 rect.set_values (4,5); 
 trgl.set_values (4,5); 
 rect.output (rect.area()); 
 trgl.output (trgl.area()); 
 return 0; 
} 
20 
10 
Đa hình (Polymorphism) 
• Chúng ta nhớ rằng 
Lệnh Thể hiện trong: 
int a::b(int c) { } Các lớp 
a->b Các cấu trúc dữ liệu 
class a: public b { }; Friend và inheritance 
Các con trỏ đến lớp cơ sở 
Một trong các đặc điểm của lớp dẫn xuất là một con trỏ đến lớp dẫn xuất là tương 
hợp kiểu với một con trỏ đến lớp cơ sở. 
#include 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle r; 
 CTriangle t; 
 CPolygon * p1 = &r; 
 CPolygon * p2 = &t; 
 p1->set_values (4,5); 
 p2->set_values (4,5); 
 cout << r.area() << endl; 
 cout << t.area() << endl; 
 return 0; 
} 
20 
10 
Nhận xét: 
p1, p2 là 2 con trỏ kiểu Cpolygon, 
nó chỉ có thể gọi các thành phần 
thuộc Cpolygon mà Crectangle và 
Ctriangle đã kế thừa 
Các thành phần ảo (Virtual members) 
Một thành phần mà nó có thể phải được định nghĩa lại trong lớp dẫn xuất được hiểu 
như là thành phần ảo. Để tạo ra thành phần ảo như vậy, ta sử dụng từ khoá virtual. 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
virtual int area () 
 { return (0); } 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area () 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle r; 
 CTriangle t; 
 CPolygon p; 
 CPolygon * p1 = &r; 
 CPolygon * p2 = &t; 
 CPolygon * p3 = &p; 
 p1->set_values (4,5); 
 p2->set_values (4,5); 
 p3->set_values (4,5); 
 cout area() << endl; 
 cout area() << endl; 
 cout area() << endl; 
 return 0; 
} 
20 
10 
0 
Nhận xét: Các tham chiếu của p1,p2,p3 
đến CRectangle::area(), 
CTriangle::area() và CPolygon::area() 
Một lớp mà nó được khai báo hoặc kế 
thừa tư một hàm ảo gọi là lớp đa hình. 
Các lớp cơ sở trừu tượng 
(Abstract base classes) 
 Lớp cơ sở trừu tượng rất giống với lớp Cpolygon ở ví dụ trước, chỉ khác là ta có 
thể định nghĩa một hàm area() với chức năng tối thiểu cho các đối tượng của lớp 
CPolygon. 
Ví dụ về lớp ảo CPolygon: 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
 virtual int area () =0; 
}; 
Nhận xét: 
• Chú ý rằng, ta thêm =0 vào hàm ảo 
area() thay cho việc chỉ ra một sự 
thực hiện của hàm. Kiểu này gọi là 
hàm thuần nhất (pure virtual function) 
• Các lớp có chứa ít nhất một hàm 
thuần nhất được gọi là lớp cơ sở trừu 
tượng. 
• Điểm khác nhau chính giữa lớp cơ sở 
trừu tượng với lớp đa hình ở chổ lớp 
cơ sở trừu tượng có ít nhất một thành 
phần của nó thiếu đi sự thực hiện. Ta 
không thể tạo một thực thể (đối 
tượng) của nó 
Như vậy: khai báo 
 CPolygon p; là không thích hợp 
Mà phải sử dụng con trỏ, kiểu như: 
 CPolygon * p1; 
 CPolygon * p2; 
Ví dụ: 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
 virtual int area (void) =0; 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle r; 
 CTriangle t; 
 CPolygon * p1 = &r; 
 CPolygon * p2 = &t; 
 p1->set_values (4,5); 
 p2->set_values (4,5); 
 cout area() << endl; 
 cout area() << endl; 
 return 0; 
} 
20 
10 
Ví dụ sau đây tạo thêm một hàm printarea để in ra màn hình area: 
#include 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
 virtual int area (void) =0; 
 void printarea (void) 
 { cout area() << endl; } 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height / 2); } 
 }; 
int main () { 
 CRectangle r; 
 CTriangle t; 
 CPolygon * p1 = ▭ 
 CPolygon * p2 = &trgl; 
 p1->set_values (4,5); 
 p2->set_values (4,5); 
 p1->printarea(); 
 p2->printarea(); 
 return 0; 
} 
20 
10 
Nhận xét: 
Hàm printarea truy xuất đến area 
được định nghĩa lại trong mỗi lớp. 
Các thành phần ảo và các lớp trừu tượng hỗ trợ cho đặc tính đa hình của C++, nó 
như là một phương tiện quan trọng cho các dự án lớn. Đặc trưng này có thể áp 
dụng cho mảng các đối tượng hoặc các đối tượng cấp phát động. 
Ví dụ: #include 
class CPolygon { 
 protected: 
 int width, height; 
 public: 
 void set_values (int a, int b) 
 { width=a; height=b; } 
 virtual int area (void) =0; 
 void printarea (void) 
 { cout area() << endl; } 
 }; 
class CRectangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height); } 
 }; 
class CTriangle: public CPolygon { 
 public: 
 int area (void) 
 { return (width * height / 2); } 
 }; 
int main () { 
 CPolygon * p1 = new CRectangle; 
 CPolygon * p2 = new CTriangle; 
 p1->set_values (4,5); 
 p2->set_values (4,5); 
 p1->printarea(); 
 p2->printarea(); 
 delete p1; 
 delete p2; 
 return 0; 
} 
20 
10 
Hết chương 4 
Chương 5 
Lập trình hướng đối tượng 
trong một số ngôn ngữ 
Tìm hiểu các phương pháp OOP trong một vài 
ngôn ngữ lập trình phổ biến hiện nay 
• C# 
• VB.NET 
• VFP 
5.1. 
Lập trình hướng đối tượng 
trong C# 
Định nghĩa lớp 
 [attributes] [access-modifiers] class identifier [:base-class [,interface(s)]] 
{class-body} 
#region Using directives 
using System; 
using System.Collections.Generic; 
using System.Text; 
#endregion 
namespace TimeClass 
{ 
 public class Time 
 { 
 // private variables 
 int Year; 
 int Month; 
 int Date; 
 int Hour; 
 int Minute; 
 int Second; 
 //public 
 public void DisplayCurrentTime( ) 
 { 
 Console.WriteLine( "stub for DisplayCurrentTime" ); 
 } 
 } 
public class Tester 
 { 
 static void Main( ) 
 { 
 Time t = new Time( ); 
 t.DisplayCurrentTime( ); 
 Console.Read(); 
 } 
 } 
} 
Kiểu Ý nghĩa 
public Như C++. 
private Như C++ 
protected 
Được truy xuất bởi các thành phần cùng lớp hoặc từ các lớp dẫn 
xuất. 
internal 
Các thành phần trong lớp A được gán là internal có thể truy xuất 
được các method của bất kỳ lớp nào trong assembly của A. 
protected 
internal 
Các thành phần trong lớp A được gán là protected internal có thể 
truy xuất đến các method của A, các method của các lớp dẫn 
xuất từ A, và bất kỳ lớp nào là assembly của A. 
Các đối số của method 
void MyMethod (int firstParam, Button secondParam) { // ...} 
#region Using directives 
using System; 
using System.Collections.Generic; 
using System.Text; 
#endregion 
namespace PassingValues 
{ 
 public class MyClass 
 { 
 public void SomeMethod( int firstParam, float secondParam ) 
 { 
 Console.WriteLine( 
 "Here are the parameters received: {0}, {1}", 
 firstParam, secondParam ); 
 } 
 } 
} 
public class Tester 
 { 
 static void Main( ) 
 { 
 int howManyPeople = 5; 
 float pi = 3.14f; 
 MyClass mc = new MyClass( ); 
 mc.SomeMethod( howManyPeople, pi ); 
 } 
 } 
Tạo các đối tượng 
Constructors 
using System; 
using System.Collections.Generic; 
using System.Text; 
namespace DeclaringConstructor 
{ 
 public class Time 
 { 
 // private member variables 
 int Year; 
 int Month; 
 int Date; 
 int Hour; 
 int Minute; 
 int Second; 
 // public accessor methods 
 public void DisplayCurrentTime( ) 
 { 
 System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}", 
 Month, Date, Year, Hour, Minute, Second ); 
 } 
// constructor 
 public Time( System.DateTime dt ) 
 { 
 Year = dt.Year; 
 Month = dt.Month; 
 Date = dt.Day; 
 Hour = dt.Hour; 
 Minute = dt.Minute; 
 Second = dt.Second; 
 } 
 } 
public class Tester 
 { 
 static void Main( ) 
 { 
 System.DateTime currentTime = 
 System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 } 
 } 
} 
Output: 
03/11/2010 16:21:40 
Các kiểu cơ sở và giá trị mặc định của chúng 
Kiểu Giá trị mặc định 
numeric (int, long, etc.) 0 
bool false 
char '\0' (null) 
enum 0 
reference null 
Khởi tạo giá trị 
namespace Initializer 
{ 
 public class Time 
 { 
 // private member variables 
 private int Year; 
 private int Month; 
 private int Date; 
 private int Hour; 
 private int Minute; 
 private int Second = 30; // initializer 
 // public accessor methods 
 public void DisplayCurrentTime( ) 
 { 
 System.DateTime now = System.DateTime.Now; 
 System.Console.WriteLine( "\nDebug\t: {0}/{1}/{2} {3}:{4}:{5}", 
 now.Month, now.Day, now.Year, now.Hour, now.Minute, now.Second ); 
 System.Console.WriteLine( "Time\t: {0}/{1}/{2} {3}:{4}:{5}", Month, Date, Year, Hour, Minute, 
Second ); 
 } 
 // constructors 
 public Time( System.DateTime dt ) 
 { 
 Year = dt.Year; 
 Month = dt.Month; 
 Date = dt.Day; 
 Hour = dt.Hour; 
 Minute = dt.Minute; 
 Second = dt.Second; //explicit assignment 
 } 
 public Time( int Year, int Month, int Date, 
 int Hour, int Minute ) 
 { 
 this.Year = Year; 
 this.Month = Month; 
 this.Date = Date; 
 this.Hour = Hour; 
 this.Minute = Minute; 
 } 
 } 
public class Tester 
 { 
 static void Main( ) 
 { 
 System.DateTime currentTime = 
System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 Time t2 = new Time( 2010, 03, 18, 11, 45 ); 
 t2.DisplayCurrentTime( ); 
 } 
 } 
} 
Output: 
Debug : 03/11/2010 7:52:54 
Time : 03/11/2010 7:52:54 
Debug : 03/11/2010 7:52:54 
Time : 03/18/2010 11:45:30 
Từ khoá this 
• this được sử dụng để tham chiếu đến thực thể hiện tại của đối tượng: 
public void SomeMethod (int hour) { 
 this.hour = hour; 
} 
• this được sử dụng đây thực thể hiện tại sang một method khác: 
private int hour; 
class myClass 
{ 
 public void Foo(OtherClass otherObject) { 
 otherObject.Bar(this); 
 } 
} 
• this được sử dụng với các chỉ số (xem sau) 
• this được sử dụng để tham chiếu đến lời gọi một cấu tử overload từ một thành 
phần khác 
class myClass 
{ 
 public myClass(int i) { //... } 
 public myClass( ) : this(42) { //... } 
} 
Trong ví dụ này, cấu tử 
mặc định sẽ gọi cấu tử 
overload với một tham 
số là số nguyên. 
Sử dụng các thành phần static 
using System; 
using System.Collections.Generic; 
using System.Text; 
namespace StaticFields 
{ 
 public class Cat 
 { 
 private static int instances = 0; 
 public Cat( ) 
 { 
 instances++; 
 } 
 public static void HowManyCats( ) 
 { 
 Console.WriteLine( "{0} cats adopted", instances ); 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 Cat.HowManyCats( ); 
 Cat frisky = new Cat( ); 
 Cat.HowManyCats( ); 
 Cat whiskers = new Cat( ); 
 Cat.HowManyCats( ); 
 } 
 } 
} 
Output: 
0 cats adopted 
1 cats adopted 
2 cats adopted 
Hũy các đối tượng 
• Hũy tử trong C# 
Định nghĩa một hũy tử trong C# 
kiểu như: 
~MyClass() 
{ 
 // làm gì đó ở đây 
} 
Khi viết như trên, C# sẽ 
dịch nó thành: 
protected override void Finalize() 
{ 
 try 
 { 
 //làm gì đó ở đây. 
 } 
 finally 
 { 
 base.Finalize( ); 
 } 
} 
• Dispose 
 • Để đóng và loại bỏ đối tượng một cách nhanh chóng, có thể thực hiện giao 
tiếp IDisposable. Giao tiếp IDisposable yêu cầu định nghĩa một method có 
tên là Dispose(). 
• Cách này kiểu như nói “Không đợi cho cấu tử được gọi, hãy thực hiện nó 
ngay bây giờ” 
• Khi thiết kế một method Dispose(), ta phải dừng việc việc gọi cấu tử của đối 
tượng. Để thực hiện điều này, gọi method static GC.SuppressFinalize( ) với 
việc truyền con trỏ đến đối tượng hiện thời (this). 
• Cấu tử cũng có thể gọi method Dispose( ). 
using System; 
class Testing : IDisposable 
{ 
 bool is_disposed = false; 
 protected virtual void Dispose(bool disposing) 
 { 
 if (!is_disposed) // only dispose once! 
 { 
 if (disposing) 
 { 
 Console.WriteLine( "Not in destructor, OK to reference other objects"); 
 } 
 // perform cleanup for this object 
 Console.WriteLine("Disposing..."); 
 } 
 this.is_disposed = true; 
 } 
 public void Dispose( ) 
 { 
 Dispose(true); 
 // tell the GC not to finalize 
 GC.SuppressFinalize(this); 
 } 
 ~Testing( ) 
 { 
 Dispose(false); 
 Console.WriteLine("In destructor."); 
 } 
} 
• Close 
Với một số đối tượng, đối khi người ta thích gọi hàm Close( ) hơn. (ví dụ, 
Close() có thể dễ cảm nhận hơn là Dispose( ) với các đối tuợng là file). 
Có thể thực hiện điều này bằng cách tạo một hàm private Dispose( ) và 
một hàm public Close() trong đó hàm Close( ) sẽ gọi hàm Dispose(). 
• Sử dụng lệnh using 
Để người sử dụng dễ hơn 
trong việc loại bỏ các đối 
tượng, C# cung cấp lệnh 
using trong đó nó đảm bảo 
hàm Dispose() sẽ được gọi 
một cách tự động. 
using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Text; 
namespace usingStatement 
{ 
 class Tester 
 { 
 public static void Main( ) 
 { 
 using ( Font Font1 = new Font( "Arial", 10.0f ) ) 
 { 
 // sử dụng Font1 
 } // C# sẽ gọi Dispose cho Font1 
 Font Font2 = new Font( "Courier", 12.0f ); 
 using ( Font2 ) 
 { 
 // sử dụng Font2 
 } // C# sẽ gọi Dispose cho Font2 
 } 
 } 
} 
Vấn đề truyền tham số 
• Một ví dụ 
 using System; 
using System.Collections.Generic; 
using System.Text; 
namespace ReturningValuesInParams 
{ 
 public class Time 
 { 
 // private member variables 
 private int Year; 
 private int Month; 
 private int Date; 
 private int Hour; 
 private int Minute; 
 private int Second; 
 public void DisplayCurrentTime( ) 
 { 
 System.Console.WriteLine( "{0}/{1}/{2} 
{3}:{4}:{5}", Month, Date, Year, Hour, Minute, 
Second ); 
 } 
 public int GetHour( ) 
 { 
 return Hour; 
 } 
 public void GetTime( int h, int m, int s ) 
 { 
 h = Hour; 
 m = Minute; 
 s = Second; 
 } 
 // constructor 
 public Time( System.DateTime dt ) 
 { 
 Year = dt.Year; 
 Month = dt.Month; 
 Date = dt.Day; 
 Hour = dt.Hour; 
 Minute = dt.Minute; 
 Second = dt.Second; 
 } 
 } 
public class Tester 
 { 
 static void Main( ) 
 { 
 System.DateTime currentTime = 
System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 int theHour = 0; 
 int theMinute = 0; 
 int theSecond = 0; 
 t.GetTime( theHour, theMinute, theSecond ); 
 System.Console.WriteLine( "Current time: 
{0}:{1}:{2}", theHour, theMinute, theSecond ); 
 } 
 } 
} 
Output: 
03/11/2010 13:41:18 
Current time: 0:0:0 
• Sử dụng các khai báo in, out và ref cho tham số 
 ref: được sử dụng như làm tham biến 
out: các tham số được sử dụng chỉ để trả lại thông tin từ hàm 
namespace InOutRef 
{ 
 public class Time 
 { 
 // private member variables 
 private int Year; 
 private int Month; 
 private int Date; 
 private int Hour; 
 private int Minute; 
 private int Second; 
 // public accessor methods 
 public void DisplayCurrentTime( ) 
 { 
 System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}", Month, Date, Year, Hour, Minute, Second ); 
 } 
 public int GetHour( ) 
 { 
 return Hour; 
 } 
 public void SetTime( int hr, out int min, ref int sec ) 
 { 
 if ( sec >= 30 ) 
 { 
 Minute++; 
 Second = 0; 
 } 
 Hour = hr; // set to value passed in 
 // pass the minute and second back out 
 min = Minute; 
 sec = Second; 
 } 
 // constructor 
 public Time( System.DateTime dt ) 
 { 
 Year = dt.Year; 
 Month = dt.Month; 
 Date = dt.Day; 
 Hour = dt.Hour; 
 Minute = dt.Minute; 
 Second = dt.Second; 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 System.DateTime currentTime = 
System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 int theHour = 3; 
 int theMinute; 
 int theSecond = 20; 
 t.SetTime( theHour, out theMinute, ref theSecond ); 
 System.Console.WriteLine( 
 "the Minute is now: {0} and {1} seconds", 
 theMinute, theSecond ); 
 theSecond = 40; 
 t.SetTime( theHour, out theMinute, ref theSecond ); 
 System.Console.WriteLine( "the Minute is now: " + 
 "{0} and {1} seconds", 
 theMinute, theSecond ); 
 } 
 } 
} 
Output: 
03/11/2010 14:6:24 
the Minute is now: 6 and 24 seconds 
the Minute is now: 7 and 0 seconds 
Các hàm overload và các cấu tử 
• Overload cho cấu tử 
using System; 
using System.Collections.Generic; 
using System.Text; 
namespace OverloadedConstructor 
{ 
 public class Time 
 { 
 // private member variables 
 private int Year; 
 private int Month; 
 private int Date; 
 private int Hour; 
 private int Minute; 
 private int Second; 
 // public accessor methods 
 public void DisplayCurrentTime( ) 
 { 
 System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}", 
 Month, Date, Year, Hour, Minute, Second ); 
 } 
 // constructors 
 public Time( System.DateTime dt ) 
 { 
 Year = dt.Year; 
 Month = dt.Month; 
 Date = dt.Day; 
 Hour = dt.Hour; 
 Minute = dt.Minute; 
 Second = dt.Second; 
 } 
 public Time( int Year, int Month, int Date, 
 int Hour, int Minute, int Second ) 
 { 
 this.Year = Year; 
 this.Month = Month; 
 this.Date = Date; 
 this.Hour = Hour; 
 this.Minute = Minute; 
 this.Second = Second; 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 System.DateTime currentTime = System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 Time t2 = new Time( 2005, 11, 18, 11, 03, 30 ); 
 t2.DisplayCurrentTime( ); 
 } 
 } 
} 
• Biến đổi kiểu kết quả trả lại trong các hàm overload 
namespace VaryingReturnType 
{ 
 public class Tester 
 { 
 private int Triple( int val ) 
 { return 3 * val; } 
 private long Triple( long val ) 
 { return 3 * val; } 
 public void Test( ) 
 { int x = 5; 
 int y = Triple( x ); 
 System.Console.WriteLine( "x: {0} y: {1}", x, y ); 
 long lx = 10; 
 long ly = Triple( lx ); 
 System.Console.WriteLine( "lx: {0} ly: {1}", lx, ly ); 
 } 
 static void Main( ) 
 { 
 Tester t = new Tester( ); 
 t.Test( ); 
 } 
 } 
} 
Lấy dữ liệu với các thuộc tính 
Cú pháp: 
 public type propertier_name 
 { 
 get 
 { 
 return value; 
 } 
 set 
 { 
 pval= value; 
 } 
 } 
namespace UsingAProperty 
{ 
 public class Time 
 { 
 // private member variables 
 private int year; 
 private int month; 
 private int date; 
 private int hour; 
 private int minute; 
 private int second; 
 // public accessor methods 
 public void DisplayCurrentTime( ) 
 { System.Console.WriteLine("Time\t: {0}/{1}/{2} 
 {3}:{4}:{5}", 
 month, date, year, hour, minute, second ); 
 } 
 // constructors 
 public Time( System.DateTime dt ) 
 { 
 year = dt.Year; 
 month = dt.Month; 
 date = dt.Day; 
 hour = dt.Hour; 
 minute = dt.Minute; 
 second = dt.Second; 
 } 
 // create a property 
 public int Hour 
 { 
 get 
 { 
 return hour; 
 } 
 set 
 { 
 hour = value; 
 } 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { System.DateTime currentTime = 
System.DateTime.Now; 
 Time t = new Time( currentTime ); 
 t.DisplayCurrentTime( ); 
 int theHour = t.Hour; 
 System.Console.WriteLine( "\nRetrieved the 
hour: {0}\n", theHour ); 
 theHour++; 
 t.Hour = theHour; 
 System.Console.WriteLine( "Updated the 
hour: {0}\n", theHour ); 
 } 
 } 
} 
Sửa đổi thuộc tính truy xuất: 
Có thể đặt trước get, set các tính chất protected, internal, private 
để quy định cách thức truy xuất: 
Ví dụ: 
public string MyString 
{ 
 protected get { return myString; } 
 set { myString = value; } 
} 
Kế thừa (Inheritance) 
• Trong C#, để tạo một lớp dẫn xuất, sử dụng cú pháp: 
 public class derived_class : base_class 
 Ví dụ: 
 public class ListBox : Control 
• Lớp dẫn xuất kế thừa tất cả các thành phần từ lớp cơ sở (cả 
biến lẫn hàm thành phần) 
Đa hình (Polymorphism) 
• Tạo hàm polymorphism 
namespace VirtualMethods 
{ 
 public class Control 
 { 
 protected int top; 
 protected int left; 
 public Control( int top, int left ) 
 { 
 this.top = top; 
 this.left = left; 
 } 
 // simulates drawing the window 
 public virtual void DrawWindow( ) 
 { 
 Console.WriteLine( "Control: 
drawing Control at {0}, {1}", top, left ); 
 } 
 } 
// ListBox derives from Control 
 public class ListBox : Control 
 { 
 private string listBoxContents; // new member variable 
 public ListBox( int top, int left, 
 string contents ): base(top, left) // call base constructor 
 { 
 listBoxContents = contents; 
 } 
 // an overridden version (note keyword) because in the 
 // derived method we change the behavior 
 public override void DrawWindow( ) 
 { 
 base.DrawWindow( ); // invoke the base method 
 Console.WriteLine( "Writing string to the listbox: {0}", 
 listBoxContents ); 
 } 
 } 
 public class Button : Control 
 { 
 public Button(int top, int left ):base(top, left) 
 { 
 } 
 // an overridden version because in the 
 // derived method we change the behavior 
 public override void DrawWindow( ) 
 { 
 Console.WriteLine( 
 "Drawing a button at {0}, {1}\n", 
 top, left ); 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 Control win = new Control( 1, 2 ); 
 ListBox lb = new ListBox( 3, 4, "Stand alone list box" ); 
 Button b = new Button( 5, 6 ); 
 win.DrawWindow( ); 
 lb.DrawWindow( ); 
 b.DrawWindow( ); 
 Control[] winArray = new Control[3]; 
 winArray[0] = new Control( 1, 2 ); 
 winArray[1] = new ListBox( 3, 4, "List box in array" ); 
 winArray[2] = new Button( 5, 6 ); 
 for ( int i = 0; i < 3; i++ ) 
 { 
 winArray[i].DrawWindow( ); 
 } 
 } 
 } 
} 
Output: 
Control: drawing Control at 1, 2 
Control: drawing Control at 3, 4 
Writing string to the listbox: Stand alone list box 
Drawing a button at 5, 6 
Control: drawing Control at 1, 2 
Control: drawing Control at 3, 4 
Writing string to the listbox: List box in array 
Drawing a button at 5, 6 
Các lớp trừu tượng (Abstract Classes) 
namespace abstractmethods 
{ 
 using System; 
 abstract public class Control 
 { 
 protected int top; 
 protected int left; 
 public Control( int top, int left ) 
 { 
 this.top = top; 
 this.left = left; 
 } 
 // simulates drawing the window 
 // notice: no implementation 
 abstract public void DrawWindow( ); 
 } 
public class ListBox : Control 
 { 
 private string listBoxContents; 
 public ListBox( int top, int left, string contents ): 
 base(top, left) 
 { listBoxContents = contents; } 
 // an overridden version implementing the abstract method 
 public override void DrawWindow( ) 
 {Console.WriteLine( "Writing string to the listbox: {0}", 
 listBoxContents ); 
 } 
 } 
public class Button : Control 
 { 
 public Button( int top, int left ): base(top, left) 
 { } 
 // implement the abstract method 
 public override void DrawWindow( ) 
 { Console.WriteLine( "Drawing a button at {0}, {1}\n", 
 top, left ); 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 Control[] winArray = new Control[3]; 
 winArray[0] = new ListBox( 1, 2, "First List Box" ); 
 winArray[1] = new ListBox( 3, 4, "Second List Box" ); 
 winArray[2] = new Button( 5, 6 ); 
 for ( int i = 0; i < 3; i++ ) 
 { 
 winArray[i].DrawWindow( ); 
 } 
 } 
 } 
} 
Gốc của tất cả các lớp: Object 
• Tất cả các lớp của C# đều dẫn xuất từ System.Object. 
• Object cung cấp một số hàm ảo mà các lớp dẫn xuất (lớp con) có thể override (xem bảng). 
Hàm Ý nghĩa 
Equals( ) Định giá có hay không hai object là tương đương. 
GetHashCode( ) Cho phép các đối tượng cho lại hàm hash để sử dụng trong tập hợp 
GetType( ) Hỗ trợ việc truy xuất đến kiểu đối tượng. 
ToString( ) Cung cấp một chuổi biểu diễn của đối tượng. 
Finalize( ) Cleans up tài nguyên, thực hiện bởi một hũy tử. 
MemberwiseClone ( ) Tạo bản sao của đối tượng. 
ReferenceEquals( ) Định giá có hay không hai object tham chiếu cùng một thực thể. 
Ví dụ: Kế thừa từ Object 
using System; 
using System.Collections.Generic; 
using System.Text; 
namespace InheritingFromObject 
{ 
 public class SomeClass 
 { 
 private int val; 
 public SomeClass( int someVal ) 
 { 
 val = someVal; 
 } 
 public override string ToString( ) 
 { 
 return val.ToString( ); 
 } 
 } 
 public class Tester 
 { 
 static void DisplayValue( object o ) 
 { 
 Console.WriteLine( 
 "The value of the object passed in is {0}", o.ToString( ) ); 
 } 
 static void Main( ) 
 { 
 int i = 5; 
 Console.WriteLine( "The value of i is: {0}", i.ToString( ) ); 
 DisplayValue( i ); 
 SomeClass s = new SomeClass( 7 ); 
 Console.WriteLine( "The value of s is {0}", s.ToString( ) ); 
 DisplayValue( s ); 
 } 
 } 
} 
Output: 
The value of i is: 5 
The value of the object passed in is 5 
The value of s is 7 
The value of the object passed in is 7 
Các lớp lồng nhau 
namespace NestedClasses 
{ 
 public class Fraction 
 { 
 private int numerator; 
 private int denominator; 
 public Fraction( int numerator, int denominator ) 
 { 
 this.numerator = numerator; 
 this.denominator = denominator; 
 } 
 public override string ToString( ) 
 { 
 return String.Format( "{0}/{1}", numerator, denominator ); 
 } 
 internal class FractionArtist 
 { 
 public void Draw( Fraction f ) 
 { 
 Console.WriteLine( "Drawing the numerator: {0}", f.numerator ); 
 Console.WriteLine( "Drawing the denominator: {0}", f.denominator ); 
 } 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { 
 Fraction f1 = new Fraction( 3, 4 ); 
 Console.WriteLine( "f1: {0}", f1.ToString( ) ); 
 Fraction.FractionArtist fa = new Fraction.FractionArtist( ); 
 fa.Draw( f1 ); 
 } 
 } 
} 
Các giao tiếp (Interfaces) 
• Một Interface là một sự đảm bảo cho một class, một struct sẽ chạy. Khi một 
class/struct thực hiện một giao tiếp, nó nói lên rằng “Tôi đảm bảo tôi sẽ hỗ trợ các 
hàm, các thuộc tính, các sự kiện, các bảng mục của Interface bởi tên gọi”. 
• Khi định nghĩa một Interface, phải định nghĩa các method, propertie, các indexer, 
và/hoặc các event mà chúng sẽ thực hiện bởi lớp chứa Interface. 
• 
• Interface thường được so sánh với abstract classes. Một abstract class phục vụ 
như là một base class cho một họ các derived classes, trong khi các interface có ý 
nghĩa là trộn các cây kế thừa khác. 
• Khi một lớp thực hiện một interface, nó phải thực hiện tất cả các phần mà 
interface (methods, properties,...); có hiệu lực, lớp nói rằng “Tôi đồng { thực hiện 
quy định được định nghĩa bởi interface này." 
• Định nghĩa và thực hiện một interface 
Cú pháp: 
[attributes] [access-modifier] interface interface-name[:base-list] {interface-
body} 
• attributes: khai báo thuộc tính 
• access_modifier: gồm public, private, protected, internal, protected internal, 
• Interface: từ khoá 
• interface-name: Tên của interface. Nó thường (nhưng không bắt buuộc) bắt 
đầu bởi chữ cái I (chẳng hạn IStorable, ICloneable, IClaudius,...). 
• base-list: Danh sách các interface mà interface này mở rộng. 
• interface-body: mô tả các method, các propertie phải được thực hiện bởi 
lớp. 
Ví dụ: Sử dụng một giao tiếp 
đơn giản 
using System; 
using System.Collections.Generic; 
using System.Text; 
#endregion 
namespace SimpleInterface 
{ 
 // declare the interface 
 interface IStorable 
 { 
 // no access modifiers, methods are public 
 // no implementation 
 void Read( ); 
 void Write( object obj ); 
 int Status { get; set; } 
 } 
// create a class which implements the IStorable interface 
 public class Document : IStorable 
 { 
 // store the value for the property 
 private int status = 0; 
 public Document( string s ) 
 { Console.WriteLine( "Creating document with: {0}", s ); 
 } 
 // implement the Read method 
 public void Read( ) 
 { Console.WriteLine( 
 "Implementing the Read Method for IStorable" ); 
 } 
 // implement the Write method 
 public void Write( object o ) 
 { Console.WriteLine( 
 "Implementing the Write Method for IStorable" ); 
 } 
 // implement the property 
 public int Status 
 { 
 get 
 { 
 return status; 
 } 
 set 
 { 
 status = value; 
 } 
 } 
 } 
// Take our interface out for a spin 
 public class Tester 
 { 
 static void Main( ) 
 { 
 // access the methods in the Document object 
 Document doc = new Document( "Test Document" ); 
 doc.Status = -1; 
 doc.Read( ); 
 Console.WriteLine( "Document Status: {0}", doc.Status ); 
 } 
 } 
} 
Output: 
Creating document with: Test Document 
Implementing the Read Method for 
IStorable 
Document Status: -1 
• Thực hiện nhiều hơn một interface 
public class Document : IStorable, ICompressible 
• Mở rộng các interface 
 interface ILoggedCompressible : ICompressible 
{ 
 void LogSavedBytes(); 
} 
• Liên hợp các interface 
interface IStorableCompressible : IStorable, ILoggedCompressible 
{ 
 void LogOriginalSize(); 
} 
Ví dụ về việc mở 
rộng và liên hợp 
các giao tiếp 
namespace ExtendAndCombineInterface 
{ 
 interface IStorable 
 { 
 void Read( ); 
 void Write( object obj ); 
 int Status { get; set; } 
 } 
 // here's the new interface 
 interface ICompressible 
 { 
 void Compress( ); 
 void Decompress( ); 
 } 
 // Extend the interface 
 interface ILoggedCompressible : ICompressible 
 { 
 void LogSavedBytes( ); 
 } 
// Combine Interfaces 
 interface IStorableCompressible : IStorable, ILoggedCompressible 
 { 
 void LogOriginalSize( ); 
 } 
// yet another interface 
 interface IEncryptable 
 { 
 void Encrypt( ); 
 void Decrypt( ); 
 } 
 public class Document : IStorableCompressible, IEncryptable 
 { 
 private int status = 0; 
 // the document constructor 
 public Document( string s ) 
 { Console.WriteLine( "Creating document with: {0}", s ); 
 } 
 // implement IStorable 
 public void Read( ) 
 { 
 Console.WriteLine( "Implementing the Read Method for IStorable" ); 
 } 
 public void Write( object o ) 
 { 
 Console.WriteLine("Implementing the Write Method for IStorable" ); 
 } 
 public int Status 
 { 
 get 
 { 
 return status; 
 } 
 set 
 { 
 status = value; 
 } 
 } 
 // implement ICompressible 
 public void Compress( ) 
 { 
 Console.WriteLine( "Implementing Compress" ); 
 } 
 public void Decompress( ) 
 { 
 Console.WriteLine( "Implementing Decompress" ); 
 } 
 // implement ILoggedCompressible 
 public void LogSavedBytes( ) 
 { 
 Console.WriteLine( "Implementing LogSavedBytes" ); 
 } 
 // implement IStorableCompressible 
 public void LogOriginalSize( ) 
 { 
 Console.WriteLine( "Implementing LogOriginalSize" ); 
 } 
 // implement IEncryptable 
 public void Encrypt( ) 
 { 
 Console.WriteLine( "Implementing Encrypt" ); 
 } 
 public void Decrypt( ) 
 { 
 Console.WriteLine( "Implementing Decrypt" ); 
 } 
 } 
 public class Tester 
 { 
 static void Main( ) 
 { // create a document object 
 Document doc = new Document( "Test Document" ); 
 // cast the document to the various interfaces 
 IStorable isDoc = doc as IStorable; 
 if ( isDoc != null ) 
 { isDoc.Read( ); 
 } 
 else 
 Console.WriteLine( "IStorable not supported" ); 
 ICompressible icDoc = doc as ICompressible; 
 if ( icDoc != null ) 
 { 
 icDoc.Compress( ); 
 } 
 else 
 Console.WriteLine( "Compressible not supported" ); 
 ILoggedCompressible ilcDoc = doc as ILoggedCompressible; 
 if ( ilcDoc != null ) 
 { 
 ilcDoc.LogSavedBytes( ); 
 ilcDoc.Compress( ); 
 // ilcDoc.Read( ); 
 } 
 else 
 Console.WriteLine( "LoggedCompressible not supported" ); 
 IStorableCompressible isc = doc as IStorableCompressible; 
 if ( isc != null ) 
 { 
 isc.LogOriginalSize( ); // IStorableCompressible 
 isc.LogSavedBytes( ); // ILoggedCompressible 
 isc.Compress( ); // ICompressible 
 isc.Read( ); // IStorable 
 } 
 else 
 { 
 Console.WriteLine( "StorableCompressible not supported" ); 
 } 
 IEncryptable ie = doc as IEncryptable; 
 if ( ie != null ) 
 { 
 ie.Encrypt( ); 
 } 
 else 
 Console.WriteLine( "Encryptable not supported" ); 
 } 
 } 
} 
5.2. 
Một số vấn đề về OOP đối với 
VB.NET 
1. Định nghĩa class 
Public Class Car 
 Public MaximumSpeed as Integer 
 Public ModelName as String 
 Public Sub Accelerate() 
 . 
 End Sub 
 Public Sub Stop() 
 . 
 End Sub 
End Class 
Có 4 cách tạo đối tượng: 
1. Dim oCar as New Car 
2. oCar = New Car 
3. oCar = Assembly.CreateInstance("Car") 
4. oCar = System.Activator.CreateInstance("Car","URIToAssembly") 
Public Class Person 
 Private m_sFirstName as String 
 Private m_sLastName as String 
 Public Property FirstName() as String 
 Get 
 FirstName = m_sFirstName 
 End Get 
 Set(ByVal Value as String) 
 m_sFirstName = Value 
 End Set 
 End Property 
 Public Property LastName() as String 
 Get 
 LastName = m_sLastName 
 End Get 
 Set(ByVal Value as String) 
 m_sLastName = Value 
 End Set 
 End Property 
 ReadOnly Property FullName() as String 
 Get 
 FullName = m_sLastName & ", " & m_sFirstName 
 End Get 
 End Property 
End Class 
2. Khai báo 
tính chất cho 
các thuộc tính 
Public Class Person 
 Private m_sFirstName as String 
 Private m_sLastName as String 
 Private m_sFullName as String 
Public Property FirstName() as String 
 Get 
 FirstName = m_sFirstName 
 End Get 
 Set(ByVal Value as String) 
 m_sFirstName = Value 
 m_sFullName = m_sLastName & ", " & m_sFirstName 
 End Set 
 End Property 
 Public Property LastName() as String 
 Get 
 LastName = m_sLastName 
 End Get 
 Set(ByVal Value as String) 
 m_sLastName = Value 
 m_sFullName = m_sLastName & ", " & m_sFirstName 
 End Set 
 End Property 
 ReadOnly Property FullName() as String 
 Get 
 FullName = m_sFirstName 
 End Get 
 End Property 
End Class 
3. Đa hình trong VB.NET 
Đa hình thông qua việc án định trể: 
Khai báo lớp: 
Public Class C1 
 Public Sub Ride() 
 Console.WriteLine(“Hiển thị trong C1") 
 End Sub 
End Class 
Public Class C2 
 Public Sub Ride() 
 Console.WriteLine(“Hiển thị trong C2") 
 End Sub 
End Class 
Đoạn code áp dụng: 
Private Sub Thuchien() 
 Dim o1 as New C1 
 Dim o2 as New C2 
 Call GoOnRide(o1) 
 Call GoOnRide(o2) 
End Sub 
Private Sub GoOnRide(o as Object) 
 o.Ride() 
End Sub 
Đa hình thông qua các giao tiếp: 
‘Khai báo giao tiếp: 
Public Interface IRide 
 Sub Ride() 
End Interface 
‘Khai báo các lớp: 
Public Class C1 
 Implements IRide 
 Public Sub IRade_Ride() 
 Console.WriteLine(“Hiển thị trong C1") 
 End Sub 
End Class 
Public Class MerryGoRound 
 Implements IRide 
 Public Sub IRide_Ride() 
 Console.WriteLine(“Hiển thị trong C2") 
 End Sub 
End Class 
‘Đoạn thử thực hiện: 
Private Sub Thuchien() 
 Dim o1 as New C1 
 Dim o2 as New C2 
 Call GoOnRide(o1) 
 Call GoOnRide(o2) 
End Sub 
Private Sub GoOnRide(o as IRide) 
 o.Ride() 
End Sub 
4. Kế thừa trong VB.NET 
Các phần tử của ngôn ngữ phục vụ việc kế thừa: 
Phần tử Ngữ cảnh áp 
dụng 
Ý nghĩa 
Inherits Class Statement Lớp mới được kế thừa từ một lớp khác (tương tư : trong C#) 
NotInheritable Class Statement Chỉ thị rằng một lớp không được kế thừa từ một lớp khác 
(tương tự Sealed trong C#) 
MustInherit Class Statement Chỉ thị rằng lớp phải được kế thừa bởi một lớp khác (tương 
tự Abstract trong C#) 
Overridable Procedure Chỉ thị rằng thủ tục có thể được Overridable bởi một subclass 
NotOverridable Procedure Chỉ thị ràng thủ tục không thể bị Overridable trong một 
subclass 
MustOverride Procedure Chỉ thị rằng thủ tục phải được Overridable trong một subclass 
(tương tự Virtual trong C#) 
Overrides Procedure Chỉ thị rằng thủ tục này là một overriding một thủ tục trong 
base class 
MyBase Code Cho phép code trong class thực hiện code trong base class 
MyClass Code Cho phép code trong class thực hiện code trong chính nó 
Protected Function, Sub, 
Field, Property 
Chỉ thị rằng code trong child class có thể truy xuất nó. 
Ví dụ: Về việc sử dụng các phần tử trong một lớp 
Public Class Person 
 Protected c_sFirstName as String 
 Protected c_sLastName as String 
 MustOverride ReadOnly Property ClassName() as String 
 Get 
 ClassName = "Person" 
 End Get 
 End Property 
 NotOverridable ReadOnly Property BaseClassName() as String 
 Get 
 BaseClassName = "Person" 
 End Get 
 End Property 
 Overidable Public Property FirstName() as String 
 Get 
 FirstName = c_sFirstName 
 End Get 
 Set(ByVal Value as string) 
 c_sFirstName = Value 
 End Set 
 End Property 
Overidable Public Property LastName() as String 
 Get 
 LastName = c_sLastName 
 End Get 
 Set(ByVal Value as string) 
 c_sLastName = Value 
 End Set 
 End Property 
 Overridable Sub Speak() 
 Console.WriteLine("I am " & c_sFirstName & " 
" & c_sLastName) 
 Console.WriteLine(" and I am a Person.") 
 End Sub 
End Class 
Ví dụ về một lớp kế thừa lớp trên: 
Public Class NewPerson 
 Inherits Person 
 MustOverride Overrides Public ReadOnly Property ClassName() as String 
 Get 
 ClassName = "NewPerson" 
 End Get 
 End Property 
 Overrides Sub Speak() 
 Console.WriteLine("My name is " & c_sFirstName & " " & c_sLastName) 
 Console.WriteLine(" and I am a new person.") 
 End Sub 
End Public 
5. Lập trình trên cơ sở giao tiếp (Interface) 
Định nghĩa một giao tiếp: 
Public Interface IEmployee 
 Property FirstName() As String 
 Property LastName() As String 
 Function ChangeSalary(ByVal PercentageIncrease as Decimal) as Decimal 
 Event Fired(ByVal ReasonCode as Integer) 
End Interface 
Public Class Employee 
 Implements IEmployee 
 Private c_sFirstName as String 
 Private c_sLastName as String 
 Private c_dSalary as Decimal 
 Public Property FirstNameofEmployee() as String Implements IEmployee.FirstName 
 Get 
 IEmployee_FirstName = c_sFirstName 
 End Get 
 Set(ByVal Value as String) 
 c_sFirstName = value 
 End Set 
 End Property 
 Public Property LastNameOfEmployee() as String Implements IEmployee.LastName 
 Get 
 IEmployee_LastName = c_sLastName 
 End Get 
 Set(ByVal Value as String) 
 c_sLastName = value 
 End Set 
 End Property 
 Private Function UpdateSalary(ByVal PercentageIncrease as Decimal) _ 
 as Decimal Implements IEmployee.ChangeSalary 
 Return c_dSalary * (PercentageIncrease / 100) 
 End Function 
End Class 
Thực hiện một giao tiếp: 
6. Một số ví dụ khác 
NAMESPACE, CLASS, OBJECT, MODULE 
Imports System 
Namespace Animals 
Class Dog 
 Function Bark() 
 Console.Writeline ("Dog is barking") 
 End Function 
End Class 
End Namespace 
Public Module modMain 
 Sub Main() 
 OurFunction() 
 End sub 
 Function OurFunction() 
 Dim Jimmy as Animals.Dog 
 Jimmy = new Animals.Dog() 
 Jimmy.Bark() 
 End Function 
End module 
Các kiểu truy xuất: 
Imports System 
Namespace Animals 
Public Class Dog 
 Public AgeOfDog as Integer 
 Public Function Bark() 
 Console.Writeline ("Dog is barking") 
 End Function 
 Private Function Walk() 
 Console.Writeline ("Dog is walking") 
 End Function 
End Class 
End Namespace 
Public Module modMain 
 'Execution will start from the Main() subroutine 
 Sub Main() 
 OurFunction() 
 End sub 
 Function OurFunction() 
 Dim Jimmy as Animals.Dog 
 Jimmy=new Animals.Dog() 
 Jimmy.Bark 
 Jimmy.AgeOfDog=10 
 End Function 
End Module 
SHARED FUNCTIONS 
Imports System 
Namespace Animals 
Class Dog 
 ‘Hàm này được shared 
 Public Shared Function Bark() 
 Console.Writeline ("Dog is barking") 
 End Function 
 ‘Hàm này không shared 
 Public Function Walk() 
 Console.Writeline ("Dog is walking") 
 End Function 
End Class 
End Namespace 
Public Module modMain 
 Sub Main() 
 ‘Có thể gọi Bark() trực tiếp vì nó là shared 
 Animals.Dog.Bark() 
 ‘Còn Walk() chỉ có thể gọi qua đối tượng 
 Dim Jimmy as Animals.Dog 
 Jimmy=new Animals.Dog() 
 Jimmy.Walk() 
 End sub 
End Module 
OVERLOADING 
Imports System 
Class Adder 
 Overloads Public Sub Add(A as Integer, B as Integer) 
 Console.Writeline ("Adding Integers: " + Convert.ToString(a + b)) 
 End Sub 
 Overloads Public Sub Add(A as String, B as String) 
 Console.Writeline ("Adding Strings: " + a + b) 
 End Sub 
 Shared Sub Main() 
 Dim o as Adder 
 ‘Tạo đối tượng 
 o=new Adder 
 ‘Thực hiện hàm 1 
 o.Add(10,20) 
 ‘Thực hiện hàm 2 
 o.Add("hello"," how are you") 
 End Sub 
End Class 
INHERITANCE 
Imports System 
Class Human 
 Public Sub Walk() 
 Console.Writeline ("Walking") 
 End Sub 
End Class 
Class Programmer 
 Inherits Human 
 Public Sub StealCode() 
 Console.Writeline ("Stealing code") 
 End Sub 
End Class 
Class MainClass 
 Shared Sub Main() 
 Dim Tom as Programmer 
 Tom=new Programmer 
 Tom.Walk() 
 Tom.StealCode() 
 End Sub 
End Class 
OVERRIDING 
Imports System 
Class Human 
 Overridable Public Sub Speak() 
 Console.Writeline ("Speaking") 
 End Sub 
End Class 
Class Indian 
 Inherits Human 
 Overrides Public Sub Speak() 
 ‘Nói tiếng Hindi 
 Console.Writeline ("Speaking Hindi") 
 ‘Chú ý, để gọi Speak() lớp cơ sở thì phải 
 ‘sử dụng kiểu như: 'Mybase.Speak() 
 End Sub 
End Class 
Class MainClass 
 Shared Sub Main() 
 Dim Tom as Human 
 Tom=new Human 
 Dim Tony as Indian 
 Tony=new Indian 
 Tom.Speak() 
 Tony.Speak() 
 End Sub 
End Class 
POLYMORPHISM 
Imports System 
Class Human 
 'Speak() is declared Overridable 
 Overridable Public Sub Speak() 
 Console.Writeline ("Speaking") 
 End Sub 
End Class 
Class Indian 
 Inherits Human 
 Overrides Public Sub Speak() 
 Console.Writeline ("Speaking Hindi") 
 End Sub 
End Class 
Class MainClass 
 Shared Sub Main() 
 Dim Tom as Human 
 Tom=new Indian 
 Tom.Speak() 
 End Sub 
End Class 
CONSTRUCTORS & DESTRUCTORS 
Imports System 
Class Dog 
 Private Age as integer 
 ‘Các cấu tử 
 Public Sub New() 
 Console.Writeline ("Dog is Created With Age Zero") 
 Age=0 
 End Sub 
 Public Sub New(val as Integer) 
 Console.Writeline ("Dog is Created With Age " + Convert.ToString(val)) 
 Age=val 
 End Sub 
 ‘Hũy tử. 
 Overrides Protected Sub Finalize() 
 Console.Writeline ("Dog is Destroyed") 
 End Sub 
 ‘Hàm Main 
 Shared Sub Main() 
 Dim Jimmy, Jacky as Dog 
 ‘Gọi cấu tử ngầm định 
 Jimmy=new Dog 
 ‘Gọi cấu tử tham số 
 Jacky=new Dog(10) 
 End Sub 
End Class 
PROPERTY ROUTINES 
Imports System 
Public Class Dog 
 Private mAgeOfDog as Integer 
 Public Property Age() As Integer 
 Get 
 Console.Writeline ("Getting Property") 
 Return mAgeOfdog 
 End Get 
 Set(ByVal Value As Integer) 
 Console.Writeline ("Setting Property") 
 mAgeOfDog=Value 
 End Set 
 End Property 
End Class 
Class MainClass 
 Shared Sub Main() 
 Dim Jimmy as Dog 
 Jimmy=new Dog 
 Jimmy.Age=30 
 Dim curAge=Jimmy.Age() 
 End Sub 
End Class 
Kế thừa từ các Control 
Imports System 
Imports System.ComponentModel 
Imports System.Windows.Forms 
Imports System.Drawing 
 Public Class SimpleForm 
 Inherits System.Windows.Forms.Form 
 Public Sub New() 
 ‘Gọi cấu tử của base class 
 MyBase.New() 
 ‘Đặt thuộc tính cho lớp mới dựa trên lớp base class. 
 Me.Text = "Hello, How Are You?" 
 End Sub 
 End Class 
 Public Class MainClass 
 Shared Sub Main() 
 ‘Tạo đối tượng 
 Dim sf as SimpleForm 
 sf=new SimpleForm 
 ‘Đẩy đối tượng cho hàm Run() để bắt đầu 
 System.Windows.Forms.Application.Run(sf) 
 End Sub 
 End Class 
5.3. VFP và OOP 
Các lớp cơ sở 
Kế thừa từ lớp cơ sở 
Có thể xây dựng các lớp con (subclass) kế thừa từ các lớp cơ sở: 
-Với các visual base class: 
 Thường thiết kế thành file class của VFP (phần mở rộng là .VCX/VCT)) 
 Sử dụng lệnh: CREATE CLASS derived_class 
-Với các non-visual class (và cả visual class): 
 Thường đặt trong các file .PRG với khai báo 
 DEFINE CLASS derived_class AS base_class 
DEFINE CLASS MyCheckbox AS checkbox 
 Height = 15 
 Width = 92 
 FontName = "Tahoma" 
 FontSize = 8 
 AutoSize = .T. 
 Alignment = 0 
 Caption = "MyCheckbox" 
 Name = "mycheckbox" 
ENDDEFINE 
Ví dụ: 
Các PEM 
• Các PEM là các property, Event, Method 
• Khi tao một lớp, có một số PEM mặc định 
• Có thể tạo thêm các PEM khác 
• Mỗi PEM có các thuộc tính: 
 Type: gồm Property, Method 
 Access: Thuộc tính truy xuất (như là GET) 
 Assign: Thuộc tính gám (như là SET) 
 Visibility: gồm Public, Protected, Hidden 
Ví dụ về màn hình khi sử đổi các thuộc tính của một lớp khi sử 
dụng kiểu visual class: 
DEFINE CLASS mycheckbox AS checkbox 
 Height = 16 
 Width = 97 
 FontName = "Verdana" 
 AutoSize = .T. 
 Alignment = 0 
 Caption = "MyCheckBox" 
 Name = "MyCheckBox" 
 MyNewProperty = .F. 
 PROCEDURE MyNewMethod 
 MESSAGEBOX("Hello from a custom class") 
 ENDPROC 
 PROCEDURE MyNewProperty_Access 
 *To do: Modify this routine for the Access method 
 RETURN THIS.MyNewProperty 
 ENDPROC 
 PROCEDURE MyNewProperty_Assign 
 LPARAMETERS vNewVal 
 *To do: Modify this routine for the Assign method 
 THIS.MyNewProperty = m.vNewVal 
 ENDPROC 
ENDDEFINE 
Ví dụ về một lớp 
với định nghĩa 
DEFINE CLASS 
Sử dụng các lớp 
• Các visual class: chọn và tạo đối tượng ngay 
trên form 
• Các non-visual class: Sử dụng các hàm như 
CREATEOBJECT(), NEWOBJECT(), ADDOBJECT() 
• Để gọi một method ở lớp cha, sử dụng 
DODEFAULT() 
• Rất nhiều vấn đề khác cần tìm hiểu 
Hết chương 5 
            Các file đính kèm theo tài liệu này:
 tailieu.pdf tailieu.pdf