Chương 11: Proxy Pattern – Kiểm soát truy cập đối tượng (P2)

Sẵn sàng cho Virtual Proxy

Được rồi, cho đến giờ bạn đã biết định nghĩa về Proxy Pattern là gì ở phần 1, và bạn đã xem qua một ví dụ cụ thể: Remote Proxy. Bây giờ chúng tôi sẽ xem xét một loại proxy khác, Virtual Proxy. Khi bạn phát hiện ra, Mẫu Proxy có thể ở nhiều dạng, tuy nhiên tất cả các dạng đều tuân theo thiết kế proxy chung. Tại sao lại có nhiều dạng? Bởi vì mẫu proxy có thể được áp dụng cho rất nhiều trường hợp sử dụng khác nhau. Hãy kiểm tra Virtual Proxy và so sánh với Remote Proxy:

Sơ đồ Virtual Proxy

Hiển thị CD covers

Giả sử bạn muốn viết một ứng dụng hiển thị CD covers danh sách đĩa CD yêu thích của bạn. Bạn có thể tạo một menu các CD title và sau đó lấy hình ảnh từ một dịch vụ trực tuyến như Amazon.com. Nếu bạn đang sử dụng Java Swing, bạn có thể tạo Icon và yêu cầu nó tải hình ảnh từ internet. Vấn đề duy nhất là, tùy thuộc vào tốc độ mạng mạng và băng thông kết nối của bạn, việc truy xuất CD covers có thể mất một chút thời gian, vì vậy ứng dụng của bạn sẽ hiển thị một cái gì đó (như “message”) trong khi bạn chờ tải hình ảnh. Chúng tôi cũng không muốn treo toàn bộ ứng dụng trong khi nó đang chờ tải hình ảnh. Khi hình ảnh được tải, “mesage” sẽ biến mất và bạn sẽ thấy hình ảnh.

Một cách dễ dàng để đạt được điều này là thông qua một virtual proxy. Proxy ảo có thể thay thế Icon, quản lý background loading và trước khi hình ảnh được tải về từ mạng, chúng ta sẽ hiển thị “Loading CD cover, please wait…”. Khi hình ảnh được tải, proxy ủy thác hiển thị cho icon.

Thiết kế CD cover Virtual Proxy 

Trước khi viết code cho CD Cover Viewer, hãy xem sơ đồ lớp của nó.

Bạn sẽ thấy nó trông giống như sơ đồ lớp Remote Proxy của chúng tôi, nhưng ở đây proxy được sử dụng để ẩn một đối tượng “đắt đỏ” (tốn thời gian) để tạo (vì chúng tôi cần truy xuất dữ liệu Icon thông qua internet) trái ngược với đối tượng thực sự đang ở một nơi khác trên mạng.

Sơ đồ lớp của CD cover Virtual Proxy

ImageProxy sẽ hoạt động như thế nào

1. Đầu tiên ImageProxy tạo ImageIcon và bắt đầu tải nó từ một URL mạng.

2. Trong khi các byte của hình ảnh đang được truy xuất, ImageProxy hiển thị “Loading CD cover, please wait…”

3. Khi hình ảnh được tải đầy đủ, ImageProxy ủy quyền tất cả các lệnh gọi phương thức đến ImageIcon, bao gồm paintIcon(), getWidth()getHeight().

4. Nếu người dùng yêu cầu một hình ảnh mới, chúng tôi sẽ tạo một proxy mới và bắt đầu lại quá trình.

Viết code Image Proxy

Viết code Image Proxy

Đáp án:

Sử dụng State Pattern: implement hai trạng thái, ImageLoaded ImageNotLoaded. Sau đó đặt code từ câu lệnh if vào các trạng thái tương ứng của chúng. Bắt đầu ở trạng thái ImageNotLoaded và sau đó chuyển sang trạng thái ImageLoaded khi ImageIcon đã được truy xuất.

Testing CD Cover Viewer

Sẵng sàng nướng code

Được rồi, đã đến lúc thử nghiệm Virtual proxy mới “lạ mắt” này. Đằng sau nó, chúng tôi đã tạo ra một ImageProxyTestDrive mới, thiết lập cửa sổ, tạo frame, cài đặt các menu và tạo proxy của chúng tôi. Chúng tôi không có thông tin chi tiết về code này ở đây, nhưng bạn luôn có thể lấy source code và xem hoặc kiểm tra nó ở cuối chương nơi chúng tôi liệt kê tất cả  source code cho Virtual Proxy.

Ở đây, một góc nhìn của test drive code:

Lớp ImageProxyTestDrive

Bây giờ hãy chạy test drive:  

Kết quả chạy test drive
Đầu tiên hiển thị message, sau thi tải ảnh xong sẽ hiện ảnh lên

Những điều bạn có thể thử…

1. Sử dụng menu để tải CD cover khác nhau; xem màn hình proxy hiển thị “loading” cho đến khi hình ảnh tải xong.

2. Thay đổi kích thước cửa sổ khi message “loading” được hiển thị. Lưu ý rằng proxy đang xử lý tải mà không treo cửa sổ Swing.

3. Thêm đĩa CD yêu thích của riêng bạn vào ImageProxyTestDrive.

Chúng ta đã làm những gì?

1. Chúng tôi đã tạo một ImageProxy cho màn hình. Phương thức paintIcon() được gọi và ImageProxy tạo một luồng (thread) để truy xuất hình ảnh và tạo ImageIcon.

2. Tại một thời điểm nào đó, hình ảnh được trả về và ImageIcon được khởi tạo hoàn toàn.

3. Sau khi ImageIcon được tạo, lần sau khi gọi paintIcon(), proxy sẽ ủy quyền cho ImageIcon.

Không có câu hỏi ngớ ngẩn

Hỏi: Đối với tôi, Remote ProxyVirtual Proxy có vẻ khác nhau; chúng có thực sự là MỘT mẫu không?
Đáp: Bạn sẽ tìm thấy rất nhiều biến thể của Mẫu proxy trong thế giới thực; điểm chung của chúng là chúng chặn một lệnh gọi phương thức mà client đang thực hiện trên đối tượng thực sự.
Mức độ chuyển hướng này cho phép chúng tôi thực hiện nhiều việc, bao gồm gửi yêu cầu đến một đối tượng từ xa, cung cấp đại diện cho một đối tượng “đắt đỏ” khi nó được tạo ra hoặc, như bạn đã thấy, cung cấp một số mức bảo vệ có thể xác định client nào nên gọi phương thức nào. Đó chỉ là sự khởi đầu; Mẫu Proxy chung có thể được áp dụng theo nhiều cách khác nhau và chúng tôi sẽ đề cập đến một số cách khác ở cuối chương.

Hỏi: ImageProxy đối với tôi có vẻ giống như một Decorator. Ý tôi là, về cơ bản chúng ta đang bao bọc một đối tượng này bằng một đối tượng khác và sau đó ủy quyền các lệnh gọi cho ImageIcon. Tôi đang bỏ lỡ điều gì?
Đáp: Đôi khi Proxy và Decorator trông rất giống nhau, nhưng mục đích của chúng khác nhau: một decorator thêm hành vi vào một lớp, trong khi proxy kiểm soát quyền truy cập vào nó.
Bạn có thể tự hỏi, “Loading message có thêm hành vi mới vào không?” Theo một số cách thì nó là có; tuy nhiên, quan trọng hơn, ImageProxy đang kiểm soát quyền truy cập vào ImageIcon. Nó kiểm soát quyền truy cập như thế nào? Hãy nghĩ về nó theo cách này: proxy đang tách Client khỏi ImageIcon. Nếu chúng không tách, Client sẽ phải đợi cho đến khi mỗi hình ảnh được truy xuất trước khi nó có thể vẽ toàn bộ giao diện. Proxy kiểm soát quyền truy cập vào ImageIcon để trước khi nó được tạo đầy đủ, proxy cung cấp một biểu diễn khác trên màn hình. Sau khi ImageIcon được tạo, proxy cho phép truy cập.

Hỏi: Làm cách nào để khiến Client sử dụng Proxy thay vì Object thực (gọi là Subject)?
Đáp: Câu hỏi hay. Một kỹ thuật phổ biến là cung cấp một Factory khởi tạo và trả về Subject. Bởi vì điều này xảy ra trong factory method nên chúng tôi có thể bọc Subject bằng proxy trước khi return. Client không bao giờ biết hoặc không quan tâm rằng họ đang sử dụng proxy thay vì Object thực (Subject).


Hỏi: Tôi nhận thấy trong ví dụ ImageProxy, bạn luôn tạo một ImageIcon để lấy hình ảnh, ngay cả khi hình ảnh đã được truy xuất. Bạn có thể triển khai một cái gì đó tương tự như ImageProx lưu trữ các truy xuất trước đó?
Đáp: Bạn đang nói về một dạng đặc biệt của Virtual Proxy được gọi là Caching Proxy. Một caching proxy duy trì bộ nhớ cache của các đối tượng đã tạo trước đó và khi có yêu cầu, nó sẽ trả về đối tượng được lưu trong bộ nhớ cache, nếu có thể. Chúng ta sẽ xem xét điều này và một số biến thể khác của Mẫu proxy ở cuối chương.

Hỏi: Tôi đã thấy Decorator và Proxy liên quan, nhưng còn Adapter thì sao? Mẫu này cũng giống một Adapter Pattern?
Đáp: Cả Proxy và Adapter Pattern đều đứng trước các đối tượng khác và chuyển tiếp yêu cầu đến chúng. Hãy nhớ rằng Adapter Pattern thay đổi giao diện của các đối tượng mà nó thích ứng, trong khi Proxy Pattern implement cùng một giao diện với Sublect.
Có một điểm tương tự bổ sung liên quan đến Protection Proxy. Protection Proxy có thể cho phép hoặc không cho phép Client truy cập vào các phương thức cụ thể trong một đối tượng dựa trên vai trò của Client. Bằng cách này, Protection Proxy có thể chỉ cung cấp một phần interface cho Client, interface này khá giống với một số Adapter Pattern. Chúng ta sẽ xem xét Protection Proxy trong một vài trang tới.

Buổi nói chuyện tối nay: Proxy và Decorator Pattern

Proxy: Xin chào, anh Decorator. Tôi cho rằng anh ở đây vì đôi khi mọi người khiến chúng ta bối rối?

Decorator: Chà, tôi nghĩ lý do khiến mọi người khiến chúng ta bối rối là chú em cứ giả vờ là một hình mẫu hoàn toàn khác, trong khi thực tế, chú em chỉ là một Người trang trí (Decorator) được ngụy trang. Tôi thực sự không nghĩ rằng chú em không nên sao chép tất cả các ý tưởng của tôi.

Proxy: Tôi sao chép ý tưởng của anh? Làm ơn… Tôi kiểm soát quyền truy cập vào các đối tượng. Anh chỉ việc trang trí chúng. Công việc của tôi quan trọng hơn công việc của anh, anh đùa không vui chút nào.

Decorator: “Chỉ” trang trí? Chú em nghĩ rằng trang trí là một mẫu không quan trọng? Hãy để tôi nói với chú em, tôi thêm hành vi. Đó là điều quan trọng nhất của các đối tượng – những gì chúng tôi làm!

Proxy: Tốt thôi, vì vậy có thể anh không hoàn toàn vô dụng … nhưng tôi vẫn không hiểu tại sao anh nghĩ rằng tôi đang sao chép tất cả ý tưởng của anh. Tôi chỉ muốn đại diện cho các đối tượng của tôi, không phải trang trí chúng.

Decorator: Chú em có thể gọi nó là “đại diện” nhưng nếu nó trông giống một con vịt và đi như một con vịt… Ý tôi là, chỉ cần nhìn vào Virtual Proxy của chú em; đó chỉ là một cách khác để thêm hành vi để thực hiện điều gì đó trong khi một số đối tượng “đắt đỏ” đang được tải và Remote Proxy của chú em là một cách nói chuyện với các đối tượng từ xa để client không phải bận tâm đến điều đó. Đó cũng chỉ là những hành vi, giống như tôi đã nói.

Proxy: Tôi không nghĩ là anh đã hiểu, Decorator. Tôi ủng hộ Subject của mình; Tôi không chỉ thêm hành vi. Client sử dụng tôi như một người đại diện cho đối tượng thực vì tôi có thể bảo vệ họ khỏi sự truy cập không mong muốn hoặc giữ cho GUI của họ không bị treo khi họ đang chờ tải các đối tượng lớn hoặc làm “ẩn đi” sự thật rằng Subject của họ đang chạy trên các máy từ xa . Tôi muốn nói rằng đó là một mục đích rất khác với ý định của anh!

Decorator: Chú em nói sao cũng được. Tôi implement giao diện giống như các đối tượng tôi bọc; bạn cũng vậy.

Proxy: Được rồi, hãy xem lại điều đó. Anh bọc một đối tượng. Mặc dù đôi khi chúng ta nói một cách không chính thức một proxy bọc Subject của nó, nhưng đó không thực sự là một thuật ngữ chính xác.

Decorator: Ồ, vậy à? Tại sao không?

Proxy: Hãy nghĩ về một Remote Proxy… tôi đang bọc đối tượng nào? Đó là đối tượng mà tôi đang “đại diện” và kiểm soát quyền truy cập đang ở trên một máy khác!
Hãy xem anh làm điều đó như thế nào.

Decorator: Được rồi, nhưng chúng ta đều biết Remote Proxy khá kỳ lạ. Có một ví dụ thứ hai không? Tôi nghi ngờ điều đó.

Proxy: Được, được rồi, sử dụng Virtual Proxy… hãy nghĩ về ví dụ về CD covers. Khi client sử dụng tôi lần đầu tiên làm proxy, Subject thậm chí không tồn tại! Vậy tôi đang bọc cái gì ở đó?

Decorator: Uh huh, và điều tiếp theo chú em sẽ nói là chú em thực sự phải tạo ra các đối tượng.

Proxy: Tôi chưa bao giờ biết những người trang trí (decorator) lại ngốc như vậy! Tất nhiên, đôi khi tôi tạo ra các đối tượng, anh nghĩ làm thế nào để Virtual Proxy có được Subject của nó! Được rồi, anh vừa chỉ ra một sự khác biệt lớn giữa chúng ta: cả hai chúng ta đều biết những decorator chỉ thêm hành vi; và không bao giờ có thể khởi tạo bất cứ điều gì.

Decorator: Ồ, vậy à?

Proxy: Này, sau cuộc trò chuyện này, tôi tin rằng bạn chỉ là một “proxy” ngu ngốc (ý nói decorator cũng là 1 dạng proxy)!

Decorator: “Proxy” ngu ngốc? Tôi muốn chú em thấy tôi có thể bọc một cách đệ quy một đối tượng với 10 decorator nhưng vẫn có thể dễ hiểu.

Proxy: Rất hiếm khi anh thấy một proxy bọc một Subject nhiều lần; trên thực tế, nếu anh đang bọc một thứ gì đó 10 lần, tốt hơn anh nên quay lại kiểm tra lại thiết kế của mình.

Decorator: Cũng giống như một proxy, hoạt động thì như đối tượng thật, trong khi thực tế chú em chỉ đứng sau các đối tượng đang thực hiện công việc thực sự. Chú em biết đấy, tôi thực sự cảm thấy tiếc cho chú em.

Sử dụng Proxy của Java API để tạo Protection Proxy

Java có hỗ trợ proxy riêng ngay trong gói java.lang.reflect. Với gói này, Java cho phép bạn tạo một lớp proxy nhanh chóng implement một hoặc nhiều interface và chuyển tiếp các lệnh gọi phương thức đến một lớp mà bạn chỉ định. Vì lớp proxy thực được tạo trong thời gian chạy (runtime), chúng tôi gọi công nghệ Java này là proxy động (dynamic proxy).

Chúng tôi sẽ sử dụng dynamic proxy của Java để tạo triển khai proxy tiếp theo (protection proxy), nhưng trước khi làm điều đó, chúng ta hãy xem nhanh sơ đồ lớp cho thấy cách các dynamic proxy được kết hợp với nhau. Giống như hầu hết mọi thứ trong thế giới thực, nó hơi khác so với khái niệm cổ điển của mẫu:

Proxy của Java API

Vì Java tạo ra lớp Proxy cho bạn, bạn cần một cách để cho lớp Proxy biết phải làm gì. Bạn không thể đặt code của mình vào lớp Proxy của Java như chúng ta đã làm trước đây, vì bạn không triển khai trực tiếp. Vì vậy, nếu bạn không thể đặt code vào lớp Proxy, bạn sẽ đặt nó ở đâu? Trong một InvocationHandler. Công việc của InvocationHandler là trả lời bất kỳ lệnh gọi phương thức nào trên proxy. Hãy coi InvocationHandler là đối tượng mà Proxy yêu cầu thực hiện tất cả các công việc thực sự sau khi nó nhận được các lệnh gọi phương thức.
Được rồi, hãy cùng xem qua cách sử dụng dynamic proxy…

Dịch vụ mai mối ở Objectville

Thị trấn nào cũng cần có dịch vụ mai mối, đúng không? Bạn đã hoàn thành nhiệm vụ và triển khai dịch vụ hẹn hò cho Objectville. Bạn cũng đã cố gắng đổi mới bằng cách đưa tính năng “Phổ biến hoặc Không – Hot or Not” vào dịch vụ nơi những người tham gia có thể đánh giá lẫn nhau – bạn cho rằng điều này giúp người dùng của bạn tương tác và xem qua các kết quả phù hợp có thể có; nó cũng làm cho mọi thứ vui vẻ hơn rất nhiều.
Dịch vụ của bạn xoay quanh một PersonBean cho phép bạn thiết lập và nhận thông tin về một người:

Bây giờ chúng ta hãy kiểm tra việc triển khai …

Implement PersonBean

Mặc dù chúng tôi nghi ngờ các yếu tố khác có thể khiến Elroy không tìm kiếm được người phù hợp, nhưng anh ấy đã đúng: bạn không thể rating cho chính mình hoặc thay đổi dữ liệu của người khác. Cách PersonBean của chúng tôi được định nghĩa, bất kỳ ai cũng có thể gọi bất kỳ phương thức nào trong đó.
Đây là một ví dụ hoàn hảo về nơi chúng tôi có thể sử dụng Protection Proxy. Protection Proxy là gì? Nó là một proxy kiểm soát quyền truy cập vào một đối tượng dựa trên quyền truy cập. Ví dụ: nếu chúng ta có đối tượng nhân viên (employee), Protection proxy có thể cho phép employee gọi một số phương thức nhất định trên đối tượng, người quản lý (manager) gọi các phương thức bổ sung (như setSalary()) và nhân viên quản lí nhân sự (human resources) có thể gọi bất kỳ phương thức nào trên đối tượng đó.
Trong dịch vụ hẹn hò của mình, chúng tôi muốn đảm bảo rằng người dùng có thể cài đặt thông tin cá nhân của riêng mình trong khi ngăn người khác sửa đổi thông tin đó. Chúng tôi cũng muốn cho phép điều ngược lại với xếp hạng HotOrNot: chúng tôi muốn những người dùng khác có thể rating cho bạn, nhưng họ không thể tự rating cho mình.
Chúng tôi cũng có một số phương thức getter trong PersonBean và vì không có phương thức nào trong số này trả về thông tin cá nhân, nên bất kỳ người dùng nào cũng có thể gọi chúng.

Đoạn phim 5 phút: protecting subjects

Internet Bubble dường như là một ký ức xa vời; đó là những ngày mà tất cả những gì bạn cần làm để tìm một công việc tốt hơn, lương cao hơn là đi bộ qua đường. Ngay cả các đại lý cho các nhà phát triển phần mềm cũng thịnh hành …

Đoạn phim 5 phút: protecting subjects

Bức tranh lớn: tạo Dynamic Proxy cho PersonBean

Chúng tôi có một số vấn đề cần khắc phục: Người dùng không được thay đổi rating HotOrNot của chính họ và cũng không thể thay đổi thông tin cá nhân của người dùng khác khác. Để khắc phục những sự cố này, chúng tôi sẽ tạo hai proxy: một proxy để truy cập đối tượng PersonBean của riêng bạnmột proxy để truy cập đối tượng PersonBean của người dùng khác. Bằng cách đó, các proxy có thể kiểm soát những yêu cầu nào có thể được thực hiện trong từng trường hợp.

Để tạo các proxy này, chúng tôi sẽ sử dụng dynamic proxy của Java API mà bạn đã thấy một vài trang trước đó. Java sẽ tạo hai proxy cho chúng ta; tất cả những gì chúng ta cần làm là cung cấp cho bộ xử lý biết phải làm gì khi một phương thức được gọi trên proxy.

Bước 1: Tạo InvocationHandlers

InvocationHandlers thực hiện hành vi của proxy. Như bạn sẽ thấy Java sẽ đảm nhận việc tạo đối tượng và lớp proxy thực tế, chúng tôi chỉ cần cung cấp một trình xử lý biết phải làm gì khi một phương thức được gọi trên đó.

Bước 2: Viết code tạo Dynamic proxy.

Chúng ta cần viết một chút code để tạo lớp proxy và khởi tạo nó. Chúng tôi sẽ xem qua code này chỉ trong giây lát.

Bước 3: Bao bọc bất kỳ đối tượng PersonBean nào bằng proxy thích hợp.

Khi chúng ta cần sử dụng một đối tượng PersonBean, đó là đối tượng của chính người dùng (trong trường hợp đó, sẽ gọi người này là “chủ sở hữu”) hoặc là một người dùng khác (trong trường hợp đó, chúng tôi sẽ gọi người này là “không sở hữu”). Trong cả hai trường hợp, chúng tôi tạo proxy thích hợp cho PersonBean.

Bước 1: Tạo Invocation Handlers

Chúng tôi biết rằng chúng tôi cần phải viết hai trình xử lý lệnh gọi (invocation handlers), một cho “chủ sở hữu” và một cho “không sở hữu”. Nhưng invocation handlers là gì? Đây là cách để suy nghĩ về chúng: khi một lệnh gọi phương thức được thực hiện trên proxy, proxy sẽ chuyển tiếp cuộc gọi đó đến invocation handlers của bạn, nhưng không phải bằng cách gọi phương thức tương ứng trên invocation handlers. Vậy, nó gọi là gì? Hãy xem giao diện InvocationHandler:

Chỉ có một phương thức, invoke() và bất kể phương thức nào được gọi trên proxy, phương thức invoke() là phương thức được gọi trên invocation handlers. Hãy xem cách này hoạt động như thế nào:

Tiếp tục tạo Invocation Handlers…

Khi invoke() được gọi bởi proxy, làm cách nào để bạn biết phải làm gì với lệnh gọi? Thông thường, bạn sẽ kiểm tra phương thức được gọi trên proxy và đưa ra quyết định dựa trên tên của method và có thể là các đối số (arguments) của nó. Hãy implement OwnerInvocationHandler để xem cách này hoạt động như thế nào:

Bài tập: NonOwnerInvocationHandler hoạt động giống như OwnerInvocationHandler ngoại trừ việc nó cho phép các lệnh gọi setHotOrNotRating() và nó không cho phép các lệnh gọi đến bất kỳ phương thức set nào khác. Hãy tiếp tục và tự viết trình xử lý này.

Đáp án:

import java.lang.reflect.*;
public class NonOwnerInvocationHandler implements InvocationHandler {
     PersonBean person;
     public NonOwnerInvocationHandler(PersonBean person) {
          this.person = person;
     }
     public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
          try {
               if (method.getName().startsWith(“get”)) {
                    return method.invoke(person, args);
               } else if (method.getName().equals(“setHotOrNotRating”)) {
                    return method.invoke(person, args);
               } else if (method.getName().startsWith(“set”)) {
                    throw new IllegalAccessException();
               }
          } catch (InvocationTargetException e) {
               e.printStackTrace();
          }
          return null;
     }
}

Bước hai: Tạo lớp Proxy và khởi tạo đối tượng Proxy

Bây giờ, tất cả những gì chúng ta còn lại là tạo “động” lớp proxy và khởi tạo đối tượng proxy. Hãy bắt đầu bằng cách viết một phương thức sử dụng PersonBean và biết cách tạo proxy “chủ sở hữu” cho nó. Đó là, chúng tôi sẽ tạo một loại proxy chuyển tiếp các cuộc gọi phương thức của nó tới OwnerInvocationHandler. Đây là code:

Làm nhọn bút chì

Mặc dù hơi phức tạp nhưng không có nhiều thứ khi tạo dynamic proxy. Tại sao bạn không viết getNonOwnerProxy(), trả về một proxy cho NonOwnerInvocationHandler:
Xa hơn: bạn có thể viết một phương thức getProxy() nhận một InvocationHandler, một PersonBean và return về một proxy sử dụng InvocationHandler đó không?

PersonBean getNonOwnerProxy(PersonBean person) {
     return (PersonBean) Proxy.newProxyInstance(
               person.getClass().getClassLoader(),
               person.getClass().getInterfaces(),
               new NonOwnerInvocationHandler(person));
}

Thử nghiệm dịch vụ mai mối

Hãy chạy thử nghiệm dịch vụ mai mối và xem cách nó kiểm soát quyền truy cập vào các phương thức setter dựa trên proxy được sử dụng.

Chạy code

Không có câu hỏi ngớ ngẩn

Hỏi: Vậy khía cạnh “dynamic” của dynamic proxy chính xác là gì? Có phải tôi đang khởi tạo proxy và đặt nó trong handler ở runtime không?
Đáp: Không, proxy là “dynamic” vì lớp của nó được tạo trong thời gian chạy. Hãy nghĩ về nó: trước khi code của bạn chạy, không có lớp proxy nào; nó được tạo theo yêu cầu từ tập hợp các interface mà bạn truyền cho nó.

Hỏi: InvocationHandler của tôi có vẻ giống như một proxy rất lạ, nó không triển khai bất kỳ phương thức nào của lớp mà nó đang ủy quyền.
Đáp: Đó là bởi vì InvocationHandler không phải là proxy – nó là một lớp mà proxy gửi đến để xử lý các cuộc gọi phương thức. Bản thân proxy được tạo dynamic trong thời gian chạy bằng phương thức static Proxy.newProxyInstance().

Hỏi: Có cách nào để biết một lớp có phải là lớp Proxy không?
Đáp: Vâng. Lớp Proxy có một phương thức static được gọi là isProxyClass(). Việc gọi phương thức này với một lớp sẽ trả về true nếu lớp đó là một lớp dynamic proxy. Ngoài ra, lớp proxy sẽ hoạt động giống như bất kỳ lớp nào khác implement một bộ giao diện cụ thể.

Hỏi: Có bất kỳ hạn chế nào đối với các loại interface tôi có thể truyền vào newProxyInstance() không?
Đáp: Vâng, có một vài. Đầu tiên, cần chỉ ra rằng chúng ta luôn truyền newProxyInstance() một mảng interface – chỉ interface là được phép, không phải class. Các hạn chế chính là tất cả các interface không public cần phải từ cùng một package. Bạn cũng không thể có các giao diện có tên phương thức xung đột (nghĩa là hai giao diện có cùng một phương thức có cùng chữ ký). Ngoài ra còn có một số điểm nhỏ khác, vì vậy tại một số thời điểm, bạn nên xem tài liệu trên các dynamic proxy trong javadoc.

Hỏi: Tại sao bạn sử dụng skeletons? Tôi nghĩ rằng chúng tôi đã loại bỏ chúng trong Java 1.2.
Đáp: Bạn nói đúng; chúng ta không cần thực sự tạo ra các skeletons. Đối với Java 1.2, thời gian chạy RMI có thể gửi các cuộc gọi máy client trực tiếp đến dịch vụ từ xa bằng cách sử dụng reflection. Nhưng chúng tôi muốn hiển thị skeleton, vì về mặt khái niệm, nó giúp bạn hiểu rằng có điều gì đó nằm bên dưới đang thực hiện giao tiếp giữa client stubs và dịch vụ từ xa xảy ra.

Hỏi: Tôi nghe nói rằng trong Java 5, tôi thậm chí không cần tạo stubs nữa. Có đúng như vậy không?
Đáp: Chắc chắn là vậy. Trong Java 5, RMI và Dynamic Proxy kết hợp với nhau và bây giờ các stub được tạo động bằng Dynamic Proxy. Stubs của đối tượng từ xa là một instance của java.lang.reflect.Proxy (với trình xử lý lệnh gọi) được tạo tự động để xử lý tất cả các chi tiết về việc ứng dụng khách gọi phương thức cục bộ tới đối tượng từ xa. Vì vậy, bây giờ bạn không cần phải sử dụng rmic nữa; mọi thứ bạn cần để client nói chuyện với một đối tượng từ xa đã được xử lý cho bạn ở bên trong.

Sở thú Proxy

Chào mừng đến với Sở thú Objectville!
Bây giờ bạn đã biết về các remote proxy, virtual và protection, nhưng ngoài thế giới thực, bạn sẽ thấy rất nhiều phiên bản của mẫu này. Ở đây, ở một góc Proxy của vườn thú, chúng tôi đã có một bộ sưu tập về các mẫu proxy hoang dã mà chúng tôi đã thu thập được để bạn nghiên cứu.
Công việc của chúng tôi chưa hoàn thành; chúng tôi chắc chắn rằng bạn sẽ thấy nhiều biến thể hơn của mẫu này trong thế giới thực, vì vậy hãy giúp chúng tôi lập danh mục nhiều proxy hơn. Hãy cùng xem bộ sưu tập hiện có:

Firewall Proxy, Smart Reference Proxy và Caching Proxy
Synchronization Proxy, Complexity Hiding Proxy và Copy on write proxy

Tóm tắt

  • Mẫu Proxy cung cấp một đại diện cho một đối tượng để kiểm soát quyền truy cập của client vào đối tượng đó. Có một số cách nó có thể quản lý quyền truy cập đó.
  • Một Remote Proxy quản lý sự tương tác giữa một client và một đối tượng từ xa (khác với vùng nhớ của client).
  • Một Virtual Proxy kiểm soát quyền truy cập vào một đối tượng “đắt đỏ” để khởi tạo.
  • Một Protected Proxy kiểm soát quyền truy cập vào các phương thức của một đối tượng dựa trên lời gọi phương thức (caller).
  • Nhiều biến thể khác của Proxy Pattern tồn tại bao gồm proxy bộ nhớ đệm (caching proxies), proxy đồng bộ hóa (synchronization proxies), proxy tường lửa (firewall proxies), copy-on-write proxy, v.v.
  • Proxy có cấu trúc tương tự như Decorator, nhưng cả hai khác nhau về mục đích của chúng.
  • Decorator thêm hành vi vào đối tượng, trong khi Proxy kiểm soát quyền truy cập.
  • Hỗ trợ tích hợp của Java dành cho Proxy có thể tạo một lớp dynamic proxy theo yêu cầu và gửi tất cả các lệnh gọi trên đó đến một trình xử lý (handler) mà bạn chọn.
  • Giống như bất kỳ trình bao bọc nào (như Decorator…), proxy sẽ tăng số lượng lớp và đối tượng trong thiết kế của bạn.

Đây là link đính kèm bản gốc của quyển sách: Head First Design Patterns.
Đây là link đính kèm sourcecode của sách: Tải SourceCode.

Trả lời

Email của bạn sẽ không được hiển thị công khai.