Sử dụng Adapter Pattern Trong Lập Trình Hướng Đối Tượng

Adapter Pattern Samble In Real Life

I/ Giới thiệu.

- Khái niệm Adapter đã xuất hiện từ rất lâu và được sử dụng rất nhiều trong cuộc sống. Thiết bị điện tử của bạn không thể sạc pin bằng điện 220V mà phải thông qua một bộ chuyển, biến điện 220 thành 12V. Bạn có một con chuột với Jack cắm USB nhưng máy đã hết chỗ cắm, người bán tặng bạn thêm 1 bộ chuyển từ USB sang PS2 để giải quyết vấn đề trên…Đó là những ví dụ hay gặp nhất về Adapter, và vì chúng quá thông dụng nên ít ai ngờ đến cái mình đang sử dụng hằng ngày lại là một kiểu adapter nào đó. Từ lâu, khái niệm Adapter đã được 4 đại ca về Design Pattern đưa vào lĩnh vực làm phần mềm và nó tỏ ra có ích không kém. Tôi dám chắc là các bạn ai đều cũng từng sử dụng đến Adapter trong cuộc sống của mình, thế thì tại sao lại không sử dụng nó trong lập trình để giải quyết những vấn đề tương tự:bbpskien: :

Adapter Pattern Samble In Real Life
Adapter Pattern Samble In Real Life

Adapter Pattern Samble In Real Life
Adapter Pattern Samble In Real Life


- Inheritance là một trong 3 nguyên tắc cơ bản của lập trình hướng đối tượng. Khi cần xây dựng một lớp dựa trên một hoặc nhiều lớp hay interface có sẵn bạn chỉ cần khai báo lớp mới kế thừa lớp cũ và có thể viết thêm những method, attribute của mình tùy thích. Tuy nhiên sẽ có lúc tính kế thừa của OOP sẽ không sử dụng được. Thật sự là có nhiều trường hợp như vậy mà tôi đã từng gặp phải. Giả sử bạn cần mở rộng một lớp nhưng lớp đó là lớp seal (không cho kế thừa) thì inheritance chắc chắn không sử dụng được rồi. Một ví dụ khác là khi bạn cần kết hợp nhiều thứ có sẵn để làm ra một lớp mới sử dụng lại những thứ đó. Như các bạn biết C# và Java không hỗ trợ đa kế thừa, chúng ta không thể nào kế thừa từ 2 lớp có sẵn được. Như vậy nếu bạn đã có một lớp để tìm ước số chung lớn nhất của hai số tự nhiên, một lớp để cộng hai phân số, và khi dùng adapter để kết hợp hai thứ có sẵn đó bạn sẽ làm được một lớp có khả năng cộng hai phân số với nhau với kết quả phép cộng là phân số đã tối giản.:bbpnen:

- Như vậy, Adapter Pattern là một design pattern, nó còn được gọi là Wrapper Pattern biến đổi một lớp không thể sử dụng trực tiếp được thành một lớp mới có thể sử dụng. Trong một số trường hợp, Adapter Pattern còn được sử dụng để biến đổi dạng dữ liệu. Để tiết kiệm chỗ trong và giảm tính phức tạp trong database, nhiều giá trị string có thể được lưu thành một string duy nhất với kí tự phân cách đặc biệt, mẫu Adapter sẽ chịu trách nhiệm tách các string đó ra thành array để nơi khác có thể sử dụng. Nhưng nói chung Adapter Pattern được sử dụng đúng nhất khi biến cái không thể thành cái có thể.:bbpraroi:

II/ Sử dụng Adapter Pattern như thế nào?

- Theo tôi nói ở trên, Adapter Pattern thường được dùng để biến cái không thể thành cái có thể, biến lớp không thể sử dụng thành một lớp mới có thể sử dụng. Tuy nhiên, Adapter Pattern còn được dùng để biến những lớp có sẵn thành một thứ khác dễ và tiện sử dụng hơn. Vì vậy có hai cách thiết kế và áp dụng Adapter Pattern trong lập trình, ở một số tài liệu Design Pattern, hai cách này được gọi là Object Adapter Pattern Class Adapter Pattern. Một số trang còn gọi là Inheritance-Way Composition-Way. Nói chung những cách gọi này đều dựa trên cách thiết kế lớp Adapter. Object Adapter Pattern hay Composition-Way là cách sử dụng một đối tượng thuộc về lớp chúng ta muốn biến đổi bên trong lớp Adapter. Trong khi đó, ClassAdapterPattern hay Inheritance-Way là cách kế thừa từ lớp chúng ta muốn biến đổi hoặc mở rộng thêm.

III/ Ví dụ mẫu về Adapter Pattern

- Tôi sẽ lần lượt đưa 2 ví dụ thiết kế lớp về 2 cách này:

A. Object Adapter Pattern (Composition-Way)

- Để kiếm đủ tiền ăn nhậu, massage:bbpxtay:, ngoài giờ làm ở cty tôi nhận làm thêm một ứng dụng quản lý bán hàng, trong đó có hỗ trợ lập biểu đồ thể hiện doanh số các kiểu. Hiện tại tôi đã đạo được phần code vẽ biểu đồ cột của một tên sinh viên học cùng trường. Tiếc thay search mãi thì chỉ tìm được một thư viện vẽ biểu đồ tròn nhưng không có source code: CloseSourceCode.BarChart. Tuy có thói quen đạo code thiên hạ:bbpcuoi3:, nhưng chương trình của tôi đã thiết kế với tính loose coupling rất cao nên đã sửa lại code của tên sinh viên đó, và truy xuất lớp đó qua Interface: IMyChart. Như vậy với cách làm thông thường tôi không thể sử dụng lớp CloseSourceCode.BarChart được vì CloseSourceCode.BarChart không thuộc interface IMyChart. Nhờ tìm hiểu kĩ về Adapter Pattern, tôi biết rằng có thể áp dụng Object Adapter Pattern trong trường hợp này. Tôi sẽ làm một lớp ThoaiChart (Thoại là tên tôi) implement từ interface IMyChart, và bên trong ThoaiChart tôi sẽ sử dụng lớp CloseSourceCode.BarChart để vẽ. Thiết kế lớp như sau:

Object Adapter Pattern

B. Class Adapter Pattern (Inheritance-Way)

- Tôi đã rất mừng khi phát hiện ra rằng chỉ mất chút ít thời gian để làm cái BarChart. Nhưng khi đưa cho khách hàng, họ yêu cầu tôi phải sửa lại cái BarChart, phải cho người dùng thấy được số liệu khi rê chuột lên hình. Đúng là đồ free có khác, không khi nào dùng ngay được. Tôi đành phải thiết kế lại lớp ThoaiChart theo hướng khác. Bây giờ tôi sẽ inherit lớp CloseSourceCode.BarChart và override lại hàm hover chuột của nó. Thiết kế lớp như sau:

Class Adapter Pattern


IV/ So sánh Adapter Pattern với các Design Patterns khác?

- Trong các Design Pattern thuộc nhóm Structural Design Pattern, có rất nhiều mẫu có thiết kế lớp tương tự nhau. Chúng ta nên nắm rõ ý nghĩa và trường hợp sử dụng của chúng để không nhầm lẫn và … để khi em trai em gái nó hỏi còn biết đường trả lời :bbpbuon:. Trong phần so sánh giữa Adapter Pattern với các Design Patterns khác, tôi có nhắc nhiều đến từ interface của lớp (class). Bạn hãy hiểu đó như là kiểu(type) của lớp. Các lớp khác namespace và khác base class (base type) sẽ có interface khác nhau. Một số tác giả Việt Nam dịch interface của lớp là "giao diện của lớp", riêng cá nhân tôi thì thấy dịch như vậy cũng kì. Một điểm nữa là các bạn cần phân biệt interface trong trường hợp này với interface trong chữ GUI, và càng nên phân biệt nó với khái niệm interface trong một số ngôn ngữ lập trình như C#, Java, ...

• Với khả năng biến cái không thể thành có thể, Adapter Pattern làm cho 1 lớp có thể sử dụng được sau khi lớp đó đã được thiết kế xong trong khi Bridge Pattern làm điều đó trước khi lớp này được tạo ra.
Bridge Pattern được nghĩ ra với ý tưởng tách rời những abstraction ra khỏi những implementation, và tạo tính độc lập cao giữa chúng. Trong khi đó, Adapter pattern đươc dùng để làm những lớp không liên quan (không cùng namespace, khác interface) có thể làm việc với nhau.
• Adapter Pattern về ý nghĩa sẽ biến đổi interface của một lớp, Proxy Pattern sẽ không đổi interface của một lớp và Decorator Pattern sẽ kế thừa và mở rộng dựa trên interface đó.
• Adapter Pattern khi dùng sẽ thay đổi interface của một đối tượng có sẵn để biến nó thành thứ có thể dùng được. Decorator Pattern sẽ phát triển thêm trên một đối tượng mà không làm thay đổi interface của nó
• Giống với Adapter Pattern, Façade Pattern có thể kết hợp nhiều xử lý vào một lớp mới đơn giản và gọn hơn , nhưng nó sẽ định nghĩa mới 1 interface trong khi Adapter Pattern tái sử dụng một interface (Trong ví dụ về các biểu đồ ở trên thì interface được tái sử dụng là IMyChart, không hề có một interface mới được tạo ra)


- Các bạn có thể tham khảo thêm những ví dụ code mẫu cho Adapter Pattern ở trang wiki. Ví dụ thứ hai ở trang này là biến đổi 1 lớp DList thành Stack dựa trên những xử lý có sẵn của DList mà không cần viết lại những xử lý phức tạp cho lớp Stack.

Tài liệu tham khảo:

3 comments:

  1. Anonymous Says:

    Em là Phi Hùng nè anh Thoại :D

    Em thấy phần giải thích về tác dụng của Adapter tốt, khá dễ hiểu.

    Hai ví dụ cũng rất trực quan, và thể hiện được "trường hợp cần áp dụng" và "mô hình cấu trúc" của 2 loại Object Adapter và Class Adapter.
    Khi lần đầu đọc thì em không hiểu rõ tại sao cần áp dụng Class Adapter cho ví dụ số 2 nhưng suy nghĩ thêm thì đã hiểu :D

    Phần so sánh thì em chưa hiểu nhiều về các design pattern khác nên đọc thấy tùm lum khó hiểu :D

    Anh Thoại dạo này viết nhiều ghê :D

    À, sao khi đọc về Adapter thì em thấy lợi ích của việc sử dụng Adapter là chuyển thành "type" của class (hay interface của class) mà mình cần, để có thể sử dụng lại Adapter với type "expected" trong chương trình của mình. Chứ phần quan trọng ko phải là gọi các phương thức từ các class có sẵn. Vì nếu chỉ cần gọi các phương thức trong các class có sẵn để dùng thì cứ tạo 1 class static rồi gọi các phương thức cần thôi chứ ko cần sử dụng IMyChart interface.
    Em hiểu như vậy có đúng ko :D?

  2. nin9 Says:

    Em thấy ví dụ về "Display AddressBook to a JTable without having AddressBook implement the TableModel" tại 1 trong các link tham khảo "http://developerlife.com/tutorials/?p=19" còn nói dc 1 ý là ko chỉ với các component đóng (minh ko có source), mà còn với các component mở nhưng mình muốn tách biệt giữa "implementation" với việc "sử dụng" component đó.

  3. Nguyễn Thoại Says:

    Đúng roài iem, anh nghĩ khi cần dùng lại với biến đổi đôi chút thì áp dụng Adapter được rồi.

rss
 

About Me

Place I've live
Near Bossley Park, Sydney, NSW, Australia
Place I've work
  • Freelancer (from 06/2010 to present)
  • Harvey Nash (from 05/2008 to 06/2010)
  • DataDesign Vietnam (10/2005 to 04/2008)
Place I've studied
  • University of Natural Science (Bachelor of Science HoChiMinh City Vietnam From 2001 to 2005)
  • Le Hong Phong High School (HoChiMinh City Vietnam From 1997 to 2000)