Thứ Sáu, 27 tháng 1, 2012

JSF - Bài 2: Phân loại kết quả xét tuyển



Center of Excellence


Bài 2
Phân loại kết quả xét tuyển


Qua bài toán phân loại xét tuyển, bài viết sẽ trình bày kỹ thuật loại bỏ (refactoring) tình trạng trùng lặp mã (code duplication) bên trong một trang web dùng công nghệ JSF.

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 một web application để 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

Dưới đây là UI screen mẫu.

Hình 1

II. Giải pháp

Vận dụng qui trình giải quyết vấn đề như ở bài trước, trước tiên ta thiết kế một UI screen tĩnh dùng ngôn ngữ thuần XHTML, sau đó dùng JSF để bổ sung phần động, tức phần tương tác với user, rồi phát triển code tính toán.

1. Thiết kế UI screen

Trong NetBeans, tạo project mới với tên ScoresJSF rồi thiết kế index.xhtml như sau

Hình 2

2. Bổ sung phần động cho UI screen

2.1. Tạo managed bean

Dựa vào yêu cầu bài toán và UI screen, ta xác định UI components nào là input và đâu là output. Do user phải nhập ba điểm số là math, essay, và interview, nên đây là ba input components. Grade là kết quả cần được hiển thị, nên grade là một output component. Từ đó ta có thiết kế sơ bộ cho managed bean.

Hình 3

Sau khi đã có thiết kế sơ bộ, ta tiến hành tạo managed bean với phạm vi hoạt động (scope) là request. (Các chọn lựa khác cho scope sẽ được trình bày trong các bài tiếp theo, vào thời điểm thuận tiện. Lúc này, request là chọn lựa phù hợp.)

Hình 4

Kế đến, ta bổ sung vào managed bean các fields cùng getter/setter tương ứng. Đối với input fields, ta cần phát sinh cả getter và setter. Tuy nhiên, đối với output field, chỉ có getter là cần thiết.

Hình 5

2.2. Tạo methods cho buttons

Bên trong managed bean, ta tạo hai methods ứng với Grade button và Clear button của UI screen. Lưu ý là lúc này managed bean vừa có một field tên là grade, vừa có một method với tên tương tự.

Hình 6

2.3. Ràng buộc UI screen với managed bean

Chi tiết của phần này tương tự như bài trước.

Hình 7

3. Phát triển code tính toán

3.1. Design

Hình 8

3.2. Test class

package model;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class ScoresTest {
    private Scores scores1 = new Scores(0, 0, 0);
    private Scores scores2 = new Scores(50, 50, 50);
    private Scores scores3 = new Scores(100, 100, 100);

    @Test
    public void grade() {
        assertEquals('F', this.scores1.grade());
        assertEquals('F', this.scores2.grade());

        assertEquals('D', new Scores(60, 60, 60).grade());
        assertEquals('D', new Scores(65, 65, 65).grade());

        assertEquals('C', new Scores(70, 70, 70).grade());
        assertEquals('C', new Scores(75, 75, 75).grade());

        assertEquals('B', new Scores(80, 80, 80).grade());
        assertEquals('B', new Scores(85, 85, 85).grade());

        assertEquals('A', new Scores(90, 90, 90).grade());
        assertEquals('A', new Scores(95, 95, 95).grade());
        assertEquals('A', this.scores3.grade());
    }

    @Test
    public void average() {
        assertEquals(0.0, this.scores1.average(), 0.0);
        assertEquals(50.0, this.scores2.average(), 0.0);
        assertEquals(100.0, this.scores3.average(), 0.0);
    }

    @Test
    public void sum() {
        assertEquals(0, this.scores1.sum());
        assertEquals(150, this.scores2.sum());
        assertEquals(300, this.scores3.sum());
    }
}


3.3. Main class

package model;

public class Scores {
    private int math;
    private int essay;
    private int interview;

    public Scores(int math, int essay, int interview) {
        this.math = math;
        this.essay = essay;
        this.interview = interview;
    }

    int sum() {
        return this.math + this.essay + this.interview;
    }

    double average() {
        return this.sum() / 3.0;
    }

    public char grade() {
        double average = this.average();
        if (average >= 90.0)
            return 'A';
        else if (average >= 80.0)
            return 'B';
        else if (average >= 70.0)
            return 'C';
        else if (average >= 60.0)
            return 'D';
        else
            return 'F';
    }
}


4. Kết nối managed bean với model

Hình 9

5. Kiểm tra tính hợp lệ của dữ liệu nhập

5.1. Xử lý trường hợp để trống ô nhập

Hình 10

Nhận xét rằng tình trạng code duplication đã diễn ra trong index.xhtml nên ta cần refactor. Tuy nhiên, trước mắt ta cần xử lý trường hợp nhập sai data, tức trường hợp user nhập data không phải là số nguyên từ 0 đến 100.

5.2. Xử lý trường hợp nhập data không đúng yêu cầu

Hình 11

Đến đây, trước khi refactor, ta cần thi hành application và kiểm thử để chắc chắn rằng application hoạt động tốt.

6. Refactor index.xhtml

Khác với code duplication trong Java code, đây là tình huống code duplication trong XHTML file. Trước tiên, ta cần nhận diện những đoạn code nào rất giống nhau. Hình dưới đây đánh dấu các khối tương tự, bao gồm những tags <label>, <h:inputText>, <h:message>, và <br/>. Tất nhiên trong mỗi khối sẽ có một số chi tiết khác nhau. Ta cũng cần nhận diện chính xác các chi tiết khác nhau này.

Hình 12

6.1. Tách khối bằng cách tạo composite component

Sau đây ta sẽ tách khối bằng cách tạo một thành phần chuyên biệt gọi là composite component. Đây là thành phần phức hợp, qui tụ nhiều thành phần đơn lẻ lại với nhau, và được đặt cho một tên, để từ đó về sau, ta có thể sử dụng component này bất kỳ khi nào ta muốn.

Trong NetBeans, right-click vào project node, chọn New, rồi chọn Other... để chuyển sang New File window.

Hình 13

Chọn JavaServer Faces ở bảng Categories: bên trái, sau đó chọn JSF Composite Component ở bảng File Types: bên phải, rồi click Next để chuyển sang bước đặt tên và xác định vị trí lưu trữ (Name and Location).

Hình 14

Nhập inputScore vào ô File Name:, nhập resources/sgt vào ô Folder:, rồi click Finish. Khi đó NetBeans sẽ tạo một file mới với tên inputScore.xhtml.

Hình 15

Hình 16

6.2. Khai báo attributes cho composite component

Đến đây ta tiến hành khai báo attributes cho composite component bên trong <cc:interface> tag. Số lượng attributes là số lượng các chi tiết khác nhau trong từng khối, như đã được nhận diện ở trên. Chúng bao gồm tên môn thi (subject) bên trong <label> tag và điểm số (score) của value attribute bên trong <h:inputText>. Ta khai báo như sau

Hình 17

6.3. Định nghĩa khối bên trong composite component

Sau khi đã khai báo attributes, ta đặt khối vào <cc:implementation> tag, rồi thay các chi tiết khác nhau trong khối bằng tên của attributes. Cần lưu ý qui ước về cách sử dụng attributes: #{cc.attrs.???}.

Hình 18

Thật ra, còn một chi tiết khác biệt nữa đó là id attribute bên trong <h:inputText> tag. Tuy nhiên, ta không cần khai báo attribute đặc biệt này. Việc duy nhất nên làm là điều chỉnh lại giá trị id cho phù hợp với ngữ cảnh mới. Ta đặt lại tên như sau

Hình 19

6.4. Sử dụng composite component

Đến đây ta đã sẵn sàng sử dụng inputScore composite component để refactor index.xhtml, rồi thi hành application để kiểm thử kết quả.

Hình 20

III. Tổng kết

Bài viết đã vận dụng một tính năng mạnh của JSF là composite components để refactor một web page đang bị tình trạng code duplication. Là software developer, ta không những phải cung cấp những giải pháp hoạt động tốt, mà các giải pháp đó còn phải có khả năng phát triển và bảo trì về sau. Muốn được như vậy, code duplication là một trong những vấn đề nghiêm trọng mà ta cần phải lưu ý loại bỏ.

IV. Tài liệu tham khảo
  1. Gaddis T. (2010) Starting Out with Java From Control Structures Through Objects, (4th edition), Pearson Education, Boston. Chương 3.

1 nhận xét: