55
Nguyên lý thiết kế hướng đối tượng Lập trình hướng đối tượng

Nguyên lý thiết kế hướng đối tượng

  • Upload
    miach

  • View
    82

  • Download
    0

Embed Size (px)

DESCRIPTION

Nguyên lý thiết kế hướng đối tượng. Lập trình hướng đối tượng. Nguyên tắc số 1. Giảm thiểu khả năng truy nhập tới lớp và các thành viên. "Trừu tượng hóa" nghĩa là gì?. - PowerPoint PPT Presentation

Citation preview

Page 1: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng

Lập trình hướng đối tượng

Page 2: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 2 Bob Tarr

Nguyên tắc số 1

Giảm thiểu khả năng truy nhập tới lớp và các thành viên

Page 3: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 3 Bob Tarr

"Trừu tượng hóa" nghĩa là gì? Tony Hoare: “trừu tượng hóa xuất phát từ một cách nhìn nhận

những đặc điểm tương đồng giữa một số đối tượng, tình thế, hoặc quy trình nhất định trong thế giới thực, và quyết định tập trung vào những điểm tương đồng này và nhất thời lờ đi các điểm khác biệt.”

Grady Booch: “Một trừu tượng hóa kí hiệu các đặc điểm cốt lõi của một đối tượng mà các đặc điểm này phân biệt nó với tất cả các loại đối tượng khác, cho ta các ranh giới được xác định rõ ràng. Tất cả được xét một cách tương đối trong góc nhìn của người quan sát.”

Trừu tượng hóa là một trong những phương pháp nền tảng để đối phó với sự phức tạp

Một trừu tượng hóa tập trung vào hình ảnh bên ngoài của một đối tượng và tách hành vi của đối tượng đó ra khỏi cài đặt của nó.

Page 4: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 4 Bob Tarr

Đóng gói - encapsulation

Grady Booch: "Đóng gói là để chia tách giữa giao diện cam kết của một trừu tượng hóa và cài đặt của nó.

Craig Larman: "Đóng gói là một cơ chế được dùng để che dữ liệu, cấu trúc bên trong, và chi tiết cài đặt của một đối tượng. Mọi tương tác với đối tượng được thực hiện qua một giao diện công khai của các thao tác"

Các lớp đối tượng không nên để mở các chi tiết cài đặt nội bộ của mình

Page 5: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 5 Bob Tarr

Che dấu thông tin ở Java Sử dụng các thành viên private và các hàm đọc (get) và ghi (set)

mỗi khi có thể. Ví dụ:

Thay thếpublic double speed;

bằngprivate double speed;

public double getSpeed() {

return speed;

}

public double setSpeed(double newSpeed) {

speed = …..

}

Page 6: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 6 Bob Tarr

Che dấu thông tin ở Java Ta có thể quy định các ràng buộc về giá trị

public void setSpeed(double newSpeed) {

if (newSpeed < 0) {

sendErrorMessage(...);

newSpeed = Math.abs(newSpeed);

}

speed = newSpeed;

} Nếu các client được truy cập trực tiếp đến thành viên dữ liệu thì

từng client phải chịu trách nhiệm kiểm tra ràng buộc

Page 7: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 7 Bob Tarr

Che dấu thông tin ở Java

Ta có thể thay đổi biểu diễn dữ liệu bên trong lớp đối tượng mà không phải sửa giao diện

// Now using metric units (kph, not mph)

public void setSpeedInMPH(double newSpeed) {

speedInKPH = convert(newSpeed);

}

public void setSpeedInKPH(double newSpeed) {

speedInKPH = newSpeed;

}

Page 8: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 8 Bob Tarr

Che dấu thông tin ở Java

Ta có thể thực hiện các hiệu ứng phụ tùy ýpublic void setSpeed(double newSpeed) {

speed = newSpeed;

notifyObservers();

}

Nếu các client của một lớp truy nhập trực tiếp dữ liệu của mình, mỗi client sẽ phải chịu trách nhiệm chạy hiệu ứng phụ

Page 9: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 9 Bob Tarr

Nguyên tắc số 2

Ưu tiên sử dụng Composition hơn Inheritance

Page 10: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 10 Bob Tarr

Composition Phương pháp tái sử dụng mà trong đó chức năng mới được xây

dựng bằng cách tạo một đối tượng có thành phần là các đối tượng khác

Chức năng mới được tạo bằng cách sử dụng chức năng của một trong các đối tượng thành phần

Composition có thể là chứa Tham chiếu Giá trị

C++ cho phép chứa giá trị đối tượng hoặc chứa tham chiếu đối tượng

Java chỉ cho phép chứa tham chiếu đối tượng.

Page 11: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 11 Bob Tarr

Ưu/nhược điểm của Composition

Ưu điểm: Lớp chứa chỉ có thể truy nhập tới các đối tượng thành phần qua

giao diện của các đối tượng đó. Tái sử dụng kiểu "hộp đen", do chi tiết cài đặt của các đối tượng

thành phần không lộ ra ngoài Tính đóng gói cao Ít phụ thuộc về cài đặt hơn Mỗi lớp chỉ chú trọng vào một tác vụ Quan hệ composition có thể được xác định một cách động trong

thời gian chạy qua việc đối tượng nhận tham chiếu tới các đối tượng khác

Page 12: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 12 Bob Tarr

Ưu/nhược điểm của Composition

Nhược điểm Kết quả là hệ thống có xu hướng chứa

nhiều đối tượng hơn Các giao diện phải được định nghĩa cẩn

thận để sử dụng nhiều đối tượng khác nhau trong vai trò các khối cấu thành

Page 13: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 13 Bob Tarr

Thừa kế

Phương pháp tái sử dụng mà trong đó chức năng mới được xây dựng bằng cách mở rộng cài đặt của một đối tượng có sẵn

Lớp tổng quát (superclass) liệt kê một cách tường minh các thuộc tính và phương thức chung

Lớp chuyên hóa (subclass) mở rộng cài đặt với các thuộc tính và phương thức bổ sung

Page 14: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 14 Bob Tarr

Ưu nhược điểm của thừa kế

Ưu điểm: Dễ dàng cài lớp mới, do phần lớn đã được thừa kế Dễ sửa hoặc mở rộng cài đặt được tái sử dụng

Nhược điểm Phá vỡ tính đóng gói, do nó để cho lớp con biết về chi tiết cài đặt

của lớp cha Tái sử dụng kiểu "hộp trắng"

Có thể phải sửa lớp con nếu cài đặt của lớp cha có thay đổi. Tại thời gian chạy, không thể thay đổi cài đặt đã được thừa kế từ

các lớp cha

Page 15: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 15 Bob Tarr

Ví dụ Inheritance & Compostion

Ví dụ lấy từ cuốn Effective Java của Joshua Bloch.

Ta cần một dạng HashSet (tập hợp được cài bằng bảng băm) có chức năng lưu lại số lần chèn thêm phần tử. Ta tạo lớp con của HashSet:

public class InstrumentedHashSet extends HashSet {

// The number of attempted element insertions

private int addCount = 0;

public InstrumentedHashSet(Collection c) {super(c);}

public InstrumentedHashSet(int initCap, float loadFactor) {

super(initCap, loadFactor);

}

Page 16: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 16 Bob Tarr

public class InstrumentedHashSet extends HashSet { // The number of attempted element insertions private int addCount = 0;

public InstrumentedHashSet(Collection c) {super(c);} public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } public boolean add(Object o) { addCount++; return super.add(o); } public boolean addAll(Collection c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; }}

public static void main(String[] args) { InstrumentedHashSet s = new InstrumentedHashSet(); s.addAll(Arrays.asList(new String[] {"Snap","Crackle","Pop"})); System.out.println(s.getAddCount());}

Page 17: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 17 Bob Tarr

Ví dụ Inheritance & Compostion

Kết quả là 6 thay vì 3 như trông đợi. Tại sao? Cài đặt bên trong của addAll() trong lớp cha

HashSet gọi phương thức add() Tại add() của InstrumentedHashSet, ta cộng 3 vào

addCount Gọi addAll() của HashSet, với mỗi phần tử, phương

thức addAll() này lại gọi add() – bản định nghĩa lại của IntrumentedHashSet.

Kết quả: mỗi phần tử bổ sung được đếm 2 lần

Page 18: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 18 Bob Tarr

Ví dụ Inheritance & Compostion

Có vài cách sửa, nhưng hãy ghi nhận điểm yếu của lớp con IntrumentedHashSet: chi tiết cài đặt của lớp cha ảnh hưởng tới hoạt động của lớp con

Cách sửa tốt nhất: sử dụng composition. Viết lớp IntrumentedSet chứa một đối tượng Set Lớp này lặp lại interface Set, nhưng tất cả các thao tác tập hợp

sẽ được chuyển tới cho đối tượng Set chứa trong IntrumentedSet

IntrumentedSet được gọi là một lớp bọc ngoài (wrapper class), nó bọc ra ngoài một đối tượng Set

Đây là ví dụ về đại diện ủy quyền qua việc sử dụng composition

Page 19: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 19 Bob Tarr

public class InstrumentedSet implements Set { private final Set s; private int addCount = 0; public InstrumentedSet(Set s) {this.s = s;} public boolean add(Object o) { addCount++; return s.add(o); } public boolean addAll(Collection c) { addCount += c.size(); return s.addAll(c); } public int getAddCount() {return addCount;}

// Forwarding methods (the rest of the Set interface methods) public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator iterator() { return s.iterator(); } public boolean remove(Object o) { return s.remove(o); } public Object[] toArray() { return s.toArray(); } public boolean equals(Object o) { return s.equals(o); } public String toString() { return s.toString(); }

Page 20: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 20 Bob Tarr

Ví dụ Inheritance & Compostion

Một vài điểm cần lưu ý về InstrumentedSet : Lớp này là một Set Có một constructor có tham số là một Set Đối tượng Set nằm trong lớp có thể là một đối tượng thuộc bất

cứ lớp nào cài đặt interface Set (có thể không phải HashSet) Lớp này rất linh động và có thể bọc ra ngoài một đối tượng Set

bất kì

Ví dụ:

int capacity = 7;float loadFactor = .66f;Set s2 = new InstrumentedSet(new HashSet(capacity, loadFactor));

List list = new ArrayList();Set s1 = new InstrumentedSet(new TreeSet(list));

Page 21: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 21 Bob Tarr

Quy tắc Coad

Chỉ sử dụng thừa kế khi tất cả các tiêu chí sau đều được thỏa mãn:

Lớp con "là một loại đặc biệt" chứ không phải "là một vai trò" của lớp cha.

Đối tượng của lớp con không bao giờ cần trở thành một đối tượng của một lớp khác

Lớp con mở rộng, chứ không định nghĩa lại hoặc xóa bỏ, các trách nhiệm của lớp cha

Lớp con không mở rộng khả năng của một lớp chỉ là lớp tiện ích Đối với một lớp trong ngữ cảnh thực của bài toán, lớp con chuyên

biệt hóa một vai trò, giao tác, hoặc thiết bị.

Page 22: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 22 Bob Tarr

Ví dụ 1

Page 23: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 23 Bob Tarr

Ví dụ 1 "Là một loại đặc biệt" chứ

không phải "là một vai trò" của lớp cha. Sai. Passenger hay Agent đều là

các vai trò mà một người có thể giữ Không biến đổi

Sai. Một người có thể lúc này là một passenger, lúc khác lại là agent Mở rộng chứ không định nghĩa lại hoặc xóa bỏ

Đúng Không mở rộng một lớp tiện ích

Đúng Trong ngữ cảnh bài toán, chuyên biệt hóa một vai trò, giao tác, hoặc

thiết bị. Sai. Một Person không phải là một vai trò, giao tác, hay thiết bị

Page 24: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 24 Bob Tarr

Ví dụ 1 - Composition

Page 25: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 25 Bob Tarr

Ví dụ 2. Inheritance/Composition

Page 26: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 26 Bob Tarr

Ví dụ 1 "Là một loại đặc biệt", không phải

"là một vai trò", của lớp cha Đúng. Passenger và Agent là

các dạng đặc biệt của PersonRole Không biến đổi

Đúng. Một đối tượng passenger sẽ luôn là passenger, agent cũng vậy Mở rộng chứ không định nghĩa lại hoặc xóa bỏ

Đúng Không mở rộng một lớp tiện ích

Đúng Trong ngữ cảnh bài toán, chuyên biệt hóa một vai trò, giao tác,

hoặc thiết bị. Đúng. Mỗi PersonRole là một vai trò

Page 27: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 27 Bob Tarr

Ví dụ 3

Page 28: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 28 Bob Tarr

Ví dụ 3 "Là một loại đặc biệt", không phải

"là một vai trò", của lớp cha Đúng. Reservation và Purchase là các dạng giao tác (transaction)

Không biến đổi Đúng. Một đối tượng Reservation sẽ luôn là Reservation, Purchase

cũng vậy Mở rộng chứ không định nghĩa lại hoặc xóa bỏ

Đúng Không mở rộng một lớp tiện ích

Đúng Trong ngữ cảnh, chuyên biệt hóa một vai trò, giao tác, hoặc thiết bị.

Đúng. Đây là giao tác

Page 29: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 29 Bob Tarr

Tóm tắt Inheritance/Composition

composition và inheritance là các phương pháp tái sử dụng quan trọng

Có thể làm cho các thiết kế phần mềm đơn giản hơn và có khả năng tái sử dụng cao hơn bằng cách ưu tiên dùng composition

Có thể dùng inheritance để mở rộng tập hợp các lớp có thể dùng làm thành phần cho composition

Do đó, composition và inheritance có tính chất tương hỗ Nhưng nguyên tắc căn bản là:

Ưu tiên sử dụng Composition hơn Inheritance

Page 30: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 30 Bob Tarr

Nguyên tắc số 3

Lập trình theo một giao diện, không theo một cài đặt

Page 31: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 31 Bob Tarr

Interface Một interface của một đối tượng là một tập các phương thức của

đối tượng đó mà các đối tượng khác biết rằng chúng có thể kích hoạt. Các đối tượng chỉ biết về nhau qua interface

Một đối tượng có thể có nhiều interface về bản chất, mỗi interface là một tập con của tập tất cả các phương

thức mà một đối tượng có cài Một kiểu (type) là một interface cụ thể Các đối tượng thuộc các lớp khác nhau có thể thuộc cùng một kiểu,

và một đối tượng có thể có nhiều kiểu khác nhau Interface là chìa khóa cho khả năng ghép nối (plugability)!

Page 32: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 32 Bob Tarr

Ưu/nhược điểm của interface Ưu điểm:

Client không biết về lớp cụ thể của đối tượng mà mình đang dùng Có thể dễ dàng thay thế đối tượng này vào chỗ của đối tượng khác Quan hệ giữa các đối tượng không cần phải được mã cứng cho một

lớp cụ thể, từ đó tăng tính linh hoạt Giảm phụ thuộc lẫn nhau (coupling) giữa các thành phần hệ thống Tăng khả năng tái sử dụng Tăng cơ hội sử dụng composition do các đối tượng thành phần có thể

thuộc bất cứ lớp nào cài đặt một interface cụ thể Nhược điểm

Làm tăng nhẹ độ phức tạp của thiết kế

Page 33: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 33 Bob Tarr

Ví dụ về interface "interface"Expression

+ asString(): String+ evaluate(): int

"interface"BinaryExpression

+ left(): Expression+ right(): Expression

Numeral

- int: value

+ Numeral(int)+ Numeral() Square

- Expression: expression

+ Square(Expression)

Addition

- Expression: left- Expression: right

+ Addition(Expression, Expression)

Page 34: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 34 Bob Tarr

Ví dụ interface Lớp Addition có thể chứa các số hạng trái và phải mà không cần

quan tâm chúng thực ra là đối tượng thuộc các lớp nào (Square, Numeral, Addition,…) hoặc nằm trong cây phân cấp thừa kế nào

class Addition implements BinaryExpression {

private Expression _left;

private Expression _right;

Addition (Expression l, Expression r)

{ _left = l; _right = r; }

public int evaluate()

{ return (_left.evaluate() + _right.evaluate() );}

}

Page 35: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 35 Bob Tarr

Nguyên tắc số 4

Nguyên tắc Mở-Đóng:Các thực thể phần mềm nên

mở đối với việc mở rộng, nhưng đóng đối với việc sửa đổi

Page 36: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 36 Bob Tarr

Nguyên tắc Mở-đóng (OCP) Phát biểu: ta nên cố gắng thiết kế các mô-đun mà không bao giờ cần sửa Để mở rộng hành vi của hệ thống, ta bổ sung các đoạn trình mới, ta không

sửa mã cũ. Các mô-đun thỏa mãn OCP cần đạt được 2 tiêu chí:

Mở đối với mở rộng: mở rộng hành vi của mô-đun để thỏa mãn yêu cầu mới Đóng đối với sửa đổi: không được sửa mã nguồn của mô-đun

Làm thế nào để thực hiện được nguyên tắc này? Trừu tượng hóa Đa hình Thừa kế Interface

Page 37: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 37 Bob Tarr

Nguyên tắc Mở-đóng

Không thể làm cho tất cả các mô-đun trong hệ thống phần mềm đều thỏa mãn OCP, nhưng ta nên cố giảm thiểu số mô-đun không thỏa mãn OCP

Nguyên tắc Mở-đóng thực sự là trái tim của thiết kế hướng đối tượng

Việc tuân theo nguyên tắc này đem lại mức cao nhất về tính tái sử dụng và khả năng bảo trì

Page 38: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 38 Bob Tarr

Ví dụ Xét phương thức tính tổng giá tiền của một loạt phụ tùng:

public double totalPrice(Part[] parts) {

double total = 0.0;

for (int i=0; i<parts.length; i++) {

total += parts[i].getPrice();

}

return total;

} Nếu Part là lớp cơ sở hoặc một interface và đang sử dụng đa hình,

thì phương thức trên dễ dàng đáp ứng các loại phụ tùng mới mà không phải sửa Thỏa mãn nguyên tắc Mở-đóng

Page 39: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 39 Bob Tarr

Ví dụ (tiếp) Nếu có thêm chính sách giá là: các phụ tùng bo mạch chủ và bộ nhớ phải

có giá sàn khi tính tổng giá. Chẳng hạn:

public double totalPrice(Part[] parts) {

double total = 0.0;

for (int i=0; i<parts.length; i++) {

if (parts[i] instanceof Motherboard)

total += (1.45 * parts[i].getPrice());

else if (parts[i] instanceof Memory)

total += (1.27 * parts[i].getPrice());

else

total += parts[i].getPrice();

}

return total;

}

Không thỏa mãn OCP!

Mỗi lần chính sách giá thay đổi là lại phải sửa nội dung totalPrice()

Không đóng đối với sửa đổi

Page 40: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 40 Bob Tarr

Ví dụ

public double totalPrice(Part[] parts) {

double total = 0.0;

for (int i=0; i<parts.length; i++) {

total += parts[i].getPrice();

}

return total;

}

Để dùng phiên bản totalPrice() đầu tiên, ta có thể kết hợp chính sách giá vào phương thức getPrice của một lớp Part

Page 41: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 41 Bob Tarr

Ví dụ (tiếp) Ví dụ về các lớp Part và ConcretePart

// Class Part is the superclass for all parts.public class Part { private double price; public Part(double price) (this.price = price;} public void setPrice(double price) {this.price = price;} public double getPrice() {return price;}}

// Class ConcretePart implements a part for sale.// Pricing policy explicit here!public class ConcretePart extends Part { public double getPrice() { // return (1.45 * price); //Premium return (0.90 * price); //Labor Day Sale }}

Nhưng giờ phải sửa từng lớp con của Part mỗi khi có thay đổi về chính sách giá.

Page 42: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 42 Bob Tarr

Ví dụ (tiếp) Cách tốt hơn là tạo lớp Price Policy với nhiệm vụ cung cấp nhiều

chính sách giá

// The Part class now has a contained PricePolicy object.public class Part { private double price; private PricePolicy pricePolicy;

public void setPricePolicy(PricePolicy pricePolicy) { this.pricePolicy = pricePolicy; } public void setPrice(double price) {this.price = price;}

public double getPrice() { return pricePolicy.getPrice(price); }}

Page 43: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 43 Bob Tarr

Ví dụ (tiếp)

Như vậy, ta có thể quy định các chính sách giá trong thời gian chạy bằng cách thay đổi đối tượng PricePolicy mà một đối tượng Part dùng đến

Trong một ứng dụng thực, cả giá của một Part và PricePolicy tương ứng đều có thể được nạp từ một cơ sở dữ liệu

/*** Class PricePolicy implements a given price policy.*/public class PricePolicy { private double factor; public PricePolicy (double factor) { this.factor = factor; } public double getPrice(double price) { return price * factor; }}

Page 44: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 44 Bob Tarr

Nguyên tắc lựa chọn duy nhất

hệ quả của nguyên tắc Mở-đóng

Khi một hệ thống phần mềm cần hỗ trợ một tập các lựa chọn,

về lí tưởng, trong hệ thống chỉ có một lớp biết toàn bộ tập lựa chọn này

Page 45: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 45 Bob Tarr

Nguyên tắc số 5

Nguyên tắc thế Liskov:

Hàm nào dùng tham chiếu tới lớp cơ sở thì phải có khả năng dùng nó

cho đối tượng thuộc lớp dẫn xuất mà không cần biết đến việc này

Page 46: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 46 Bob Tarr

Nguyên tắc thế Liskov Nguyên tắc này có vẻ hiển nhiên đối với những gì ta biết về đa hình Ví dụ

public void drawShape (Shape s) {

//code here

} Phương thức drawShape cần chạy được với lớp dẫn xuất bất kì

của lớp cơ sở Shape (hoặc, nếu Shape là một interface, phương thức cần chạy với bất cứ lớp nào cài interface Shape)

Khi cài đặt các lớp dẫn xuất, cần cẩn trọng để đảm bảo không vô tình vi phạm nguyên tắc Liskov

Page 47: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 47 Bob Tarr

Nguyên tắc thế Liskov

Nếu một hàm không thỏa mãn nguyên tắc Liskov, có thể nó đã tham chiếu tường minh đến một vài hoặc tất cả các lớp con của lớp cơ sở

Hàm đó cũng vi phạm nguyên tắc Mở-đóng vì nó sẽ phải bị sửa đổi khi có một lớp con mới được tạo ra.

Page 48: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 48 Bob Tarr

// A very nice Rectangle class.public class Rectangle { private double width; private double height; public Rectangle(double w, double h) { width = w; height = h; } public double getWidth() {return width;} public double getHeight() {return height;} public void setWidth(double w) {width = w;} public void setHeight(double h) {height = h;} public double area() {return (width * height);}

Lớp Square (hình vuông) thì sao?

Hình vuông cũng là một hình chữ nhật.

Ví dụ

Page 49: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 49 Bob Tarr

Ví dụ (tiếp)

Square là lớp con của Rectangle?Không cần cả width và height

Hơi phí bộ nhớ nhưng không quan trọng lắm

Các phương thức setWidth() và setHeight() phải được định nghĩa lại.

Phải định nghĩa lại cả những phương thức đơn giản nhất??

Có vẻ thừa kế ở đây không thích hợp lắm

Page 50: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 50 Bob Tarr

// A Square class.public class Square extends Rectangle {

public Square(double s) {super(s, s);}

public void setWidth(double w) { super.setWidth(w); super.setHeight(w); }

public void setHeight(double h) { super.setHeight(h); super.setWidth(h); }}

Ví dụ (tiếp)

Trông có vẻ ổn?

Page 51: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 51 Bob Tarr

public class TestRectangle { // Define a method that takes a Rectangle reference. public static void testLSP(Rectangle r) { r.setWidth(4.0); r.setHeight(5.0); System.out.println("Width is 4.0 and Height is 5.0" + ", so Area is " + r.area()); if (r.area() == 20.0) System.out.println("Looking good!\n"); else System.out.println ("Huh?? What kind of rectangle is this??\n"); } public static void main(String args[]) { //Create a Rectangle and a Square Rectangle r = new Rectangle(1.0, 1.0); Square s = new Square(1.0); // Now call the method above. testLSP(r); testLSP(s); }}

Theo nguyên tắc thế Liskov, testLSP phải chạy ổn cho cả Rectangle và Square. Thực tế thì sao?

Page 52: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 52 Bob Tarr

Ví dụ (tiếp)

Output:

Nguyên tắc Liskov đã bị vi phạm!

Width is 4.0 and Height is 5.0, so Area is 20.0Looking good!

Width is 4.0 and Height is 5.0, so Area is 25.0Huh?? What kind of rectangle is this??

Page 53: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 53 Bob Tarr

Ví dụ (tiếp) Vấn đề ở đây là gì? Người viết testLSP() đã dùng giả thiết hợp lý

rằng việc sửa chiều rộng hình chữ nhật không làm thay đổi chiều dài hình

Truyền một đối tượng Square cho một phương thức như vậy đã gây vấn đề, làm lộ diện một vi phạm đối với nguyên tắc LSP

Các lớp Rectangle và Square trông có vẻ nhất quán và hợp lệ. Nhưng một giả thiết hợp lý của lập trình viên lại làm mô hình thiết kế bị đổ vỡ

Không thể nhìn các giải pháp thiết kế một cách riêng rẽ, phải xem xét chúng trong các giả thiết hợp lý mà người sử dụng thiết kế có thể đưa ra.

Page 54: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 54 Bob Tarr

Ví dụ (tiếp)

Trong ngữ cảnh toán học, một hình vuông là một hình chữ nhật. Nhưng một đối tượng Square không phải là một đối tượng Rectangle vì hành vi của một đối tượng Square không nhất quán với hành vi của một đối tượng Rectangle.

Về mặt hành vi, hình vuông không phải là hình chữ nhật. Không có tính đa hình giữa Square và Rectangle.

Page 55: Nguyên lý thiết kế hướng đối tượng

Nguyên lý thiết kế hướng đối tượng 55 Bob Tarr

Nguyên tắc thế Liskov

Nguyên tắc thế Liskov (LSP) nêu bật rằng quan hệ ISA (là) hoàn toàn là về hành vi

Để nguyên tắc này được thỏa mãn (cùng với nó là nguyên lý Mở-đóng) tất cả các lớp con phải tuân theo hành vi mà client trông đợi ở lớp cha. Một kiểu con không được có nhiều ràng buộc hơn kiểu cơ bản,

nếu không, sẽ có trường hợp sử dụng mà lớp cha dùng được nhưng lớp con lại không dùng được.

Điều kiện đảm bảo nguyên tắc Liskov: bất cứ đâu dùng được lớp cha thì cũng dùng được lớp con.