3. Tái sử dụng mã chương trình
(1)
Xét ví dụ DataSet.java (ch07/input)
Tìm giá trị trung bình và giá trị lớn nhất
của 1 tập các giá trị số
Phải làm gì nếu muốn tìm trị trung bình và lớn
nhất của 1 tập các tài khoản ngân hàng
(BankAccount, ch08)?
Hay muốn tìm loại tiền xu có giá trị cao nhất
trong 1 tập các loại tiền xu (Coin, bài 6)?
2009-2010 OOP - http://mim.hus.edu.vn/elearning 3
4. Tái sử dụng mã chương trình
(2)
public class DataSet // Modified for BankAccount objects
{
. . .
public void add(BankAccount x) {
sum = sum + x.getBalance();
if (count == 0 || maximum.getBalance() < x.getBalance())
maximum = x;
count++;
}
public BankAccount getMaximum() {
return maximum;
}
private double sum;
private BankAccount maximum;
private int count;
}
2009-2010 OOP - http://mim.hus.edu.vn/elearning 4
5. Tái sử dụng mã chương trình
(3)
public class DataSet // Modified for Coin objects
{
. . .
public void add(Coin x) {
sum = sum + x.getValue();
if (count == 0 || maximum.getValue() < x.getValue())
maximum = x;
count++;
}
public Coin getMaximum() {
return maximum;
}
private double sum;
private Coin maximum;
private int count;
}
2009-2010 OOP - http://mim.hus.edu.vn/elearning 5
6. Tái sử dụng mã chương trình
(4)
Các ví dụ trên có chung cơ chế phân tích dữ liệu, chỉ khác
nhau về độ đo dữ liệu
Các lớp liên quan có thể thống nhất 1 phương thức getMeasure
để thu được giá trị đo dùng trong quá trình phân tích
Khi đó có thể cài 1 lớp duy nhất DataSet có thể sử dụng chung,
trong đó phần thân của phương thức add có dạng:
sum = sum + x.getMeasure();
if (count == 0 || maximum.getMeasure() <
x.getMeasure())
maximum = x;
count++;
Kiểu của biến x là gì? x cần phải tham chiếu tới bất kì lớp nào có
hàm getMeasure.
2009-2010 OOP - http://mim.hus.edu.vn/elearning 6
7. Tái sử dụng mã chương trình
(5)
Để mô tả các lớp có chung một số thao tác nào đó
(như getMeasure), trong Java người ta khai báo một
kiểu giao diện (interface) đặc tả các thao tác chung
đó
public interface Measurable {
double getMeasure();
}
Khai báo interface liệt kê danh sách các nguyên mẫu
phương thức (prototype) mà kiểu giao diện cần có
Các lớp có chung giao diện này sẽ cài đặt
(implements) cụ thể các phương thức đặc tả trong
kiểu giao diện.
2009-2010 OOP - http://mim.hus.edu.vn/elearning 7
8. Lớp DataSet dùng chung cho
các đối tượng đo được
public class DataSet
{
. . .
public void add(Measurable x) {
sum = sum + x.getMeasure();
if (count == 0 || maximum.getMeasure() < x.getMeasure())
maximum = x;
count++;
}
public Measurable getMaximum() {
return maximum;
}
private double sum;
private Measurable maximum;
private int count;
}
2009-2010 OOP - http://mim.hus.edu.vn/elearning 8
9. Cài đặt một kiểu giao diện
Để khai báo 1 lớp cài đặt 1 kiểu giao diện, dùng
từ khoá implements
public class BankAccount implements Measurable {
public double getMeasure() {
return balance;
}
// Additional methods and fields
}
1 lớp có thể cài đặt nhiều kiểu giao diện
Khi cài đặt 1 kiểu giao diện, lớp đó phải định nghĩa tất
cả các phương thức đã đặc tả trong kiểu giao diện
2009-2010 OOP - http://mim.hus.edu.vn/elearning 9
10. Cú pháp
Định nghĩa 1 kiểu giao diện
public interface InterfaceName {
// method signatures
}
Tất cả các phương thức trong kiểu giao diện đều
đương nhiên là public
Cài đặt 1 kiểu giao diện
public class ClassName implements
InterfaceName, InterfaceName, ... {
// interface method implementations
// other methods
// instance variables
}
VD: Xem các lớp trong ch11/measure1
2009-2010 OOP - http://mim.hus.edu.vn/elearning 10
11. Sơ đồ UML (1)
Sử dụng kiểu giao diện có thể giúp giảm tính
móc nối giữa các lớp
Kí hiệu UML:
Kiểu giao diện được đánh dấu bằng nhãn
«interface»
Mũi tên nét đứt với mũi hình tam giác kí hiệu
quan hệ “is-a” giữa 1 lớp và 1 giao diện
Mũi tên nét đứt với mũi hình chữ v mở kí hiệu
quan hệ “uses” hay quan hệ phụ thuộc
2009-2010 OOP - http://mim.hus.edu.vn/elearning 11
13. Chuyển đổi kiểu giữa lớp và
giao diện
Có thể chuyển 1 kiểu lớp về 1 kiểu giao diện, với điều
kiện lớp cài đặt giao diện
BankAccount account = new BankAccount(10000);
Measurable x = account; // OK
Ngược lại, để chuyển 1 kiểu giao diện về 1 kiểu lớp phải
thực hiện ép kiểu
DataSet coinData = new DataSet();
coinData.add(new Coin(0.25, "quarter"));
coinData.add(new Coin(0.1, "dime"));
. . .
// Get the largest coin
Measurable max = coinData.getMaximum();
String name = max.getName(); // ERROR
Coin maxCoin = (Coin) max;
String name = maxCoin.getName();
2009-2010 OOP - http://mim.hus.edu.vn/elearning 13
14. Tính đa hình
Biến kiểu giao diện dùng để chứa tham chiếu tới đối
tượng của 1 lớp cài đặt giao diện đó
Measurable x;
x = new BankAccount(10000);
x = new Coin(0.1, "dime");
Có thể gọi bất kì phương thức nào của kiểu giao diện
với tham biến ẩn x
Tuỳ theo x đang tham chiếu tới đối tượng thuộc lớp nào thì áp
dụng phương thức cài đặt trong lớp đó => tính đa hình
Gọi là liên kết muộn (late binding): khi chạy chương trình mới xác
định dùng phương thức của lớp nào
Khác với liên kết sớm (early binding): chương trình dịch xác định
dùng phương thức nào trong các phương thức cùng tên
2009-2010 OOP - http://mim.hus.edu.vn/elearning 14
15. Sử dụng kiểu giao diện cho
gọi lại (callback)
Nhược điểm của kiểu giao diện Measurable:
Chỉ dùng được giao diện này để đo trong các lớp thuộc quyền của
người lập trình
Chỉ đo/đánh giá được đối tượng theo 1 tiêu chí
VD với 1 tài khoản tiết kiệm không thể vừa đo theo số tiền trong tài
khoản, vừa đo theo lãi suất
Cơ chế gọi lại (callback): cho phép 1 lớp gọi tới 1 phương
thức đặc trưng tuỳ theo thông tin cần khai thác
Xét lớp DataSet
Trong VD trước, việc đo 1 đối tượng do 1 phương thức của đối
tượng đó đảm nhiệm
Thay cho cách làm đó, dùng 1 phương thức đo có tham biến hiện
là đối tượng cần đo
2009-2010 OOP - http://mim.hus.edu.vn/elearning 15
16. Sử dụng kiểu giao diện cho
gọi lại (2)
Muốn vậy:
Định nghĩa kiểu giao diện
public interface Measurer {
double measure(Object anObject);
}
Định nghĩa phương thức add trong DataSet
public void add(Object x) {
sum = sum + measurer.measure(x);
if (count == 0 || measurer.measure(maximum)
< measurer.measure(x))
maximum = x;
count++;
}
2009-2010 OOP - http://mim.hus.edu.vn/elearning 16
17. Sử dụng kiểu giao diện cho
gọi lại (3)
Cài đặt giao diện Measurer:
public class RectangleMeasurer implements Measurer {
public double measure(Object anObject) {
Rectangle aRectangle = (Rectangle) anObject;
double area =
aRectangle.getWidth()*aRectangle.getHeight();
return area;
}
}
Sử dụng DataSet:
Measurer m = new RectangleMeasurer();
DataSet data = new DataSet(m);
data.add(new Rectangle(5, 10, 20, 30));
data.add(new Rectangle(10, 20, 30, 40));
. . .
2009-2010 OOP - http://mim.hus.edu.vn/elearning 17
19. Lớp trong (inner classes)
Có thể định nghĩa 1 lớp đơn giản bên trong 1 phương thức
Lớp này chỉ dùng được trong phương thức
VD (ch11/measure3)
public class DataSetTester3 {
public static void main(String[] args) {
class RectangleMeasurer implements Measurer {
. . .
}
Measurer m = new RectangleMeasurer();
DataSet data = new DataSet(m);
. . .
}
}
Chương trình dịch sẽ tạo ra 1 tệp riêng cho lớp trong
DataSetTester3$1RectangleMeasurer.class
Nếu 1 lớp được định nghĩa trong 1 lớp khác nhưng ngoài các phương
thức của lớp đó thì có thể dùng được lớp đó trong mọi phương thức
của lớp ấy.
2009-2010 OOP - http://mim.hus.edu.vn/elearning 19
20. Ví dụ minh hoạ: Xử lí sự kiện
đếm thời gian
javax.swing.Timer sinh các sự kiện đếm thời gian cách đều nhau,
hữu dụng khi cần cập nhật 1 đối tượng trong 1 khoảng thời gian đều đặn
Cách dùng (Xem ch11/timer1):
Định nghĩa 1 lớp MyListener cài đặt kiểu giao diện ActionListener: gửi sự kiện
tới bộ nghe
public interface ActionListener {
void actionPerformed(ActionEvent event);
}
class MyListener implements ActionListener {
void actionPerformed(ActionEvent event){
// This action will be executed at each timer event
Place listener action here
}
}
Tạo đối tượng Timer với bộ nghe đã cài đặt
MyListener listener = new MyListener();
Timer t = new Timer(interval, listener);
t.start();
2009-2010 OOP - http://mim.hus.edu.vn/elearning 20
21. Truy cập các biến xung quanh
(1)
Các phương thức của 1 lớp trong có thể truy cập
các biến định nghĩa trong phạm vi xung quanh
Hữu dụng khi cài đặt các bộ điều khiển sự kiện
VD: Làm hình động – di chuyển 1 hình 10 lần trong 1s
class Mover implements ActionListener {
public void actionPerformed(ActionEvent event) {
// Move the rectangle
}
}
ActionListener listener = new Mover();
final int DELAY = 100; // Milliseconds between timer ticks
Timer t = new Timer(DELAY, listener);
t.start();
2009-2010 OOP - http://mim.hus.edu.vn/elearning 21
22. Truy cập các biến xung quanh
(2)
Phương thức actionPerformed có thể truy cập các biến trong phạm vi
xung quanh (ch11/timer2):
public static void main(String[] args) {
. . .
final Rectangle box = new Rectangle(5, 10, 20, 30);
class Mover implements ActionListener {
public void actionPerformed(ActionEvent event) {
// Move the rectangle box.translate(1, 1);
}
}
. . .
}
Các biến địa phương muốn truy cập được bởi lớp trong phải được khai
báo là final
Lớp trong có thể truy cập trường dữ liệu của đối tượng thuộc lớp ngoài
1 đối tượng thuộc lớp trong được tạo trong 1 phương thức tĩnh chỉ có
thể truy cập các trường dữ liệu tĩnh
2009-2010 OOP - http://mim.hus.edu.vn/elearning 22