Adapter Pattern trong Java

0 0 0

Người đăng: Ông Huy Thắng

Theo Viblo Asia

Adapter Pattern là gì?

Adapter cho phép giao tiếp giữa 2 interface không tương thích bằng cách bao bọc (wrap) một lớp hiện tại bằng một lớp adapter để nó có thể sử dụng interface mong muốn.

Ví dụ minh họa:

Giả sử bạn có một interface MediaPlayer chỉ phát nhạc .mp3, nhưng bạn lại muốn phát .mp4 hoặc .vlc, bạn có thể tạo một MediaAdapter để chuyển đổi định dạng đó.

Cấu trúc Adapter Pattern

Client -> Target Interface <- Adapter -> Adaptee

  • Target: Interface mà client mong đợi.
  • Adaptee: Lớp có sẵn nhưng interface không tương thích.
  • Adapter: Cầu nối giúp Adaptee tương thích với Target.

Ví dụ Java đơn giản:

// Target
interface MediaPlayer { void play(String audioType, String fileName);
} // Adaptee
class AdvancedMediaPlayer { void playVlc(String fileName) { System.out.println("Playing vlc: " + fileName); }
} // Adapter
class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer = new AdvancedMediaPlayer(); public void play(String audioType, String fileName) { if(audioType.equalsIgnoreCase("vlc")) { advancedMusicPlayer.playVlc(fileName); } }
}

Ưu điểm:

  • Tái sử dụng mã nguồn có sẵn.
  • Tách biệt logic tích hợp khỏi logic xử lý chính.
  • Tăng tính mở rộng (open for extension).

Nhược điểm:

  • Gây thêm một lớp trừu tượng.
  • Có thể phức tạp nếu adapter phải xử lý nhiều loại chuyển đổi khác nhau.

Ứng dụng thực tế trong Spring Boot

Trường hợp sử dụng phổ biến:

  • Tích hợp hệ thống cũ (legacy system) vào hệ thống mới.
  • Chuyển đổi DTO/Entity khác nhau giữa các microservices.
  • Bọc API bên thứ ba (3rd-party) để dùng theo chuẩn riêng.
  • Chuyển đổi dữ liệu giữa các tầng: ví dụ adapter giữa DAO và service layer.

Tình huống thực tế:

  • Bạn có một hệ thống Spring Boot nhận yêu cầu tìm kiếm thông tin khách hàng.
  • Bạn phải gọi tới một API bên ngoài (vd: http://third-party.com/api/customer?id=...) để lấy thông tin khách hàng.
  • API bên ngoài trả về định dạng không khớp với DTO nội bộ.

Định nghĩa cấu trúc dữ liệu

Giả sử response từ API bên ngoài:
{ "full_name": "Nguyen Van A", "phone_number": "0123456789", "email_address": "nva@example.com"
}

DTO nội bộ mong muốn:

public class CustomerInfo { private String name; private String phone; private String email;
}

Tạo lớp Adaptee (dữ liệu từ bên ngoài)

Tạo lớp Adapter:

public class ExternalCustomerResponse { @JsonProperty("full_name") private String fullName; @JsonProperty("phone_number") private String phoneNumber; @JsonProperty("email_address") private String emailAddress; // getters, setters
}

Tạo lớp Adapter

// Adapter
public class CustomerAdapter { public static CustomerInfo convert(ExternalCustomerResponse external) { CustomerInfo info = new CustomerInfo(); info.setName(external.getFullName()); info.setPhone(external.getPhoneNumber()); info.setEmail(external.getEmailAddress()); return info; }
}

Gọi API bên ngoài và sử dụng Adapter

@Service
public class CustomerService { private final RestTemplate restTemplate; public CustomerService(RestTemplateBuilder builder) { this.restTemplate = builder.build(); } public CustomerInfo getCustomerById(String id) { String url = "http://third-party.com/api/customer?id=" + id; ExternalCustomerResponse response = restTemplate.getForObject(url, ExternalCustomerResponse.class); return CustomerAdapter.convert(response); // Adapter dùng ở đây }
}

Controller sử dụng CustomerInfo nội bộ

@RestController
@RequestMapping("/customers")
public class CustomerController { private final CustomerService customerService; public CustomerController(CustomerService service) { this.customerService = service; } @GetMapping("/{id}") public ResponseEntity<CustomerInfo> getCustomer(@PathVariable String id) { return ResponseEntity.ok(customerService.getCustomerById(id)); }
}

Có thể sử dụng ModelMapper, hoặc những thư viện khác hỗ trợ việc mapping. Còn nếu các thuộc tính trùng nhau về tên thì có thể sử dụng BeanUtils.copyProperties của Spring

https://www.baeldung.com/java-performance-mapping-frameworks

Tìm hiểu thêm và đưa ra quyết định link phía trên nhé.

So sánh hiệu năng (thời gian trung bình để map 1 object)

Thư viện Hiệu năng (thấp hơn là tốt hơn)
MapStruct Tốt nhất
JMapper Rất tốt
Orika Trung bình
ModelMapper Chậm
Dozer Rất chậm

MapStruct vượt trội về hiệu năng vì nó generate code tại compile-time, không dùng reflection.

Đặc điểm từng thư viện

  • MapStruct: Hiệu năng cao, compile-time check, cần generate code nhưng rất phù hợp cho dự án lớn.
  • ModelMapper: Dễ dùng, mapping tự động, nhưng chậm hơn do dùng reflection.
  • Orika: Hỗ trợ mapping phức tạp, hiệu năng ổn định.
  • Dozer: Dễ dùng nhưng hiệu năng thấp, ít được cập nhật.
  • JMapper: Hiệu năng cao, nhưng cấu hình phức tạp hơn.

Gợi ý lựa chọn

  • Dự án lớn, cần hiệu năng cao: Nên dùng MapStruct.
  • Dự án nhỏ, cần triển khai nhanh: Có thể dùng ModelMapper.
  • Cần mapping phức tạp: Xem xét Orika hoặc JMapper.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng hợp các bài hướng dẫn về Design Pattern - 23 mẫu cơ bản của GoF

Link bài viết gốc: https://gpcoder.com/4164-gioi-thieu-design-patterns/. Design Patterns là gì. Design Patterns không phải là ngôn ngữ cụ thể nào cả.

0 0 313

- vừa được xem lúc

Giới thiệu về Builder Design Pattern

Nguồn: refactoring.guru. Builder. Ý đồ.

0 0 47

- vừa được xem lúc

Một ví dụ nhỏ về Factory method

Trong bài viết trước mình đã giới thiệu tới các bạn về Abstract Factory pattern, các bạn quan tâm có thể theo dõi lại tại đây. Để tiếp tục về chủ đề design pattern trong bài viết này mình sẽ trình bày những khái niệm, ưu nhược điểm và các sử dụng của một creational design pattern khác đó là Factory

0 0 41

- vừa được xem lúc

Tôi đã dùng Service Pattern trong NuxtJS như thế nào ?

Giới thiệu. Trong quá trình làm VueJS NuxtJS hay thậm chí là Laravel mình cũng hay áp dụng các pattern như Service hoặc Repository.

0 0 75

- vừa được xem lúc

Hướng dẫn Adapter Design Pattern

Trong bài viết này, chúng ta sẽ cùng tìm hiểu về Adapter Design Pattern qua cấu trúc, cánh triển khai, ví dụ, ưu điểm nhược điểm và ứng dụng của nó. Đây là bài viết đầu tiên của mình nên sẽ không trán

1 1 71

- vừa được xem lúc

Giới thiệu về Prototype Design Pattern

Ý đồ. Prototype là một creational design pattern cho phép bạn sao chép các object hiện có mà không làm cho code của bạn phụ thuộc vào các class của chúng.

0 0 55