58
VN- INFO LẬP TRÌNH JAVA HIỆU QUẢ Tác giả: Joshua Bloch Trình bày: Lê Quốc Anh 30-05-2015 VN- INFO

Lap trinh java hieu qua

  • Upload
    le-anh

  • View
    73

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Lap trinh java hieu qua

VN-INFO

LẬP TRÌNH JAVA HIỆU QUẢ

Tác giả: Joshua BlochTrình bày: Lê Quốc Anh

30-05-2015

VN-INFO

Page 2: Lap trinh java hieu qua

Có nhận xét gì về đoạn code này ?…

List<Integer> lst = new ArrayList<Integer>();

Iterator<Integer> iter = lst.iterator();

while (iter.hasNext()) {

// ...

iter.remove();

}

… Lst.Size()

2Running timeNguồn: Murex Test

Page 3: Lap trinh java hieu qua

3

Có gì khác nhau?

List<Integer> lst = new ArrayList<Integer>();

Iterator<Integer> iter = lst.iterator();

while (iter.hasNext()) {

// ...

iter.remove();

}

List<Integer> lst = new LinkedList<Integer>();

Iterator<Integer> iter = lst.iterator();

while (iter.hasNext()) {

// ...

iter.remove();

}

Page 4: Lap trinh java hieu qua

4

So sánh thời gian thực hiện remove

size=1000: time =166 ms size=1000: time =57 ms

size=10000: time =8167 ms size=10000: time =60 ms

Lst.Size()

4Running time

Lst.Size()

4Running time

ArrayList LinkedList

Page 5: Lap trinh java hieu qua

5

Nguyên nhân

ArrayList

LinkedList

Page 6: Lap trinh java hieu qua

6

Nguyên nhân

LinkedList<E> ArrayList<E>Get (idx) O(n) O(1)

Add(e) O(1) O(1)

Add(idx, e) O(n) O(n-idx)

Remove(idx) O(n) O(n-idx)

Iterator.remove() O(1) O(n-idx)

ListIterator.add(e) O(1) O(n-idx)

Page 7: Lap trinh java hieu qua

Lập trình hiệu quả

7

Yêu cầu chung-dễ đọc -dễ bảo trì -dễ mở rộng

Yêu cầu riêng: -Vận dụng ngôn ngữ-Kỹ thuật lập trình

Page 8: Lap trinh java hieu qua

8

Page 9: Lap trinh java hieu qua

PHẦN 1: TẠO VÀ HỦY ĐỐI TƯỢNG

9

Page 10: Lap trinh java hieu qua

Quy tắc 1: Dùng hàm gọi đối tượng

• Định nghĩa: Là hàm trả lại một đối tượng của lớp.public static Boolean valueOf(boolean b) {return b ? Boolean.TRUE : Boolean.FALSE;}

• Lợi ích:– Không cần phải khởi tạo lớp mỗi lần gọi– Gọi đến cùng một đối tượng (ví dụ Singleton ở phần sau)– Dễ đọc (hàm có tên gọi, rút gọn tham số, etc..). Ví dụ:

•Cách bình thường: Map<String, List<String>> m=new HashMap<String, List<String>>();•Với hàm khởi tạo: Map<String, List<String>> m=HashMap.newInstance();

• Quy ước: – lớp chứa hàm kiểu này không nên được khởi tạo trực tiếp nữa, vì đã có hàm khởi tạo và trả lại đối

tượng của lớp rồi. Do vậy lớp hoặc constructor của lớp được khai báo private. – Nếu đối tượng trả về có tên gọi là Type thì lớp chứa hàm factory sẽ có tên là Types. Ví dụ

public interface book { public void cover(); } public abstract class Books() { public static Book createBook(){ return new MyBook(); } private class MyBook implements Book{ public void cover(){} } }

10

Page 11: Lap trinh java hieu qua

Quy tắc 1: Dùng hàm gọi đối tượng

• Điểm hạn chế:– Lớp chứa các static factory methods không cho phép

kế thừa. Ví dụ không thể kế thừa lớp java.util.Collections (theo quy ước lớp này có giao diện tên là java.util.Collection không có s). Nguyên nhân là để các lập trình viên nên dùng composition thay vì inheritance mà ta sẽ xem trong Quy tắc 16.

– Không có sự khác biệt rõ ràng giữa các hàm static factory methods và các static factory khác.

11

Page 12: Lap trinh java hieu qua

Quy tắc 2: Dùng builder cho lớp có nhiều tham số • Xem xét lớp:

public class NutritionFacts {private final int servingSize; // (mL) requiredprivate final int servings; // (per container) requiredprivate final int calories; // optionalprivate final int fat; // (g) optionalprivate final int sodium; // (mg) optionalprivate final int carbohydrate; // (g) optional…

}NutritionFacts cocaCola = new NutritionFacts(size, serving, calos, fat, sodium, carbo);

• Hạn chế: Nhiều tham số tùy chọn nhưng vẫn phải khai báo hết khi khởi tạo• Giải pháp 1: Khởi tạo đối tượng và dùng hàm setter.

– Ví dụ public void setFat(int val) { fat = val; }– Không khả thi cho lớp bất biến

• Giải pháp 2: Dùng lớp trung gian builder để tùy chọn khai báo tham sốpublic class NutritionFacts {

public static class Builder {}

}12

Page 13: Lap trinh java hieu qua

Quy tắc 2: Dùng builder cho lớp có nhiều tham số public class NutritionFacts {

…public static class Builder {

private final int servingSize; // bắt buộcprivate int calories = 0; // tùy chọn, khởi tạo với giá trị mặc địnhprivate int fat = 0; // tùy chọn, khởi tạo với giá trị mặc địnhpublic Builder(int servingSize) {this.servingSize = servingSize; }public Builder calories(int val) { calories = val; return this; }public Builder fat(int val) { fat = val; return this; }public NutritionFacts build() {return new NutritionFacts(this); }}private NutritionFacts(Builder builder) {

servingSize = builder.servingSize;calories = builder.calories;…

}NutritionFacts cocaCola = new NutritionFacts.Builder(240). calories(100).fat(35).build();

13

Page 14: Lap trinh java hieu qua

Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum• Câu hỏi: Singleton khác với lớp bất biến???• Định nghĩa: Lớp chỉ được khởi tạo 1 lần duy nhất. Ví dụ:// Singleton with static factorypublic class Elvis {

private static final Elvis INSTANCE = new Elvis();private Elvis() { ... }public static Elvis getInstance() { return INSTANCE; }public void leaveTheBuilding() { ... }

}• Câu hỏi: Khi lớp Singleton thực thi Serializable?

14

Page 15: Lap trinh java hieu qua

Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum// Singleton with static factorypublic class Elvis implements Serializable {

private transient String[] songs= {« a», « b »};

private static final Elvis INSTANCE = new Elvis();private Elvis() { ... }public static Elvis getInstance() { return INSTANCE; }public void leaveTheBuilding() { ... }

// readResolve method to preserve singleton propertyprivate Object readResolve() {

// Return the one true Elvis and let the garbage collector// take care of the Elvis impersonator.return INSTANCE;

}}•Deserialization sẽ gọi hàm constructor để tạo ra đối tượng mới ngay cả khi nó được khai báo private•readResolve() bình thường sẽ trả lại đối tượng mới được tạo mới sau khi deserialized => viết lại • Các thuộc tính (eg., version) nên được khai báo với từ khóa transient nếu giá trị của chúng không đổi trước và sau khi serialization hoặc chúng có kiểu khác primitives.

15

Page 16: Lap trinh java hieu qua

Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum• Giải pháp ngắn gọn và an toàn hơn là dùng Enum

public enum Elvis {INSTANCE;

private transient String[] songs= {« a », « b »};public void leaveTheBuilding() { ... }}

• Enum mặc định đã thực thi serializable

16

Page 17: Lap trinh java hieu qua

Quy tắc 4: Cấm khởi tạo lớp với private constructor• Đôi khi ta muốn xây dựng lớp chỉ chứa các

static methods hay static fields• Giải pháp khai báo lớp kiểu trừu tượng

Abstract không hiệu quả vì lớp con của nó vẫn có thể được khởi tạo

• Giải pháp triệt để là khai báo lớp với private constructor

17

Page 18: Lap trinh java hieu qua

Quy tắc 5: Tránh tạo đối tượng thừa

• Ví dụ: String s = new String("stringette"); – Có đến 2 đối tượng được tạo ra– Tương đương: String s = "stringette";

• Giải pháp chung:– Sử dụng static factory methods (Quy tắc 1)– Sử dụng phương pháp lazy initialization– Sử dụng kiểu biến primitives (eg., long, int,

double) thay vì boxed primitives (eg., Long, Integer, Double)

18

Page 19: Lap trinh java hieu qua

Quy tắc 6: Loại bỏ các tham chiếu đến đối tượng không dùng nữa • Xét ví dụ sau:

public class Stack {private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {

elements = new Object[DEFAULT_INITIAL_CAPACITY];

}public Object pop() {

if (size == 0) throw new EmptyStackException();Object result = elements[--size];

return result;}

19

Page 20: Lap trinh java hieu qua

Quy tắc 6: Loại bỏ các tham chiếu đến đối tượng không dùng nữa • Xét ví dụ sau:

public Object pop() {if (size == 0) throw new EmptyStackException();Object result = elements[--size];

elements[size] = null; // Eliminate obsolete reference return result;}

• Giải pháp: chú ý giải pháp bộ nhớ khi xây dựng caches, listeners hoặc callbacks

20

Page 21: Lap trinh java hieu qua

Quy tắc 7: Tránh sử dụng finalizers• Định nghĩa: Garbage collector (GC) trong Java phụ trách

giải phóng các đối tượng không còn có thể được sử dụng (unreacheable). Trước khi giải phóng đối tượng thì hàm finalize() của đối tượng đó sẽ được gọi.

• Sử dụng finalizers nguy hiểm? Vì sao?– Không dự đoán được kết quả: thời gian từ lúc một đối tượng

thành unreachable cho tới lúc được GC xử lý là không xác định.

– Ngay cả ép chạy GC bằng lệnh System.gc vì nó cũng không đảm bảo để finalize() của đối tượng được thực thi tức thời.

– Sử dụng finalize() làm tăng thời gian thực hiện lên 430 lần (cần kiểm chứng lại)

21

Page 22: Lap trinh java hieu qua

Quy tắc 7: Tránh sử dụng finalizers

• Giải pháp– Thay thế bằng try..finally – Hoặc gọi lệnh System.runFinalizersOnexit hoặc

Runtime.runFinalizersOnExit. Tuy nhiên cả 2 hàm này không còn được sử dụng (deprecated) bởi lý do nó có thể được gọi ngay cả với đối tượng reacheable => gây ra các hành vi bất thường không kiểm soát được

22

Page 23: Lap trinh java hieu qua

Quy tắc 7: Tránh sử dụng finalizers• Giải pháp

– Nếu buộc phải sử dụng, phải đảm bảo finalize() luôn được gọi cả ở lớp cha (superclass) và lớp con (subclass). Ví dụ

@Override protected void finalize() throws Throwable { try {... // Finalize subclass state }

finally { super.finalize(); } }– Hoặc sử dụng phương pháp finalizer guardian để đảm bảo

finalize() luôn được gọi. Ví dụ:public class Foo {

Foo object private final Object finalizerGuardian = new Object() {

@Override protected void finalize() throws Throwable { }

}; }

23

Page 24: Lap trinh java hieu qua

PHẦN 2: PHƯƠNG THỨC CHUNG CHO CÁC ĐỐI TƯỢNG

24

Page 25: Lap trinh java hieu qua

Quy tắc 8: Điều kiện khi viết lại hàm equals()• Trước hết trả lời câu hỏi liệu việc viết lại hàm equals có

cần thiết không???– Co hai loai lop (value classes vs activity classes). Doi voi mot

activity class nhu lop Thread thi khong can phai viet lai– Khong can quan tam lieu ham equals hien co co hop ly khong.

Vi du nhu viet lai ham equals cho lop Random de bat buoc 2 the hien cua lop Random duoc cho la bang nhau neu va chi neu chung san xuat mot chuoi cac gia tri giong nhau. Cai nay phai xem lai nhu cau co can thiet ko.

– Cac lop cua Set: HashSet, TreeSet,.. hay cac lop cua List: ArrayList, LinkedList, ... da viet de ham equals thich hop cho chung roi thi cung ko nen viet lai.

– Cac lop private hay package-private thi tuyet doi ko can viet lai ham equals vi chung ko the duoc truy cap toi :).

25

Page 26: Lap trinh java hieu qua

Quy tắc 8: Điều kiện khi viết lại hàm equals()• Nếu cần viết lại hàm equals thì phải tuân thủ 5 quy định sau:

1. Phan chieu: No phai bang no, x.equals(x) luon dung2. Doi xung: x.equals(y) dung khi va chi khi y.equals(x) cung dung3. Bac cau: Neu x.equals(y) va y.equals(z) dung thi x.equals(z) cung

dung4. Vung chac: Neu x va y la cac gia tri khong null thi x.equals(y) se

phai luon tra lai gia tri nhu nhau moi khi duoc goi den cho du cac gia tri khong thuoc equals cua lop x va y co the bi thay doi. Loi khuyen la khong neu dung cac resources khong tin cay de xay dung ham equals.

5. Loi goi x.equals(null) luon tra lai false.–

26

Page 27: Lap trinh java hieu qua

Quy tắc 8: Điều kiện khi viết lại hàm equals()• Cách xây dựng hàm equals hiệu quả:

– Dau tien kiem tra bang toan tu == xem lieu chung co reference den cung doi tuong khong (vd: if (o == this) return true;)

– Kiem tra xem chung co cung kieu khong bang toan tu instanceof (vd: if (!(o instanceof PhoneNumber))return false;)

– Ep kieu de chung ve cung kieu (vd: PhoneNumber pn = (PhoneNumber)o;)

– So sanh tung doi so (argument) xem chung co tuong ung voi nhau khong (vd: return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;)

– Sau khi viet xong ham equals, kiem tra lai xem no co tuan thu 5 quy dinh khong.

– Luon viet lai ham hashCode mot khi da viet lai ham equals (quy tac 9)

27

Page 28: Lap trinh java hieu qua

Quy tắc 9: Luôn viết lại hashCode() sau khi thay đổi hàm equals()1. Trong một phiên làm việc nếu hàm hashCode được gọi lại

nhiều lần thì kết quả trả lại đều phải như nhau. Tuy nhiên, nếu đó là 2 phiên làm việc khác nhau thì có thể cho kết quả hashCode khác nhau.

2. Nếu hai đối tượng x, y được tính là bằng nhau theo hàm x.equals(y)==true, thì x.hashCode() == y.hashCode().

3. Ngược lại, nếu x.equals(y) == false, thì hàm hashCode() của x và y không nhất thiết phải khác nhau.

4. Nên biết rằng khi hàm hashCode khác nhau trên mỗi đối tượng khác nhau thì mới tăng hiệu suất của hệ thống.

28

Page 29: Lap trinh java hieu qua

Quy tắc 9: Luôn viết lại hashCode() sau khi thay đổi hàm equals()• Điều 2 « đối tượng bằng nhau theo equals thì hashCode phải bằng

nhau» hay bị vi phạm khi ta viết lại hàm equals. Ví dụ:@Override public boolean equals(Object o) {

if (o == this) return true;if (!(o instanceof PhoneNumber)) return false;PhoneNumber pn = (PhoneNumber)o;return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;

}Map<PhoneNumber, String> m = new

HashMap<PhoneNumber, String>();m.put(new PhoneNumber(707, 867, 5309), "Jenny");

• Chúng ta hi vọng lời gọi m.get(new PhoneNumber(707 , 867 , 5309)) sẽ trả lại giá trị "Jenny", nhưng nó trả lại null => Lý do là vì hàm hashCode cho 2 đối tượng không bằng nhau.

29

Page 30: Lap trinh java hieu qua

Quy tắc 9: Luôn viết lại hashCode() sau khi thay đổi hàm equals()• Cach giai quyet don gian nhat la viet lai ham

hashCode nhu sau:@Override public int hashCode() {

return 42; }

• Cach lam nhu tren co on thoa khong?

30

Page 31: Lap trinh java hieu qua

Quy tắc 9: Luôn viết lại hashCode() sau khi thay đổi hàm equals()• Cach giai quyet don gian nhat la viet lai ham hashCode nhu

sau:@Override public int hashCode() { return 42; }

• Cach lam nhu tren co on thoa khong? • Khong, vi nhu vay tat ca cac doi tuong se co cung gia tri

hashCode. Khi do bang bam se phai su dung danh sach lien ket (linkedList) de tim kiem doi tuong va do do lam giam hieu suat tim kiem.

• Giai phap hay nhat la xay dung hashCode sao cho hai doi tuong khong bang nhau se ko co chung gia tri hashCode => Dieu nay kho, phai lam vai cai luan van TS

31

Page 32: Lap trinh java hieu qua

Quy tắc 9: Luôn viết lại hashCode() sau khi thay đổi hàm equals()• Nguyen tac xay dung an toan ham hashCode:

– la chi su dung cac gia tri dung trong ham equals– Can doi giua running time tong the he thong so voi running time cua ham

hashCode. Loi khuyen la khong nen ngai su dung tinh toan phuc tap hashCode.

– Neu viec tinh toan hashCode phuc tap, khong nen tinh lai no nhieu lan cho mot doi tuong, cung nhu chi tinh toan no khi doi tuong duoc tao ra. Vi du:

private volatile int hashCode; // (Xem quy tac 71)@Override public int hashCode() {

int result = hashCode;if (result == 0) { result = 17; result = 31 * result + areaCode;result = 31 * result + prefix; result = 31 * result + lineNumber;}return result; }

32

Page 33: Lap trinh java hieu qua

Quy tắc 10: Nên viết lại toString()• ham toString nen tra lai toan bo thong tin ve doi tuong• viet tai lieu ro rang ve dinh dang du lieu tra ve cua

toString- cung cap ham truy cap den tat ca cac thong tin tra ve boi toString– LTV khong can phai xu ly chuoi String ket xuat thong tin nao do– Kho kiem soat khi dinh dang du lieu tra ve cua toString thay

doi– Vi du, binh thuong lop PhoneNumber.toString se tra lai

"PhoneNumber@14845" tuong ung "ten lop@gia tri hashCode viet duoi dang unsigned hexadecimal". Chung ta co the viet lai ham toString ro rang hon de tra lai gia tri vi du "(0084) 043 - 8738242"

33

Page 34: Lap trinh java hieu qua

Quy tắc 11: Cẩn thận khi viết lại clone

• Định nghĩa: tao ra mot doi tuong moi voi tat ca thuoc tinh giong y chang mot doi tuong khac ma khong can goi den ham khoi tao

• Nguyen tac viet ham clone:– x.clone() != x: Day phai hoan toan la 2 doi tuong doc lap nhau. Chu y: cac doi

tuong mutables co cau truc "deep structure" can duoc sao chep theo phuong phap "deep copy".

– x.clone().getClass() == x.getClass(): chu y super.clone phai duoc khai bao trong ham clone.

– x.clone().equals(x) == true: Dam bao gia tri giong nhauOverride public Stack clone() {

try {Stack result = (Stack) super.clone();result.elements = elements.clone();return result;

} catch (CloneNotSupportedException e) { throw new AssertionError(); } }

34

Page 35: Lap trinh java hieu qua

Quy tắc 12: Thực thi Comparable• Định nghĩa: Lop a thuc thi Comparable va viet lai ham

compareTo(). Khi do Array.sort(dsa) se sap xep danh sach cac doi tuong cua lop a theo tieu chi cua ham compareTo().

• Nguyen tac xay dung ham compareTo()– Doi xung: sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) dung cho moi

x, y– Bac cau: (x.compareTo(y)>0 && y.compareTo(z)>0) keo theo

x.compareTo(z)>0– Vung chac: Neu (x.compareTo(y)==0) thi (x.compareTo(z) ==

y.compareTo(z))– (x.compareTo(y) == 0) == (x.equals(y)): Thong tin 2 doi tuong duoc so

sanh se gan lien voi thong tin duoc xay dung trong ham equals

35

Page 36: Lap trinh java hieu qua

PHẦN 3: LỚP VÀ GIAO DIỆN

36

Page 37: Lap trinh java hieu qua

Quy tắc 13: Hạn chế truy cập lớp và các thành viên bên trong lớp • Mot modul duoc cho la thiet ke tot neu no ẩn duoc cac du lieu va

cac ham xu ly du lieu cua no khoi su truy cap boi cac module khac (ie., tinh dong goi) => de phat trien song song, cai tien doc lap, de phat hien loi, de bao tri, ...

• Uu tien neu co the khai bao lop hoac giao dien la package-private class/interface. Boi vi neu khai bao public, ban bat buoc phai bao tri tinh tuong thich cua no mai mai do no co the duoc su dung o dau do ben ngoai package.

• ngay ca cach viet hang so cho du lieu mang (ie. public static final array) cung khong dam bao noi dung khong bi sua doi: Nen dung ham tra ve doi tuong. Vi du đúng:private static final Thing[] PRIVATE_VALUES = { ... };

public static final List<Thing> VALUES =Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

37

Page 38: Lap trinh java hieu qua

Quy tắc 14: Dùng accessor methods để truy cập các biến bên trong lớp• Tat ca cac truong (fields) cua public classes

phai duoc truy cap thong qua ham tuong ung• Nhu giai thich trong quy tac 13, de no dam

bao tinh bao dong• Tuy nhien co the ngoai le voi cac truong bat

bien immutable. Vi du: public final int hour;

38

Page 39: Lap trinh java hieu qua

Quy tắc 15: immutable vs. mutable

• Định nghĩa?• Bắt đầu qua ví dụ sau

39

Page 40: Lap trinh java hieu qua

Quy tắc 15: Nhận xét gì về đoạn code này?String string = "";

for (int i = 0; i < times; i++) {

string = string.concat("sfsd");

}

40

times

40Running time

Page 41: Lap trinh java hieu qua

41

Quy tắc 15: Có gì khác nhau?

String string;

for (int i: 0 -> input)

{

string = string.concat("sfsd");

}

StringBuilder string;

for (int i: 0 -> input)

{

string.append("sfsd");

}

Page 42: Lap trinh java hieu qua

42

Quy tắc 15: So sánh thời gian thực hiện append

input=10000: time =566 ms input=10000: time =24 ms

input

42Running time

input

42Running time

String StringBuilder

input=5000: time =157 ms input=5000: time =29 ms

Page 43: Lap trinh java hieu qua

43

String

StringBuilder

string = string.concat("sfsd");Tương đương với tạo biến mới:string = new String("sfsdsfsd");

stringBuilder.append("sfsd");Tương đương thay thế giá trị:stringBuilder = "sfsdsfsd";

Quy tắc 15: immutable vs mutable

Page 44: Lap trinh java hieu qua

44

String

StringBuilder

2016 public String concat(String str) { 2017 int otherLen = str.length(); 2018 if (otherLen == 0) { 2019 return this; 2020 } 2021 char buf[] = new char[count + otherLen]; 2022 getChars(0, count, buf, 0); 2023 str.getChars(0, otherLen, buf, count); 2024 return new String(0, count + otherLen, buf); 2025 }

412 public AbstractStringBuilder append(String str) { 413 if (str == null) str = "null"; 414 int len = str.length(); 415 ensureCapacityInternal(count + len); 416 str.getChars(0, len, value, count); 417 count += len; 418 return this; 419 }

Quy tắc 15: immutable vs mutable

Page 45: Lap trinh java hieu qua

45

• Lý do sử dụng lớp bất biến:– An toàn: không thể truy cập vào lớp bất biến để thay đổi giá trị trong nó, ví dụ như dùng

làm khóa trong bảng băm, multithread – Kiểm soát lỗi tốt hơn: vì mỗi đối tượng của lớp chỉ có duy nhất một trạng thái

• Hạn chế:– Cần tạo các đối tượng riêng biệt cho mỗi giá trị khác nhau (ví dụ append của String)

• 5 quy tắc xây dựng lớp bất biến– Không cung cấp hàm thay đổi trạng thái của đối tượng– Lớp không thể được kế thừa– Từ khóa final cho tất cả biến– Từ khóa private cho tất cả biến– Giấu mọi tham chiếu (reference) đến các thành phần có thể bị thay đổi

Quy tắc 15: ưu tiên sử dụng lớp bất biến và hạn chế sử dụng lớp không bất biến

Page 46: Lap trinh java hieu qua

Quy tắc 15: Đổi sang lớp bất biến

46Refactoring for Immutability, Fredrik Berg Kjolstad , Illinois 2010

Page 47: Lap trinh java hieu qua

Quy tắc 16: Ưu tiên dùng composition thay vì inheritance• Lý do: thừa kế phá vỡ tính bao đóng [Snyder86]

– Con phụ thuộc vào lớp cha (không biết rõ lớp cha hoặc lớp cha thay đổi)– Lấy ví dụ xây dựng một lớp kế thừa từ HashSet để đếm số phần tử được

thêm vào:

47

public class InstrumentedHashSet<E> extends HashSet<E> {

private int addCount = 0;@Override public boolean add(E e)

{addCount++;return super.add(e);

}@Override public boolean

addAll(Collection<? extends E> c) {addCount += c.size();return super.addAll(c);

}public int getAddCount() {return

addCount; }}

Test:InstrumentedHashSet<String> s =new InstrumentedHashSet<String>();

s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));

Output: s.getAddCount() = 6 => Tại sao??

Page 48: Lap trinh java hieu qua

Quy tắc 16: Ưu tiên dùng composition thay vì inheritance• Khi nào nên sử dụng inheritance???

– Lớp B là lớp con của A hay B kế thừa của A khi và chỉ khi B thực sự là A (ví dụ chó là một con vật).

– Nếu B không thực sự là A thì tốt nhất dùng composition.

• Trong API có rất nhiều vi phạm điều này– Stack không phải là Vector, vì vậy Stack không nên

là lớp con kế thừa từ Vector– Properties không nên là lớp con của HashTable

48

Page 49: Lap trinh java hieu qua

Quy tắc 16: Ưu tiên dùng composition thay vì inheritance• Sử dụng composition sẽ không bị phụ thuộc vào lớp cha• Dùng phương pháp chuyển tiếp (forwarding) để thay thế

inheritance bằng composition (decoration pattern)

49

public class InstrumentedHashSet<E> implements Set<E> {private Set<E> s;private int addCount = 0;public InstrumentedHashSet(Set<E> s) {this.s = s;}public boolean add(E e) {

addCount++;return s.add(e);

}public boolean addAll(Collection<? extends E> c) {

addCount += c.size();return s.addAll(c);

}public int getAddCount() {return addCount; }

}

Page 50: Lap trinh java hieu qua

Quy tắc 16: Ưu tiên dùng composition thay vì inheritance• Khi nào nên sử dụng composition???

– Nếu B không thực sự là A thì tốt nhất dùng composition.– Nếu B có nhiều hơn 1 cha (vì Java không cho kế thừa từ nhiều cha)

• Ví dụ: – Hợp đồng bán lẻ, hợp đồng bán buôn, hợp đồng cty A, hợp đồng cty B

đều là hợp đồng– Hợp đồng bán lẻ của cty A và hợp đồng bán buôn của cty A đều là hợp

đồng của công ty A nhưng chúng không thể kế thừa cả 2Hợp đồng

Hợp đồng bán lẻ Hợp đồng bán buôn

Hợp đồng bán lẻ cty A Hợp đồng bán buôn cty A

Hợp đồng cty A

Page 51: Lap trinh java hieu qua

Quy tắc 17: Viết tài liệu chi tiết cho việc kế thừa hoặc cấm kế thừa• Tài liệu hướng dẫn được viết ngay trước các

methods có thể được viết đè • Cách tốt nhất để kiểm tra tính an toàn cho các

lớp được thiết kế để có thể kế thừa là viết thử các lớp con

• Hàm khởi tạo constructor không nên chứa các lớp có thể bị viết đè vì chúng sẽ được gọi truớc khi lớp con sinh ra

• ….

51

Page 52: Lap trinh java hieu qua

Quy tắc 18: Ưu tiên sử dụng interface hơn là các lớp abstract• Java chi cho phep ke thua tu 1 lop va thi hanh nhieu

interfaces cung luc.• Interfaces do do duoc cho la ly tuong de dinh nghia cac

mixins. Mixin duoc hieu la mot tuy chon de them cac chuc nang phu vao chuc nang chinh trong mot lop. Vi du nhu Comparable la mot mixin vi no trang bi them cho lop mot cach thuc so sanh giua cac doi tuong. Abstract classes khong lam duoc mixins vi khong the them duoc lop cha vao lop dang ton tai.

• Interfaces cho phep xay dung cau truc khong co thu bac.• Interfaces cho phep cai tien tinh nang manh me hon ma

khong bi le thuoc vao lop ca nhu da noi o quy tac 16.

52

Page 53: Lap trinh java hieu qua

Quy tắc 18: Ưu tiên sử dụng interface hơn là các lớp abstract• Han che: Phai thiet ke Interfaces can than

truoc khi thuc thi.– Khi thay doi hay them vao mot ham, tat ca cac

lop thuc thi interface se bi thay doi.– Voi abstracts thi de dang hon khi them ham cu

the, cac lop dan xuat khong bi anh huong.– Chu y: Java 8 cho phep viet ham mac dinh trong

Interfaces

53

Page 54: Lap trinh java hieu qua

Quy tắc 19: Chỉ dùng interface để định nghĩa kiểu đối tượng• Khong su dung Interfaces ma chi chua cac

static final fields: Goi la constant interfaces.• Vi mac dinh cac truong hoac ham trong

Interfaces la public, nen neu mot lop thuc thi constant interfaces, tat ca cac lop con cua no se bi o nhiem boi constant interfaces vi chung co the khong duoc dung den.

• Giai phap: Su dung kieu Enum de khai bao hang so (Quy tac 30).

54

Page 55: Lap trinh java hieu qua

C++ hỗ trợ con trỏ hàm (function pointers) qua ví dụ sau:•float Cong (float a, float b) { return a+b; }•float Tru (float a, float b) { return a-b; }•float Nhan (float a, float b) { return a*b; }•float Chia (float a, float b) { return a/b; }

Tùy điều kiện mà một hàm sẽ được dùng:void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float)){ float ketqua = pt2Func(a, b); // gọi con trỏ đến hàm tương ứng}

55

Java thì sao ???

Quy tắc 21: Con trỏ hàm trong java

Page 56: Lap trinh java hieu qua

// Sử dụng interface với kiểu genericpublic interface Toantu<T> {

public T toantu(T t1, T t2);}

56

public class Test {private static class Cong implements Toantu<Double> {

@Override public Double toantu(Double a, Double b) {return a + b; } }

public static final Toantu<Double> CONG = new Cong();

public static Double TinhToan(Double a, Double b, Toantu<Double> O) {

return O.toantu(a, b);}

// DEMOpublic static void main(String[] args) {System.out.println(Test.TinhToan(1.0, 2.0, Test.CONG));}

}

Quy tắc 21: Con trỏ hàm trong java

Page 57: Lap trinh java hieu qua

HỎI ĐÁP?

57

Email: [email protected]él: 06 64 74 78 13Fb: VN-INFO

Page 58: Lap trinh java hieu qua

TÀI LIỆU THAM KHẢO

58

Joshua Bloch