Chào mọi người, mình đang tìm hiểu các dạng design pattern, một trong số đó là dạng bridge pattern, mình đọc cũng khá nhiều tài liệu lẫn ví dụ nhưng mình vẫn không hiểu được lợi ích của design patter này. Một trong số các ví dụ mình đọc là http://www.newthinktank.com/2012/10/bridge-design-pattern-tutorial/
và
https://gpcoder.com/4520-huong-dan-java-design-pattern-bridge/#Loi_ich_cua_Bridge_Pattern_la_gi
Mình đọc trên wiki hoặc gpcoder thì có bảo là " Giảm sự phục thuộc giữa abstraction và implementation (loose coupling) : tính kế thừa trong OOP thường gắn chặt abstraction và implementation lúc build chương trình. Bridge Pattern có thể được dùng để cắt đứt sự phụ thuộc này và cho phép chúng ta chọn implementation phù hợp lúc runtime."
Mọi người có thể cho ví dụ cụ thể giúp mình về việc gắn chặt abstraction và implementation lúc build chương trình hay không.
Không hiểu về lợi ích của Bridge design pattern
Thật ra thì pattern này cũng không phải là khó. Ví dụ như thế này: Giả sử bạn có một method được định nghĩa như sau:
fileOperation1(7-zip compressionProgram)
{
....
// Gọi phương thức compress từ đối tượng compressProgram để nén file
compressionProgram.compress();
...
}
với 7-zip là một class được định nghĩa như sau:
class 7-zip{
void compress () { // method dùng để nén file
...
}
}
Bây giờ, giả sử thay vì chỉ có một method fileOperation1, bạn có n method fileOperation và mỗi method này đều gọi method compress() của 7-zip object. Nếu bạn chỉ sử dụng tham số cho các method này như định nghĩa ban đầu (khai báo tham số có kiểu là class 7-zip) nhưng về sau bạn quyết định thay đối không dùng 7-zip mà dùng Winzip hay Winrar chẳng hạn, bạn sẽ phải tìm và thay thế từng tham chiếu đến đối tượng này trong các file liên quan. Đây là một liên kết chặt (tight coupling) và gây ra khó khăn khi cần thay thế đối tượng sử dụng. Trong quá trình thay thế, nếu không cẩn thận, bạn có thể bỏ sót một vài tham chiếu và tạo ra các lỗi runtime.
Để giải quyết tình huống này, chúng ta sử dụng Bridge pattern như sau: thay vì dùng các class cụ thể như là 7-zip hay Winzip, bạn sẽ truyền một interface ICompressible vào các method fileOperation:
fileOperation1(ICompressible compressionInterface)
{
....
// Gọi phương thức compress từ Interface ICompressible để nén file
compressionInterface.compress();
...
}
với ICompressible được định nghĩa như sau:
interface ICompressible {
...
void compress () // method dùng để nén file
...
}
và các class 7-zip hay Winzip sẽ implement interface này:
class 7-zip implement ICompressible {
...
void compress() {
}
...
}
Trong trường hợp này, các class như là 7-zip hay Winzip được gọi là concrete implementor và interface ICompressible được gọi là implementor (tôi chừa phần Abstraction trong sơ đồ UML để bạn tự giải thích vì nó không phải là ý chính trong khái niệm này). Bởi vì bạn không sử dụng trực tiếp các class 7-zip hay Winzip mà chỉ sử dụng interface ICompressible, bạn sẽ truyền tham số cho method fileOperation bằng bất kỳ object nào có implement interface ICompressible lúc runtime mà không cần thay đổi mã nguồn và không cần lo ngại về bất kỳ hiệu ứng phụ nào. Trong lập trình, điều này được gọi là decoupling (phân tách) và có tác dụng giảm bớt sự phụ thuộc của mã nguồn vào các mã cụ thể.
Trong các thư viện .NET và Java, rất dễ thấy các trường hợp sử dụng pattern này. Ví dụ như trong .NET, bạn có hai interface phổ biến là ICollection<T>
và IEnumerable<T>
thường được sử dụng với các tập hợp, danh sách hoặc mảng… Khi viết mã, bạn có thể sử dụng một số method được định nghĩa trong các interface này để thực hiện các thao tác nhất định mà không cần quan tâm đến đối tượng đang sử dụng có kiểu cụ thể nào.
Hy vọng lời giải thích của tôi giúp được cho bạn.