2024-07-11 17:34 — 6 phút đọc

Proxy Pattern

#design-pattern#proxy#structural-pattern


Proxy

Proxy là một structural pattern cho phép bạn cung cấp một đối tượng thay thế hoặc đại diện cho một đối tượng khác. Proxy kiểm soát truy cập đến đối tượng gốc, cho phép thực hiện một số hành động trước hoặc sau khi yêu cầu được chuyển đến đối tượng gốc.

Proxy Pattern

Vấn Đề

Tại sao bạn muốn kiểm soát một đối tượng? Đây là một ví dụ: Bạn cần phải tiêu tốn một lượng lớn tài nguyên của hệ thống. Chỉ thỉnh thoảng cần đến nó chứ không phải lúc nào cũng vậy.

proxy-problemDatabase queries can be really slow.

Trong nhiều trường hợp, bạn cần kiểm soát truy cập đến một đối tượng, ví dụ như khi đối tượng này tiêu tốn nhiều tài nguyên hệ thống. Thay vì khởi tạo đối tượng này ngay khi ứng dụng khởi động, bạn có thể trì hoãn việc khởi tạo đến khi thực sự cần thiết. Tuy nhiên, việc trì hoãn này thường dẫn đến sự trùng lặp mã và khó quản lý.

Một ý tưởng thực tế là chúng ta muốn được thực hiện đoạn mã này vào bên trong của các đối tượng đó. Nhưng điều đó đâu phải lúc nào cũng thực hiện được, có thể đối tượng đó lại nằm trong một thư viện bên ngoài hoặc mã nguồn không thể sửa đổi.

Giải Pháp

Proxy pattern gợi ý bạn tạo một lớp proxy mới với cùng giao diện như đối tượng dịch vụ ban đầu. Sau đó, ứng dụng của bạn sẽ sử dụng đối tượng proxy thay vì đối tượng dịch vụ ban đầu. Khi nhận được yêu cầu từ client, proxy sẽ khởi tạo đối tượng dịch vụ thực sự và chuyển tiếp công việc cho đối tượng này.

SolutionThe proxy disguises itself as a database object. It can handle lazy initialization and result caching without the client or the real database object even knowing.

Nhưng mà lợi ích của việc này là gì vậy? Nếu bạn muốn thực thi một số hành động trước hoặc sau logic của class chính, proxy pattern sẽ làm điều đó mà không phải thay đổi mã nguồn của class chính. Vì vậy, bạn có thể sử dụng proxy để thực hiện các chức năng như: kiểm soát truy cập, ghi log, ghi nhớ cache, kiểm soát truy cập từ xa, …

Ví Dụ Thực Tế

Live exampleCredit cards can be used for payments just the same as cash.

Thẻ tín dụng là một proxy cho tài khoản ngân hàng, giúp bạn thanh toán mà không cần mang theo nhiều tiền mặt. Cả 2 đều được implements từ một interface: Chúng có thể được tạo ra để thanh toán. Phục vụ cho người dùng cuối mà không cần thiết phải mang theo quá nhiều tiền mặt, gây rủi ro về các vấn đề an ninh.

Cấu Trúc

Structure Indexes

  1. Service Interface: Khai báo giao diện của dịch vụ. Proxy phải tuân thủ giao diện này để có thể thay thế dịch vụ thực sự.
  2. Service: Lớp cung cấp logic kinh doanh.
  3. Proxy: Lớp có trường tham chiếu đến đối tượng dịch vụ. Proxy thực hiện xử lý trước hoặc sau khi chuyển yêu cầu đến đối tượng dịch vụ. Thường thì proxy sẽ quản lý toàn bộ vòng đời của đối tượng dịch vụ.
  4. Client: Làm việc với cả dịch vụ và proxy thông qua cùng một giao diện. Cách này bạn

Ví Dụ Code

Example

Thư viện ở đây cung cấp cho chúng ta các lớp xử lý về downloading video. Mặc dù vậy, nó rất không hiệu quả. Nếu client requests videos cùng loại rất nhiều lần, thư viện này sẽ tải đi tải lại, thay vì lưu lại vào cache và trả lại kết quả sau lần download đầu tiên.

Lớp proxy sẽ thực thi một interface tương tự giống như lớp nguyên bản của thư viện. Tuy nhiên nó sẽ kiểm tra các tệp videos được tải xuống và trả lại kết quả nếu như client request cùng một videos nhiều lần.

interface ThirdPartyYouTubeLib {
    void listVideos();
    void getVideoInfo(String id);
    void downloadVideo(String id);
}

class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib {
    public void listVideos() { /* Gửi yêu cầu API đến YouTube */ }
    public void getVideoInfo(String id) { /* Lấy thông tin video */ }
    public void downloadVideo(String id) { /* Tải video từ YouTube */ }
}

class CachedYouTubeClass implements ThirdPartyYouTubeLib {
    private ThirdPartyYouTubeLib service;
    private List<String> listCache;
    private Map<String, String> videoCache;
    private boolean needReset;

    public CachedYouTubeClass(ThirdPartyYouTubeLib service) {
        this.service = service;
    }

    public void listVideos() {
        if (listCache == null || needReset) {
            listCache = service.listVideos();
        }
        return listCache;
    }

    public void getVideoInfo(String id) {
        if (videoCache == null || needReset) {
            videoCache.put(id, service.getVideoInfo(id));
        }
        return videoCache.get(id);
    }

    public void downloadVideo(String id) {
        if (!downloadExists(id) || needReset) {
            service.downloadVideo(id);
        }
    }
}

class YouTubeManager {
    protected ThirdPartyYouTubeLib service;

    public YouTubeManager(ThirdPartyYouTubeLib service) {
        this.service = service;
    }

    public void renderVideoPage(String id) {
        String info = service.getVideoInfo(id);
        // Render trang video
    }

    public void renderListPanel() {
        List<String> list = service.listVideos();
        // Render danh sách video
    }

    public void reactOnUserInput() {
        renderVideoPage();
        renderListPanel();
    }
}

class Application {
    public void init() {
        ThirdPartyYouTubeLib youTubeService = new ThirdPartyYouTubeClass();
        ThirdPartyYouTubeLib youTubeProxy = new CachedYouTubeClass(youTubeService);
        YouTubeManager manager = new YouTubeManager(youTubeProxy);
        manager.reactOnUserInput();
    }
}

Tính ứng dụng của Proxy Pattern

  • Khởi tạo trì hoãn (virtual proxy): Thay vì khởi tạo đối tượng khi ứng dụng khởi động, bạn có thể trì hoãn việc khởi tạo đến khi thực sự cần thiết.
  • Kiểm soát truy cập (protection proxy): Chỉ cho phép những client nhất định sử dụng dịch vụ.
  • Thực thi cục bộ dịch vụ từ xa (remote proxy): Gửi yêu cầu qua mạng đến dịch vụ từ xa.
  • Ghi log yêu cầu (logging proxy): Ghi lại các yêu cầu trước khi chuyển đến đối tượng dịch vụ.
  • Bộ nhớ đệm (caching proxy): Lưu kết quả các yêu cầu để sử dụng lại.
  • Tham chiếu thông minh (smart reference): Quản lý vòng đời của đối tượng dịch vụ.

Ưu và Nhược Điểm

  • Ưu Điểm:
    • Kiểm soát đối tượng dịch vụ mà client không biết.
    • Quản lý vòng đời của đối tượng dịch vụ.
    • Hoạt động ngay cả khi đối tượng dịch vụ chưa sẵn sàng.
    • (Open/Closed Principle). Có thể thay đổi và mở rộng dễ dàng mà không cần thay đổi mã nguồn của đối tượng dịch vụ.
  • Nhược Điểm:
    • Mã nguồn phức tạp hơn do cần thêm nhiều lớp.
    • Đáp ứng từ dịch vụ có thể bị chậm trễ.

Liên Quan Đến Các Mẫu Khác

  • Adapter: Truy cập object qua interface diện khác.
  • Decorator: Truy cập object qua interface được mở rộng.
  • Facade: Tương tự Proxy, nhưng với nhiều interface khác nhau.

Proxy là một Pattern mạnh mẽ, cung cấp cách tiếp cận linh hoạt và hiệu quả để quản lý truy cập đối tượng trong các ứng dụng phần mềm phức tạp.

Tài Liệu Tham Khảo


aitu avatar

Hi! Tôi là Tuyên — Hiện tại tôi đang làm Software Architect, Senior developer tại một công ty nhỏ ở Hà Nội. Tôi cảm thấy thích thú, đam mê, yêu thích với việc viết lách và chia sẻ những kiến thức mà tôi biết. Hãy đọc nhiều hơn tại blogs và tới about để biết thêm về tôi nhé.