Center
of Excellence, SaigonTech
Bài
2
Phân
loại kết quả xét tuyển
Một trong những
nguyên tắc thiết kế software giá trị nhất
là phải tránh
tình trạng dư thừa mã (code duplication).
-- Martin Fowler
I.
Bài toán
Kết
quả xét tuyển vào Học viện SaigonTech của một thí sinh
bao gồm ba điểm: toán (math), viết luận (essay), và phỏng
vấn (interview). Các điểm này được chấm trên thang số
nguyên từ 0 đến 100. Hãy phát triển chương trình phân
loại (grade) kết quả xét tuyển cho một thí sinh, biết
rằng kết quả này được tính dựa trên điểm trung bình
(average) của ba môn như sau
- Loại A nếu average từ 90.0 trở lên,
- Loại B nếu average từ 80.0 đến dưới 90.0,
- Loại C nếu average từ 70.0 đến dưới 80.0,
- Loại D nếu average từ 60.0 đến dưới 70.0, và
- Loại F nếu average dưới 60.0
Bạn không phải
phát triển phần UI, nhưng để hiểu rõ hơn yêu cầu bài
toán, bạn có thể tham khảo một UI mẫu dưới đây.
II.
Giải pháp
Ta
tiếp tục vận dụng qui trình giải quyết vấn đề gồm
4 bước như đã làm quen ở bài trước.
Bước
1. Phát biểu lại yêu cầu bài toán
Phân
loại kết quả xét tuyển
- Input: ba điểm math, essay, và interview
- Output: grade
Bước
2. Thiết kế sơ bộ class để giải quyết bài toán đã
đặt ra
- Xác định tên method và tên parameters
Hình
2
- Thử chuyển parameters thành fields của class
- Xác định tên class dựa vào fields và methodTa cần trả lời câu hỏi: Đối tượng nào có ba điểm math, essay, và interview, rồi từ đó tính được grade? Đó chính là các điểm số (test scores).Hình 4
Hình
3
Bước
3. Xây dựng test cases
Để
xác định số trường hợp kiểm thử, ta cần dựa
vào phương pháp phân loại. Theo mô tả bài toán,
kết quả phân loại là một trong năm ký tự (char)
từ A đến F, nên bộ dữ liệu kiểm thử phải chứa tối
thiểu năm trường hợp này. Ngoài ra, cũng cần chú ý đến
các giá trị biên (boundary values) của average là 0, 60, 70,
80, 90, và 100. Như vậy, ta sẽ có bộ dữ liệu kiểm thử
bao gồm 11 trường hợp.
0 60 70 80 90 100 average
Hình
5. Mười một vị trí của
average cần được kiểm thử
- grade()Nomathessayinterviewsum?average?grade?100000.0F250505015050.0F360606018060.0D465656519565.0D570707021070.0C675757522575.0C780808024080.0B885858525585.0B990909027090.0A1095959528595.0A11100100100300100.0A
Thêm
vào đó, biết rằng kết quả phân loại phải dựa vào
tổng (sum) và average của ba điểm, ta bổ sung thêm các
methods sum()
và average() vào
thiết kế, đồng thời xác định data types cho fields và
kết quả trả về của các methods.
Hình
6
Lưu
ý rằng sum()
và average()
chỉ có chức năng bổ trợ cho method chính, đó là
grade(),
nên trong thiết kế ta không đặt dấu cộng (+) ở đằng
trước các methods này.
Bước
4: Phát triển test class
Trình
tự thực hiện sẽ tương tự như Bài 1.
- Project: TestScores
- Package: testscores
- Test class: TestScoresTest
Hình
7
Do
grade() phụ thuộc vào sum() và average()
nên ta phát triển sum() trước, sau đó là average(),
và cuối cùng là grade().
Phát
triển sum()
Tổng
của ba điểm là một số nguyên nằm trong đoạn từ 0
đến 300. Ta tạo ra ba test cases cho method này, bao gồm hai
trường hợp biên (0 và 300) kèm theo một trường hợp
không phải là biên (ở đây ta chọn giá trị 150).
Hình
8
Sau
đó, tương tự như Bài 1, ta tiến hành loại bỏ hết lỗi
trong test class bằng cách tạo ra TestScores class và
phát triển sum() method.
Hình
9
Lưu
ý rằng do sum() chỉ là method bổ trợ (helper hay
auxiliary method), ta không cần dùng từ khóa (keyword) public
khi phát triển method này.
Đến
đây ta cần thi hành test class để nhận được tín hiệu
xanh, từ đó tự tin tiếp tục phát triển giải pháp.
Hình
10
Phát
triển average()
Do
kết quả của average() phải là một số thực trong
đoạn từ 0.0 đến 100.0, ta tạo ra ba test cases cho method
này, bao gồm hai trường hợp biên (0.0 và 100.0) và một
trường hợp không phải là biên (ở đây ta chọn giá trị
50.0).
Hình
11
Quan
sát rằng khi tạo test method cho average(), ta đã lặp
lại việc tạo ra ba TestScores objects hoàn toàn giống
với ba objects bên trong test method cho sum(). Đây là
tình trạng dư thừa mã (code duplication). Khi phát hiện
tình trạng này, ta cần loại bỏ nó để tạo thuận lợi
cho việc bảo trì (maintenance) về sau. Tuy nhiên, trước
khi nghĩ đến việc tái cấu trúc (refactor) code, ta cần
hoàn thành nhiệm vụ trước mắt, đó là phát triển
average().
Hình
12
Tương
tự như sum(), average() chỉ là một helper, ta
không cần dùng public keyword cho nó.
Một
điểm cần lưu ý nữa là trong average(), ta đã dùng
3.0 thay vì 3, bởi vì nếu dùng số nguyên 3, kèm theo việc
sum() sẽ trả về kết quả là một số nguyên, phép
chia bên trong average() sẽ là một phép chia nguyên
(bỏ qua phần thập phân), khiến kết quả tính toán sẽ
thiếu chính xác (chẳng hạn 5 / 2 sẽ bằng 2, chứ không
bằng 2.5, do đây là phép chia nguyên). Khi viết 3.0, ta muốn
báo cho Java biết rằng đây phải là một phép chia số
thực (không được là phép chia nguyên).
Đến
đây ta thi hành test class một lần nữa để nhận được
tín hiệu xanh, từ đó tự tin refactor code để loại bỏ
tình trạng code duplication.
Loại
bỏ code duplication trong TestScoresTest
Như
đã đề cập ở trên, bên trong TestScoresTest class,
ta đã tạo nhiều TestScores objects giống nhau một
cách không cần thiết. Thấy rằng các objects này nằm
trong hai methods khác nhau. Để loại bỏ code duplication, ta
cần chuyển chúng ra bên ngoài để hai methods đều có thể
sử dụng được chúng. Đồng thời, ta gán cho mỗi object
một cái tên (testScores1, testScores2, và testScores3)
để dễ dàng đề cập đến chúng. Ta được code sau.
Hình
13
Sau
khi refactoring, ta cần thi hành test class để chắc chắn
rằng chương trình vẫn hoạt động tốt.
Phát
triển grade()
Ta
có tổng cộng 11 test cases để kiểm tra tính đúng đắn
của grade(). Đây là một lượng không nhỏ, có thể
gây khó khăn cho quá trình tìm kiếm giải pháp. Ta quyết
định tạm thời chỉ dùng vài test cases, rồi sẽ bổ
sung đầy đủ ngay sau khi hoàn thành việc phát triển
code.
Do
test class đã có sẵn ba TestScores objects, ta dùng luôn
những objects này vào mục đích kiểm thử grade().
Hình
14
Chú
ý rằng ta đã bao quanh các ký tự F và A bằng
các dấu nháy đơn (single quote). Đây là qui ước của
ngôn ngữ Java.
Lỗi
của test class được loại bỏ bằng cách tạo ra grade()
method bên trong TestScores class, với data type của kết
quả trả về là một ký tự (char).
Hình
15
Bây
giờ ta tiến hành lý luận trên grade(). Dựa vào
phát biểu bài toán và test cases, biết rằng grade()
chỉ có thể trả về một trong năm ký tự từ A
đến F, ta dùng cấu trúc điều kiện IF như
sau
Hình
16
Tất
nhiên kết quả trả về của grade() sẽ phụ thuộc
vào giá trị trung bình nên ta đưa vào biến cục bộ
(local variable) average để lưu giữ giá trị này, rồi
sử dụng nó để hoàn tất cấu trúc điều kiện IF.
Hình
17
Ở
trên ta đã sử dụng toán tử so sánh (comparison operators)
lớn hơn hay bằng >= để so sánh average với một
số cụ thể. Ngoài >=, Java còn có các comparison
operators khác: lớn hơn >, nhỏ hơn <, nhỏ hơn hay
bằng <=, bằng ==, và không bằng !=. Cần chú ý phân
biệt toán tử so sánh bằng (gồm hai dấu bằng) với toán
tử gán (chỉ gồm một dấu bằng).
Đến
đây ta thi hành test class để nhận được tín hiệu xanh,
từ đó có thể tự tin bổ sung các test cases còn thiếu.
Hình
18
Cuối
cùng, ta kết thúc quá trình tìm kiếm giải pháp bằng
cách thi hành một lần nữa TestScoresTest để được
tín hiệu xanh.
III.
Tổng kết
Bài
viết minh họa việc vận dụng cấu trúc điều kiện IF
để giải quyết vấn đề phân loại kết quả xét tuyển.
Trong quá trình tìm ra giải pháp cho nhiệm vụ chính là
vấn đề phân loại (grade), ta cần phải tính toán giá
trị trung bình (average), và để tính average, ta lại cần
tính tổng (sum). Với từng tính toán như vậy, ta luôn sử
dụng các test cases và tín hiệu xanh để hỗ trợ quá
trình tư duy.
Ngoài
ra, khi phát triển giải pháp, ta cần chú ý loại bỏ tình
trạng trùng lặp mã (code duplication). Đây là một trong
những nguyên tắc quan trọng nhất trong phát triển
software. Kỹ thuật loại bỏ code duplication sẽ còn được
trình bày chi tiết hơn trong các bài tiếp theo.
IV.
Tài liệu tham khảo
- Gaddis T. (2010) Starting Out with Java From Control Structures Through Objects, (4th edition), Pearson Education, Boston. Chương 3.
V.
Thuật ngữ tiếng Anh
average
|
trung
bình
|
auxiliary
method
|
phương
thức bổ trợ, phương thức phụ
|
boundary
value
|
giá
trị biên
|
char
|
kiểu
ký tự
|
code
duplication
|
trùng
lặp mã, dư thừa mã
|
comparison
operator
|
toán
tử so sánh
|
grade
|
điểm
phân loại, là một chữ thuộc tập { A, B, C, D, F }
|
helper
|
phương
thức bổ trợ
|
keyword
|
từ
khóa, từ dành riêng
|
local
variable
|
biến
cục bộ
|
maintenance
|
bảo
trì
|
operator
|
toán
tử
|
refactor
|
tái
cấu trúc mã (động từ)
|
refactoring
|
việc
tái cấu trúc mã (danh từ)
|
score
|
điểm số
|
single quote
|
dấu nháy đơn
|
sum
|
tổng
|
Không có nhận xét nào:
Đăng nhận xét