Center
of Excellence
Bài
1
Tính
lương trước thuế
Càng khởi sự
viết mã (code) sớm chừng nào,
chương trình
(program) càng hoàn thành trễ chừng đó.
-- Roy Carlson,
University of Wisconsin
Đối
với người mới học lập trình hướng đối tượng
(Object-Oriented Programming - OOP), bài đầu tiên có thể có
nhiều chi tiết khiến bạn cảm thấy khó khăn. Đừng nản
lòng. Điều quan trọng là bạn có thể đi theo các bước
hướng dẫn trong bài. Sau đó nếu cần, bạn xóa hết đi
rồi làm lại những bước này cho đến khi thuần thục.
Lập trình là một nghề cần đức tính tận tụy, đổi
lại, lập trình sẽ đem lại niềm vui đặc biệt vì có
thể nhận biết ngay tác dụng của kết quả mà ta vừa
tạo ra.
I.
Bài toán
Hãy
phát triển chương trình tính lương chính thức (gross
pay), hay còn được gọi là lương chưa trừ thuế, của
một nhân viên biết rằng thông tin để tính lương bao
gồm số giờ làm việc (number of hours worked, được qui
tròn về một số nguyên không âm) và lương một giờ
(hourly pay rate, là một số thực dương từ 6.00 trở lên,
đơn vị là USD, đô-la Mỹ). Sau đây là công thức tính
gross pay:
gross-pay
= number-of-hours-worked
x
hourly-pay-rate
Bạn không phải
phát triển phần tương tác với người dùng (user
interface – 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.
Hình
1
II.
Qui trình giải quyết vấn đề
Để
phát triển giải pháp, ta sẽ đi theo một qui trình giải
quyết vấn đề gồm 4 bước cơ bản sau:
Bước
1. Phát biểu lại yêu cầu bài toán
Bước
này nhằm một lần nữa khẳng định rằng ta đã đọc
và hiểu rõ yêu cầu, rồi viết lại theo cách hiểu của
mình. Viết càng cô đọng càng tốt. Nhớ ghi xuống dữ
liệu hiện có (input) và kết quả cần phải đưa ra
(output).
Bước
2. Thiết kế sơ bộ lớp (class) để giải quyết bài toán
đã đặt ra
Dựa
vào kết quả ở Bước 1 ta xác định tên phương
thức (method), các tham biến (parameters) truyền vào method
đó, tên thuộc tính (fields), và tên class. Điểm quan trọng
ở bước này là việc đặt tên, đặc biệt là tên
class. Đôi khi cần thảo luận nhóm để tìm được một
tên thích hợp nhất. Tuy nhiên, bước này chỉ là thiết
kế sơ bộ vì rất có thể ta sẽ sửa lại thiết kế
trong quá trình viết mã (coding).
Bước
3. Xây dựng bộ dữ liệu kiểm thử (test cases)
Bước
này giúp xác định rõ hơn và cụ thể hơn yêu cầu bài
toán, đồng thời có thể giúp ta có được manh mối nào
đó từ đó có thể tìm ra được giải pháp để giải
quyết vấn đề. Một test case bao gồm dữ liệu cụ thể
của input và output. Để có được dữ liệu input, cách
tốt nhất là ta thảo luận và thu thập từ người đặt
ra bài toán. Nếu họ không thể cung cấp, ta mới tiến
hành tự xây dựng input. Output là kết quả có được sau
các tính toán bằng tay với sự trợ giúp của một
Calculator hay một phần mềm bảng tính (spreadsheet).
Sau
khi đã tạo test cases, ta có cơ sở để xác định kiểu
dữ liệu (data type) cho field(s), parameter(s), và kết quả
trả về của method ở Bước
2.
Bước
4: Phát triển class kiểm thử (test class)
Dựa
vào kết quả ở Bước 2 và 3, ta tiến hành
phát triển test class, rồi dựa vào test class để suy luận,
từ đó tìm ra giải pháp thích hợp.
III.
Giải pháp
Bước
1. Phát biểu lại yêu cầu bài toán
Tính
gross pay của một nhân viên
- Input: number of hours worked và hourly pay rate
- Output: gross pay
Bước
2. Thiết kế sơ bộ class để giải quyết bài toán đã
đặt ra
Ta
dùng UML class diagram (Hình
2)
để thiết kế class.
Hình
2
Dựa
vào Bước
1,
đầu tiên ta xác định tên method và tên parameters (Hình
3).
Kiểu dữ liệu (data type) của kết quả và của parameter
sẽ được xác định ở Bước
3.
Các dấu chấm hỏi (???) là những phần được bổ sung
sau. Cách này giúp ta không phải tập trung giải quyết quá
nhiều vấn đề tại cùng một thời điểm.
Hình
3
Sau
đó ta thử xem có nên chuyển parameters thành fields của
class hay không (Hình 4).
Hình
4
Tiếp
theo, ta dựa vào ý nghĩa của fields và method để xác định
một tên class thích hợp. Ta cần trả lời câu hỏi: Đối
tượng nào có number of hours worked và hourly pay rate, từ đó
tính được gross pay?
Đó chính là một khoản trả lương (Hình
5).
Hình
5
Lưu
ý rằng quá trình đặt tên không nhất thiết phải theo
đúng trình tự như trên. Có thể từ phát biểu bài toán,
ta xác định được ngay tên class và fields. Tuy nhiên, việc
tập trung vào hành động (method) chính mà bài toán yêu
cầu sẽ giúp ta phát triển được một thiết kế vừa
đủ để giải quyết vấn đề đã đặt ra, vừa tránh
được những thiết kế phức tạp không cần thiết.
Bước
3. Xây dựng test cases
Test case 1
- Input
- hoursWorked: 40
- hourlyPayRate: $25.00
- Output
- grossPay: $1000.00
Test
case 2
- Input
- hoursWorked: 10
- hourlyPayRate: $15.50
- Output:
- grossPay: $155.00
Test
cases có thể được trình bày dưới dạng bảng như sau:
computeGrossPay()
|
||
---|---|---|
hoursWorked | hourlyPayRate | grossPay? |
40 | 25.00 | 1000.00 |
10 | 15.50 | 155.00 |
Sau
khi xây dựng test cases, ta tiến hành xác định data type
của kết quả trả về từ computeGrossPay()
method và data type của fields. Ở đây kết quả trả về
là một số thực (double), hoursWorked
là một số nguyên (int), và hourlyPayRate
là một double (Hình
6).
Hình
6
Bước
4: Phát triển test class
Ta
sẽ dùng Eclipse IDE (Integrated Development Environment – Môi
trường Phát triển Tích hợp) để phát triển test class.
Phần Phụ Lục sẽ hướng dẫn việc cài đặt IDE này.
Sau
đây là cấu trúc của một dự án (Hình
7).
Hình
7
Bước
4.1. Tạo dự án (project)
Bước
này nhằm tạo một thư mục chính (project folder) để chứa
các class và thư viện (library) cần thiết cho việc thực
thi project.
Bước
4.2. Tạo gói (package)
Bước
này tạo ra một hay nhiều thư mục con (subfolder) bên trong
project để chứa các class. Việc tạo ra package
nhằm
mục đích tập hợp các class có cùng chức năng về một
chỗ.
Bước
4.3. Tạo class kiểm thử (test class)
Bước
này chuẩn bị cho việc phát triển phương thức test (test
method) ở bước tiếp theo.
Bước
4.4. Phát triển test method
Sau khi đã tạo test class, ta sẽ phát triển test method dựa vào test cases ở Bước 3.
Bước
4.5. Phát triển class chính (main class)
Dựa vào test class để phát triển main class cho đến khi test class không còn lỗi.
Bước
4.6. Thực thi project
4.1.
Tạo project
Dựa
vào yêu cầu bài toán ở Bước
1,
ta tạo project với tên gọi Payment:
Trong File
menu chọn New,
rồi chọn Java
Project
(Hình
8).
Hình
8
Nhập
Project
name là
Payment,
rồi chọn Finish
(Hình
9).
Hình
9
4.2.
Tạo package
Dựa
vào thiết kế class sơ bộ ở Bước
2,
ta tạo một package với tên gọi payment:
Click phải (right-click) vào project, chọn New,
rồi chọn Package
(Hình
10).
Hình
10
Nhập
Name
là payment
rồi chọn Finish
(Hình
11).
Hình
11
4.3.
Tạo
test
class
Cũng
dựa vào thiết kế sơ bộ ở Bước
2,
ta tạo một test class bằng cách ghép tên class Payment
với
từ Test
để trở thành PaymentTest:
Right-click vào payment
package,
chọn
New, rồi
chọn JUnit
Test Case
(Hình
12).
Hình
12
Chọn
New
JUnit 4 test, nhập
Name
là PaymentTest,
rồi chọn Finish
(Hình
13).
Hình
13
Chọn
OK
(Hình 14).
Hình
14
4.4.
Phát triển test method
Dựa
vào thiết kế ở Bước
2,
ta phát triển test method cho computeGrossPay()
(Hình
15).
Hình
15
Trong
test method, ta dùng câu lệnh assertEquals()
để khẳng định sự ăn khớp giữa kết quả mà ta dự
kiến sẽ nhận được
(expected)
và kết quả thật sự mà computeGrossPay()
sẽ trả về
(actual).
Ta biết rằng expected
và actual
là
các số thực (double),
và do máy tính có giới hạn trong việc biểu diễn miền
số thực, ta cần bổ sung vào lệnh assertEquals()
một độ lệch cho phép giữa hai giá trị so sánh. Ở đây
ta chỉ định độ lệch đó là 0.0, tức yêu cầu hai giá
trị phải khớp với nhau hoàn toàn (Hình
16).
Hình
16
Lưu
ý ở Hình
16
rằng ta đã sử dụng dấu gạch dưới làm dấu phân cách
hàng ngàn cho kết quả dự kiến 1000.0 (được viết là
1_000.0).
Bây
giờ ta cần thay thế actual
bằng lệnh gọi thi hành computeGrossPay().
Vì computeGrossPay()
được đặt bên trong Payment
class,
nên ta phải tạo đối tượng (object) Payment
bằng toán tử new
trước
khi có thể gọi thi hành method (Hình
17).
Hình
17
Dựa
vào thiết kế ở Bước
2
và test cases ở Bước
3,
ta thay thế các giá trị vào phần ??? (Hình
18).
Hình
18
Bước
4e: Phát triển
main
class
Bây
giờ ta sẽ làm cho PaymentTest
class
không còn lỗi.
Phát
sinh Payment class
Di
chuyển thiết bị chuột (mouse) vào Payment,
chọn
Create
class ‘Payment’,
rồi
chọn Finish.
Eclipse
sẽ phát sinh Payment
class bên trong payment
package (Hình
19 và
20).
Hình
19
Hình
20
Phát
sinh phương thức tạo object (constructor) cho Payment class
Constructor
là một method nhằm hỗ trợ việc tạo ra một object thuộc
một class. Đây là method đặc biệt vì tên của nó trùng
với tên class. Để phát sinh constructor, ta quay về
PaymentTest
class,
tiếp tục di chuyển mouse vào Payment,
chọn
Create
constructor (Hình
21 và
22).
Hình
21
Hình
22
Ta
hoàn chỉnh constructor
bằng cách đổi tên parameters và bổ sung fields như theo
thiết kế ban đầu
(Hình 25).
Hình
23
Ở
đây constructor có nhiệm vụ tiếp nhận các giá trị vào
parameters rồi lưu chúng vào fields để xây dựng một
object mới. Để ý rằng ta có hai cặp field và parameter
trùng tên. Để phân biệt đâu là field, ta thêm từ khóa
this
vào trước tên.
Phát
sinh computeGrossPay() method
Ta
quay về PaymentTest
class,
di chuyển mouse vào computeGrossPay,
rồi
chọn
Create
method (Hình
24 và
25).
Hình
24
Hình
25
Phát
triển computeGrossPay() method
Dựa
vào công thức tính gross pay của đề bài, ta hoàn tất
phần tính toán của method này (Hình
26).
Hình
26
4.5.
Thực thi project
Quay
về PaymentTest
class,
click menu Run,
chọn Run
As,
rồi chọn tiếp JUnit
Test
(Hình
27).
Nếu thấy tín hiệu màu xanh tức là kết quả dự kiến
của ta và kết quả tính toán từ chương trình khớp với
nhau (Hình
28).
Hình 27
Hình
28
IV.
Kết luận
Bài viết đã
trình bày một qui trình giải quyết vấn đề để tìm ra
một giải pháp lập trình hướng đối tượng. Đây là
qui trình cần theo trước khi khởi sự viết code. Giải
pháp cuối cùng bao gồm hai phần: phần mã kiểm thử
(test code) và phần code chính. Một giải pháp chỉ được
xem là hoàn tất khi ta thu được tín hiệu màu xanh.
V.
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 1 và 2.
- Felleisen M., Findler R.B., Flatt M., and Krishnamurthi S. (2010) How To Design Programs, Second Edition, The MIT Press. http://www.ccs.neu.edu/home/matthias/HtDP2e/index.html
- Dromey R.G. (1982) How to Solve it by Computer, Prentice-Hall, London.
VI.
Thuật ngữ tiếng Anh
class | lớp |
class diagram | sơ đồ lớp |
click | kích nút chuột trái |
code | mã |
constructor | phương thức tạo mới một đối tượng |
data type | kiểu dữ liệu |
double | kiểu số thực |
field | trường, thuộc tính |
IDE
|
xem
Integrated Development Environment
|
input | dữ liệu nhập |
int | kiểu số nguyên |
Integrated
Development Environment
|
Môi
trường Phát triển Tích hợp
|
method | phương thức |
mouse | thiết bị chuột |
object | đối tượng |
Object-Oriented Programming | Lập trình Hướng Đối tượng |
OOP | xem Object-Oriented Programming |
output | dữ liệu xuất |
package | gói |
parameter | tham biến |
private | riêng tư |
program | chương trình (danh từ), lập trình (động từ) |
project | dự án |
public | công cộng |
return | trả về kết quả cho nơi gọi phương thức |
right-click | kích nút chuột phải |
test case | một trường hợp cần kiểm thử |
test code | mã kiểm thử |
test class | lớp kiểm thử |
test method | phương thức kiểm thử |
UI | xem user interface |
UML | xem Unified Modeling Language |
Unified Modeling Language | Ngôn ngữ Mô hình hóa Thống nhất |
user interface | giao diện người dùng |
VII.
Phụ lục. Hướng
dẫn cài đặt Java
SE Development Kit (JDK)
và Eclipse
1.
Cài đặt JDK
Download
phiên bản JDK mới nhất tại
http://www.oracle.com/technetwork/java/javase/downloads/index.html,
hiện nay là
Java SE Development Kit 7u3.
Double-click
file vừa download để tiến hành cài đặt.
Click
Next.
Tiếp
tục click Next.
Tiếp
tục click Next.
Click
Continue
Click
Cancel
rồi click Yes
để bỏ qua quá trình cài đặt JavaFX
(ta sẽ không dùng JavaFX
trong loạt bài này) và hoàn tất quá trình cài đặt.
2.
Cài đặt Eclipse
IDE for Java Developers
Download
phiên bản mới nhất tại http://www.eclipse.org/downloads/,
hiện nay là eclipse-java-indigo-SR2.
Giải
nén file này để được folder có tên là eclipse.
Double-click
eclipse.exe
để chạy Eclipse.
Chương
trình yêu cầu xác định nơi lưu trữ (workspace) các
chương trình java mà sẽ được phát triển sau này. Click
OK.