Thứ Năm, 16 tháng 2, 2012

Công nghệ và đạo đức

"Công nghệ thông tin là rất hữu dụng nhưng nếu chúng được dùng mà không có lương tâm, nó có thể lôi chúng ta lên con đường của phá huỷ và suy tàn. Chúng ta nên học cách dùng chúng cho phúc lợi xã hội của nhân loại và tiến bộ của nền văn minh của chúng ta bằng việc có bộ luật ứng xử đạo đức tốt. Kẻ tội phạm nguy hiểm có thể là người có năng khiếu lập luận logic, nhưng không có đạo đức. Thông minh là không đủ. Thông minh cộng với nhân cách đạo đức là mục đích của giáo dục. Giáo dục đầy đủ cho con người không chỉ tri thức, mà còn nhân cách đạo đức. Giáo dục tốt nhất phải hội tụ không chỉ vào việc thu nhận tri thức mà còn tích luỹ kinh nghiệm sống xã hội bởi vì mục đích của giáo dục là tạo ra công dân tốt cho đất nước."

Chủ Nhật, 5 tháng 2, 2012

Hãy lập trình với tinh thần phải bảo trì cho đến chết


Hãy lập trình với tinh thần phải bảo trì cho đến chết

Yuriy Zubarev


Khi hỏi 97 người rằng mọi lập trình viên phải học những gì và cần làm gì, bạn có thể nhận được 97 lời khuyên khác nhau. Kết quả này có thể khiến bạn cảm thấy quá sức và lo sợ. Lời khuyên nào cũng hay, nguyên tắc nào cũng hợp lý, và câu chuyện nào cũng đầy thuyết phục, song phải bắt đầu từ đâu? Quan trọng hơn, một khi đã bắt đầu, làm thế nào duy trì được tất cả các thực hành tốt mà bạn đã học, và làm thế nào tích hợp được chúng vào kỹ năng lập trình của mình?

Tôi nghĩ rằng câu trả lời nằm trong tâm, cụ thể hơn là ở thái độ của bạn. Chẳng hạn, nếu chẳng màng đến đồng nghiệp lập trình, kiểm thử, quản lý, bán hàng, tiếp thị, và người sử dụng, bạn sẽ không bị thôi thúc áp dụng phương pháp phát triển phần mềm dựa trên kiểm thử (test-driven development), hoặc không muốn chú thích rõ ràng trong chương trình. Tôi nghĩ rằng có một cách đơn giản để điều chỉnh thái độ của bạn và luôn thôi thúc bạn xuất xưởng những sản phẩm có chất lượng tuyệt hảo, đó là

Hãy lập trình với tinh thần phải bảo trì cho đến chết

Chỉ vậy thôi. Nếu bạn thừa nhận quan điểm này, nhiều điều kỳ diệu sẽ xảy đến. Nếu phải chấp nhận để nhà tuyển dụng trong quá khứ hay ở hiện tại có quyền nửa đêm dựng bạn dậy, yêu cầu giải thích vì sao bạn lại viết phương thức (method) fooBar như thế, bạn sẽ cải thiện từng bước để trở thành một chuyên gia lập trình. Tự nhiên bạn muốn đặt tên biến và tên method cho tốt hơn. Bạn sẽ tránh xa những khối lệnh chứa hàng trăm dòng. Bạn sẽ tìm học và áp dụng các công thức thiết kế (design patterns). Bạn sẽ chú thích, kiểm thử, và liên tục tái cấu trúc (refactor) chương trình. Tinh thần bảo trì mã (code) do bạn viết ra cho đến chết còn là một nỗ lực hoàn thiện. Qua đó bạn chẳng còn chọn lựa nào khác ngoài việc trở nên tốt hơn, chín chắn hơn, và hiệu quả hơn.

Nếu suy niệm sâu sắc, bạn nhận thấy rằng chương trình do mình viết ra nhiều năm trước vẫn tiếp tục tác động đến nghề nghiệp, bất chấp mình có thích điều đó hay không. Bạn sẽ để lại di tích về kiến thức, thái độ, lòng kiên trì, tính chuyên nghiệp, tinh thần tận tụy, và cảm hứng của mình trong từng method, lớp (class), và bộ phận (module) mà bạn đã thiết kế và thi công. Người khác sẽ đánh giá bạn qua code của bạn. Nếu những đánh giá đó thường xuyên là tiêu cực, bạn sẽ thu nhận từ nghề nghiệp ít hơn điều mà mình mong ước. Hãy chăm sóc nghề nghiệp, khách hàng, và người sử dụng qua từng dòng code – hãy lập trình với tinh thần phải bảo trì cho đến chết.


Nguồn

Zubarev Y. (2010) Write Code As If You Had to Support It for the Rest of Your Life, 97 Things Every Programmer Should Know, Henney K. (ed), O'Reilly, Sebastopol, CA.

OOP - Bài 7: Doanh thu trong tuần



Center of Excellence


    Bài 7
    Doanh thu trong tuần


Vạn vật được tạo tác từ không
Everything is from nothing
-- Jessie Fairweather

I. Bài toán

Hãy phát triển chương trình tính tổng doanh thu trong tuần, doanh thu bình quân ngày, doanh thu ngày cao nhất, và doanh thu ngày thấp nhất. Sau đây là ví dụ về doanh thu của một tuần:

1000.00, 500.00, 3000.00, 4000.00, 9000.00, 6000.00, 7000.00

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.

Hình 1


II. Giải pháp
Ta cần giải quyết các yêu cầu sau
  1. Tính tổng doanh thu tuần (total weekly sales)
    • Input: doanh thu hàng ngày trong tuần (daily sales figures)
    • Output: total weekly sales
  2. Tính doanh thu bình quân ngày (average daily sales)
    • Input: daily sales figures
    • Output: average daily sales
  3. Tính doanh thu ngày cao nhất (highest daily sales)
    • Input: daily sales figures
    • Output: highest daily sales
  4. Tính doanh thu ngày thấp nhất (lowest daily sales)
    • Input: daily sales figures
    • Output: lowest daily sales

1. Tính total weekly sales

1.1. Thiết kế sơ bộ

Xác định tên methods và parameters

Hình 2

Thử chuyển parameter thành field

Hình 3

Xác định data type cho field

Do dailySalesFigures là một tập hợp doanh thu của bảy ngày trong tuần, ta không thể sử dụng double cho field này, mà phải là một mảng (array) gồm nhiều double. Để chỉ định một array, ta bổ sung cặp ngoặc vuông.

Hình 4

Xác định tên class dựa vào field và method

Ta cần trả lời câu hỏi: Đối tượng nào chứa doanh thu của bảy ngày trong tuần, rồi từ đó tính được doanh thu tổng? Đó chính là doanh thu của một tuần (weekly sales).

Hình 5

Do đã xác định được tên class, giờ đây ta có thể đơn giản hóa tên method và field.

Hình 6


1.2. Test cases


sum()
No
figures
sum?
1
1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0
30_500.0



1.3. Phát triển method

Project: WeeklySales

package weeklysales;

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

public class WeeklySalesTest {
    @Test public void sum() {
        assertEquals(30_500.0, new WeeklySales(???).sum(), 0.0);
    }
}

Để thay thế cụm ??? bằng giá trị cụ thể, ta đặt doanh thu của bảy ngày trong tuần vào một array. Lưu ý là tất cả các giá trị này đều nằm trong cặp ngoặc nhọn, và chúng cách nhau bằng dấu phẩy.

public class WeeklySalesTest {
    @Test public void sum() {
        double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
        assertEquals(30_500.0, new WeeklySales(figures).sum(), 0.0);
    }
}

Từ WeeklySalesTest, ta tạo ra WeeklySales class.

package weeklysales;

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double[] figures) {
        this.figures = figures; 
    }

    public double sum() {
        return ???;
    }
}

Để tính tổng, ta sử dụng một bộ tích lũy (accumulator) lấy tên là acc. Ban đầu acc chưa tích lũy được gì nên bằng 0.0.

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double[] figures) {
        this.figures = figures; 
    }

    public double sum() {
        double acc = 0.0;
        ???
        return acc;
    }
}

Sau đó ta dùng vòng lặp for-each, lấy ra từng giá trị doanh thu (figure) bên trong figures, rồi tích lũy figure vào acc thông qua toán tử cộng dồn +=. (Ngoài +=, ta còn có -=, *=, /=, %=)

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double[] figures) {
        this.figures = figures; 
    }

    public double sum() {
        double acc = 0.0;
        for (double figure : this.figures)
            acc += figure;
        return acc;
    }
}


2. Tính average daily sales

2.1. Thiết kế sơ bộ

Do yêu cầu này dùng chung input với yêu cầu trước nên ta đặt chúng vào cùng một class.

Hình 7


2.2. Test cases


average()
No
figures
average?
1
1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0
4_357.14 (làm tròn về hai chữ số sau dấu thập phân từ kết quả 4_357.1428...)



2.3. Phát triển method

public class WeeklySalesTest {
    @Test public void sum() {
        double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
        assertEquals(30_500.0, new WeeklySales(figures).sum(), 0.0);
    }
    
    @Test public void average() {
        double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
        assertEquals(4_357.14, new WeeklySales(figures).average(), 0.0);
    }
}

Xét thấy có tình trạng code duplication trong hai test methods sum()average(), nhưng ta cần hoàn thành nhiệm vụ trước mắt, đó là phát triển average(). Doanh thu bình quân ngày sẽ bằng tổng doanh thu chia cho tổng số ngày, ta viết

package weeklysales;

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double[] figures) {
        this.figures = figures; 
    }

    public double sum() {
        double acc = 0.0;
        for (double figure : this.figures)
            acc += figure;
        return acc;
    }
    
    public double average() {
        return this.sum() / this.figures.length;
    }
}

Ở trên, bên trong average(), ta đã tận dụng method tính tổng sum() đã được phát triển từ trước. Thêm vào đó, ta còn dùng length field của figures array để xác định tổng số ngày. Dĩ nhiên có thể biết trước tổng số ngày là 7. Tuy vậy, việc sử dụng length sẽ làm giải pháp trở nên tổng quát hơn. Chẳng hạn, nếu cửa hàng đóng cửa ngày Chủ nhật, tức doanh số trong tuần chỉ gồm 6 ngày, thì code của ta không cần phải thay đổi.

Đến đây nếu thi hành WeeklySalesTest class, ta gặp tín hiệu đỏ với thông báo lỗi: expected:<4357.14> but was:<4357.14857142857>, tức kết quả của chương trình là 4357.14857142857, chứ không phải giá trị 4357.14 mà ta đã kỳ vọng. Lý do là ta đã chủ động làm tròn kết quả về hai chữ số sau dấu chấm thập phân. Để được tín hiệu xanh, ta cần xác định lại độ lệch cho phép giữa hai giá trị bên trong lệnh assertEquals(). Trước tiên, ta lấy giá trị lớn hơn trừ giá trị nhỏ hơn, 4357.14857142857 - 4357.14 = 0.00857142857. Từ đó độ lệch được xác định bằng cách giữ lại giá trị chỉ chứa chữ số khác 0 đầu tiên tính từ bên trái (tức 0.008), rồi tăng 1 vào chữ số đó, tức 0.009. Trường hợp vẫn gặp tín hiệu đỏ thì ta lại tiếp tục tăng 1 vào chữ số khác 0 cho đến khi gặp tín hiệu xanh.

    @Test public void average() {
        double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
        assertEquals(4_357.14, new WeeklySales(figures).average(), 0.009);
    }

Loại bỏ code duplication

Ta tiến hành loại bỏ tình trạng code duplication bằng cách khai báo figures là một field.

public class WeeklySalesTest {
    private double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
    
    @Test public void sum() {
        assertEquals(30_500.0, new WeeklySales(this.figures).sum(), 0.0);
    }
    
    @Test public void average() {
        assertEquals(4_357.14, new WeeklySales(this.figures).average(), 0.009);
    }
}

Hơn nữa, cả hai methods đều gọi new WeeklySales(this.figures), nên ta tiếp tục chuyển object này thành field và đặt tên là weeklySales.

public class WeeklySalesTest {
    private double[] figures = { 1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0 };
    private WeeklySales weeklySales = new WeeklySales(this.figures);
    
    @Test public void sum() {
        assertEquals(30_500.0, this.weeklySales.sum(), 0.0);
    }
    
    @Test public void average() {
        assertEquals(4_357.14, this.weeklySales.average(), 0.009);
    }
}

Ta còn có thể cải tiến code thêm một chút thông qua việc vận dụng khái niệm varargs, tức variable-length argument list (danh sách đối số có chiều dài biến động). Trong đoạn code trên, thay vì phải định nghĩa một array lấy tên là figures, rồi truyền array đó vào lệnh new WeeklySales(figures), ta có thể viết trực tiếp như sau

public class WeeklySalesTest {
    private WeeklySales weeklySales = new WeeklySales(
            1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0);
    
    @Test public void sum() {
        assertEquals(30_500.0, this.weeklySales.sum(), 0.0);
    }
    
    @Test public void average() {
        assertEquals(4_357.14, this.weeklySales.average(), 0.009);
    }
}

Khi đó WeeklySales class cần phải được viết dưới đây, ở đó bên trong parameter của constructor, ta đã thay cặp ngoặc vuông bằng ba dấu chấm.

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double... figures) {
        this.figures = figures; 
    }

    public double sum() {
        double acc = 0.0;
        for (double figure : this.figures)
            acc += figure;
        return acc;
    }
    
    public double average() {
        return this.sum() / this.figures.length;
    }
}


3. Tính highest daily sales

3.1. Thiết kế sơ bộ

Hình 8


3.2. Test cases


max()
No
figures
max?
1
1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0
9_000.0



3.3. Phát triển method

WeeklySalesTest.java

public class WeeklySalesTest {
    private WeeklySales weeklySales = new WeeklySales(
            1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0);
    
    @Test public void sum() {
        assertEquals(30_500.0, this.weeklySales.sum(), 0.0);
    }
    
    @Test public void average() {
        assertEquals(4_357.14, this.weeklySales.average(), 0.009);
    }
    
    @Test public void max() {
        assertEquals(9_000.0, this.weeklySales.max(), 0.0);
    }
}

WeeklySales.java

public class WeeklySales {
    private double[] figures;

    public WeeklySales(double... figures) {
        this.figures = figures; 
    }

    public double sum() {
        double acc = 0.0;
        for (double figure : this.figures)
            acc += figure;
        return acc;
    }
    
    public double average() {
        return this.sum() / this.figures.length;
    }
    
    public double max() {
        return ???;
    }
}

Để tìm được doanh thu cao nhất, ta áp dụng thuật toán (algorithm) sau: đầu tiên giả định rằng doanh thu cao nhất hiện thời (current max) là hằng số âm vô cực (negative infinity).

    public double getMax() {
        double currentMax = Double.NEGATIVE_INFINITY;
        ???
        return currentMax;
    }

Sau đó ta dùng vòng lặp for-each để lấy ra từng giá trị (figure) bên trong figures array,

    public double max() {
        double currentMax = Double.NEGATIVE_INFINITY;
        for (double figure : this.figures)
            ???
        return currentMax;
    }

Mỗi lần lấy được figure nào ra, ta so sánh nó với currentMax, nếu lớn hơn currentMax, thì figure đó sẽ trở thành currentMax.

    public double max() {
        double currentMax = Double.NEGATIVE_INFINITY;
        for (double figure : this.figures)
            if (figure > currentMax)
                currentMax = figure;
        return currentMax;
    }

Sau khi đã so sánh currentMax với toàn bộ sales trong dailySales, currentMax chính là doanh số cao nhất.


4. Tính lowest daily sales

Phần này dành làm bài tập. Lưu ý hằng số dương vô cực sẽ là Double.POSITIVE_INFINITY.


III. Tổng kết

Bài viết đã vận dụng cấu trúc dữ liệu array, vòng lặp for-each, bộ tích lũy (accumulator), và toán tử cộng dồn +=, để thực hiện những tính toán trên tập hợp các số double. Khái niệm về danh sách đối số có chiều dài biến động (varargs) cũng đã được đề cập. Ngoài ra, bài còn trình bày một phương pháp xác định độ lệch cho phép giữa hai giá trị double trong quá trình kiểm thử.


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

Thứ Bảy, 4 tháng 2, 2012

JSF - Bài 7: Doanh thu trong tuần

    Center of Excellence


    Bài 7
    Doanh thu trong tuần
I. Bài toán

Hãy phát triển một web application để tính tổng doanh thu trong tuần, doanh thu bình quân ngày, doanh thu ngày cao nhất, và doanh thu ngày thấp nhất. Ví dụ về doanh thu của một tuần như sau:

1000.00, 500.00, 3000.00, 4000.00, 9000.00, 6000.00, 7000.00

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

Hình 1


II. Giải pháp

Project: WeeklySalesJSF

1. Thiết kế UI screen

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <head>
        <title>Weekly Sales</title>
        <script src="resources/clearForm.js"/>
        <style>
            table input,
            fieldset input {
                width: 100px;
            }
            fieldset {
                margin: 0 60% 0 0;
            }
            fieldset label {
                width: 140px;
                float: left;
                text-align: right;
            }
        </style>
    </head>
    <body>
        <h2>Weekly Sales</h2>
        
        <table>
            <thead>
                <tr>
                    <th>Monday</th>
                    <th>Tuesday</th>
                    <th>Wednesday</th>
                    <th>Thursday</th>
                    <th>Friday</th>
                    <th>Saturday</th>
                    <th>Sunday</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td><input/></td>
                    <td><input/></td>
                    <td><input/></td>
                    <td><input/></td>
                    <td><input/></td>
                    <td><input/></td>
                    <td><input/></td>
                </tr>
            </tbody>
        </table>
        
        <fieldset>
            <legend>Statistics</legend>
            
            <label>Total weekly sales $</label>
            <input disabled="true"/><br/>
            
            <label>Average daily sales $</label>
            <input disabled="true"/><br/>
            
            <label>Highest daily sales $</label>
            <input disabled="true"/><br/>
            
            <label>Lowest daily sales $</label>
            <input disabled="true"/>
        </fieldset>
        
        <div>
            <input type="submit" value="Compute Statistics"/>
            <input type="button" value="Clear" onclick="clearForm(this.form)"/>
        </div>
    </body>
</html>

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

2.1. Thiết kế managed bean

Thay vì phải dùng bảy fields trong managed bean để lưu trữ bảy doanh số (sales) nhập vào từ user, ta tập hợp chúng vào một array, gọi là salesFigures.

Hình 2

Tuy nhiên, data type của salesFigures không thể là Double[], bởi vì đây là input component, ta phải có setter cho từng thành phần của Double[], tức phải có setter cho từng Double, nhưng Double đã được qui định sẵn là không có setter. Để giải quyết vấn đề này, ta có thể dùng wrapper class, lấy tên là SalesFigure, để bọc một Double lại, rồi bổ sung getter và setter cho wrapper đó.

Hình 3

2.2. Tạo fields và methods cho managed bean

Tạo SalesFigure class

package controller;

public class SalesFigure {
    private Double value;

    public SalesFigure() {
    }

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }
}


Tạo managed bean

Lưu ý rằng ta cần khởi tạo SalesFigure[]. Dưới đây, ta đặt code khởi tạo bên trong makeSalesFigures() method, và method này được gọi thi hành bên trong constructor.

package controller;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named(value = "index")
@RequestScoped
public class Index {
    private SalesFigure[] salesFigures;
    private Double sum;
    private Double average;
    private Double max;
    private Double min;

    public Index() {
        salesFigures = makeSalesFigures();
    }

     private SalesFigure[] makeSalesFigures() {
        SalesFigure[] figures = new SalesFigure[7];
        for (int i = 0; i < figures.length; i++)
            figures[i] = new Figure();
        return figures;
    }
    
    public void computeStatistics() {
        // TODO
    }

    public SalesFigure[] getSalesFigures() {
        return salesFigures;
    }

    public Double getAverage() {
        return average;
    }

    public Double getMax() {
        return max;
    }

    public Double getMin() {
        return min;
    }

    public Double getSum() {
        return sum;
    }
}


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

Khi ràng buộc <table> ở UI screen với salesFigures[] trong managed bean, ta dùng một cấu trúc lặp của JSF, tương tự như for-each, đó là <ui:repeat>. Cần lưu ý value attribute trong <h:inputText>#{figure.value}.

Đối với các components khác, ta thực hiện việc ràng buộc như đã làm trong những bài trước.

Hình 4


3. Phát triển model

3.1. Design

Hình 5

Lưu ý là ta không cần dùng wrapper class cho salesFigures bên trong model. Tất nhiên là sau này, khi kết nối giữa managed bean và model, ta cần phải chuyển đổi từ SalesFigure[] về double[].


3.2. Test class

package model;

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

public class WeeklySalesTest {
    private WeeklySales weeklySales = new WeeklySales(
            1_000.0, 500.0, 3_000.0, 4_000.0, 9_000.0, 6_000.0, 7_000.0);
    
    @Test public void sum() {
        assertEquals(30_500.0, weeklySales.sum(), 0.0);
    }
    
    @Test public void average() {
        assertEquals(4_357.14, weeklySales.average(), 0.009);
    }
    
    @Test public void max() {
        assertEquals(9_000.0, weeklySales.max(), 0.0);
    }
    
    @Test public void min() {
        assertEquals(500.0, weeklySales.min(), 0.0);
    }
}

3.3. Main class

package model;

public class WeeklySales {
    private final double[] salesFigures;

    public WeeklySales(double... salesFigures) {
        this.salesFigures = salesFigures;
    }
    
    public double sum() {
        double acc = 0.0;
        for (double saleFigure : this.salesFigures)
            acc += salesFigure;
        return acc;
    }
    
    public double average() {
        return sum() / salesFigures.length;
    }
    
    public double max() {
        double currentMax = Double.NEGATIVE_INFINITY;
        for (double salesFigure : this.salesFigures)
            if (salesFigure > currentMax)
                currentMax = salesFigure;
        return currentMax;
    }
    
    public double min() {
        double currentMin = Double.POSITIVE_INFINITY;
        for (double salesFigure : this.salesFigures)
            if (salesFigure < currentMin)
                currentMin = salesFigure;
        return currentMin;
    }
}


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

Như đã đề cập ở trên, trước khi gọi model từ managed bean, ta cần chuyển đổi Figure[] về double[]. Điều này thể hiện trong toDoubleArrayOfFigures().

    public void computeStatistics() {
        WeeklySales weeklySales = new WeeklySales(salesFiguresToDoubles());
        sum = weeklySales.sum();
        average = weeklySales.average();
        max = weeklySales.max();
        min = weeklySales.min();
    }
    
    private double[] salesFiguresToDoubles() {
        double[] a = new double[figures.length];
        for (int i = 0; i < a.length; i++)
            a[i] = figures[i].getValue();
        return a;
    }


5. Định dạng số

Bây giờ ta muốn hiển thị kết quả tính toán theo các điều kiện sau
  • Áp dụng dấu phân cách hàng ngàn (thousands separator) cho những số lớn
  • Nếu không có chữ số khác 0 nào đứng trước dấu chấm thập phân (decimal point) thì sẽ hiển thị số 0 trước decimal point
  • Có hai chữ số sau decimal point

    Ta điều chỉnh lại index.xhtml như sau

            <fieldset>
                <legend>Statistics</legend>

                <label>Total weekly sales: $</label>
                <h:inputText disabled="true" value="#{index.sum}">
                    <f:convertNumber pattern="#,##0.00"/>
                </h:inputText><br/>

                <label>Average daily sales: $</label>
                <h:inputText disabled="true" value="#{index.average}">
                    <f:convertNumber pattern="#,##0.00"/>
                </h:inputText><br/>

                <label>Highest daily sales: $</label>
                <h:inputText disabled="true" value="#{index.max}">
                    <f:convertNumber pattern="#,##0.00"/>
                </h:inputText><br/>

                <label>Lowest daily sales: $</label>
                <h:inputText disabled="true" value="#{index.min}">
                    <f:convertNumber pattern="#,##0.00"/>
                </h:inputText>
            </fieldset>


6. Kiểm tra tính hợp lệ của input

Hình 6


III. Tổng kết

Bài viết đã đề cập các khái niệm và kỹ thuật sau
  • Cấu trúc bảng với <table>
  • Wrapper class cùng getter và setter để ràng buộc web page với managed bean
  • Định dạng với <f:convertNumber>


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 8.
  2. Ed Burns, Chris Schalk (2010) JavaServer Faces 2.0: The Complete Reference, McGraw-Hill.


V. Bài tập

Hãy điều chỉnh để application có thể tiếp nhận input từ user theo các điều kiện đã được đề cập trong phần định dạng output. Ví dụ, application có thể tiếp nhận input có giá trị là 1,234.56.