Phương pháp lập trình

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...

pdf223 trang | Chia sẻ: Khủng Long | Lượt xem: 1047 | Lượt tải: 0download
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:

  • pdftailieu.pdf