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 |
Chia sẻ: Khủng Long | Lượt xem: 1214 | 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