设计模式示例_代理设计模式示例
設計模式示例
本文是我們名為“ Java設計模式 ”的學院課程的一部分。
在本課程中,您將深入研究大量的設計模式,并了解如何在Java中實現和利用它們。 您將了解模式如此重要的原因,并了解何時以及如何應用模式中的每一個。 在這里查看 !
目錄
1.簡介 2.什么是代理模式 3.遠程代理 4.虛擬代理 5.保護代理 6.何時使用代理模式 7.其他代理 8. JDK中的代理模式 9.下載源代碼1.簡介
在本課程中,我們將討論結構模式(代理模式)。 代理模式為另一個對象提供代理或占位符,以控制對其的訪問。
代理模式提供了許多不同的變體。 一些重要的變化是遠程代理,虛擬代理和保護代理。 在本課程中,我們將對這些變體有更多的了解,并將使用Java實現它們。 但是在我們這樣做之前,讓我們先了解更多有關代理模式的知識。
2.什么是代理模式
代理模式用于創建代表對象,該代表對象控制對另一個對象的訪問,該對象可能是遠程的,創建成本很高或需要保護。
控制對對象的訪問的一個原因是將創建和初始化的全部成本推遲到我們實際需要使用它之前。 另一個原因可能是充當駐留在不同JVM中的對象的本地代表。 代理對于控制對原始對象的訪問非常有用,尤其是在對象應具有不同的訪問權限時。
在代理模式中,客戶端不直接與原始對象對話,而是將其調用委托給代理對象,該代理對象調用原始對象的方法。 重要的一點是客戶端不了解代理,代理充當客戶端的原始對象。 但是這種方法有很多變化,我們將很快看到。
讓我們看看代理模式及其重要參與者的結構。
圖1
1a。 維護一個參考,使代理可以訪問真實主題。 如果RealSubject和Subject接口相同,則代理可以引用Subject。
1b。 提供與Subject相同的接口,以便可以用代理代替真實的Subject。 1c。 控制對真實主題的訪問,并可能負責創建和刪除它。
2a。 為RealSubject和Proxy定義公共接口,以便可以在需要RealSubject的任何地方使用Proxy。
3a。 定義代理代表的真實對象。
代理模式有三個主要變體:
我們將在下一部分中逐一討論這些代理。
3.遠程代理
有一家披薩公司,其在各個位置都有分店。 公司的所有者從各個銷售點獲取公司員工的每日報告。 Pizza Company支持的當前應用程序是桌面應用程序,而不是Web應用程序。 因此,所有者必須要求其員工生成報告并將其發送給他。 但是現在所有者希望自己生成并檢查報告,以便他可以在任何人需要的情況下隨時生成報告,而無需任何人的幫助。 所有者希望您為他開發一個應用程序。
這里的問題是所有應用程序都在各自的JVM上運行,而Report Checker應用程序(我們將很快設計)應在所有者的本地系統中運行。 所有者的系統JVM中不存在生成報告所需的對象,并且您不能直接調用遠程對象。
遠程代理用于解決此問題。 我們知道該報告是由用戶生成的,因此需要一個對象來生成報告。 我們所需要做的就是聯系位于遠程位置的對象,以獲得所需的結果。 遠程代理充當遠程對象的本地代表。 遠程對象是駐留在不同JVM堆中的對象。 您將方法調用到本地對象,該方法將調用轉移到遠程對象。
您的客戶端對象的行為就像其進行遠程方法調用一樣。 但這是在處理本地所有網絡通信底層細節的堆本地代理對象上調用方法。
Java支持使用RMI在位于兩個不同位置(或兩個不同JVM)的兩個對象之間進行通信。 RMI是遠程方法調用,用于構建客戶端和服務幫助程序對象,直至使用與遠程服務相同的方法創建客戶端幫助程序對象。 使用RMI,您不必自己編寫任何網絡或I / O代碼。 使用客戶端,您可以像在客戶端的本地JVM中運行的對象上的普通方法調用一樣調用遠程方法。
RMI還提供了運行中的基礎結構來使其全部正常工作,其中包括客戶端可以用來查找和訪問遠程對象的查找服務。 RMI調用和本地方法調用之間有一個區別。 客戶助手通過網絡發送方法調用,因此RMI調用涉及網絡和I / O。
現在讓我們看一下代碼。 我們有一個接口ReportGenerator及其具體實現ReportGeneratorImpl已在不同位置的JVM上運行。 首先要創建一個遠程服務,我們需要更改代碼。
現在, ReportGenerator接口將如下所示:
package com.javacodegeeks.patterns.proxypattern.remoteproxy;import java.rmi.Remote; import java.rmi.RemoteException;public interface ReportGenerator extends Remote{public String generateDailyReport() throws RemoteException;}這是一個遠程接口,它定義客戶端可以遠程調用的方法。 客戶端將使用它作為您的服務的類類型。 Stub和實際服務都將實現此目的。 接口中的方法返回一個String對象。 您可以從方法中返回任何對象; 該對象將通過網絡從服務器傳送回客戶端,因此它必須是Serializable 。 請注意,此接口中的所有方法都應拋出RemoteException 。
package com.javacodegeeks.patterns.proxypattern.remoteproxy;import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Date;public class ReportGeneratorImpl extends UnicastRemoteObject implements ReportGenerator{private static final long serialVersionUID = 3107413009881629428L;protected ReportGeneratorImpl() throws RemoteException {}@Overridepublic String generateDailyReport() throws RemoteException {StringBuilder sb = new StringBuilder();sb.append("********************Location X Daily Report********************");sb.append("\\n Location ID: 012");sb.append("\\n Today's Date: "+new Date());sb.append("\\n Total Pizza's Sell: 112");sb.append("\\n Total Price: $2534");sb.append("\\n Net Profit: $1985");sb.append("\\n ***************************************************************");return sb.toString();}public static void main(String[] args) {try {ReportGenerator reportGenerator = new ReportGeneratorImpl();Naming.rebind("PizzaCoRemoteGenerator", reportGenerator);} catch (Exception e) {e.printStackTrace();}}}上面的類是完成實際工作的遠程實現。 客戶端希望在其上調用方法的對象。 該類擴展了UnicastRemoteObject ,為了用作遠程服務對象,您的對象需要一些與遠程相關的功能。 最簡單的方法是從java.rmi.server包中擴展UnicastRemoteObject ,然后讓該類為您完成工作。
UnicastRemoteObject類構造函數會拋出RemoteException ,因此您需要編寫一個聲明了RemoteException的無參數構造函數。
為了使遠程服務對客戶端可用,您需要在RMI注冊表中注冊該服務。 您可以通過實例化并將其放入RMI注冊表來完成此操作。 注冊實現對象時,RMI系統實際上將存根放在注冊表中,因為這是客戶端需要的。 Naming.rebind方法用于注冊對象。 它有兩個參數,第一個是用于命名服務的字符串,另一個參數是將由客戶端獲取以使用服務的對象。
現在,要創建存根,您需要在遠程實現類上運行rmic 。 Rmic工具與Java軟件開發人員一起提供,實現服務實現并創建一個新的存根。 您應該打開命令提示符(cmd),然后從遠程實現所在的目錄運行rmic。
下一步是運行rmiregistry,打開一個終端并啟動注冊表,只需鍵入命令rmir??egistry 。 但是請確保從具有訪問類權限的目錄中啟動它。
最后一步是啟動服務,即運行遠程類的具體實現,在這種情況下,該類為ReportGeneratorImpl 。
到目前為止,我們已經創建并運行了服務。 現在,讓我們看看客戶端將如何使用它。 比薩公司所有者的報表應用程序將使用此服務來生成和檢查報表。 我們需要向將使用該服務的客戶端提供ReportGenerator接口和存根。 您可以簡單地交付存根以及服務中所需的任何其他類或接口。
package com.javacodegeeks.patterns.proxypattern.remoteproxy;import java.rmi.Naming;public class ReportGeneratorClient {public static void main(String[] args) {new ReportGeneratorClient().generateReport();}public void generateReport(){try {ReportGenerator reportGenerator = (ReportGenerator)Naming.lookup("rmi://127.0.0.1/PizzaCoRemoteGenerator");System.out.println(reportGenerator.generateDailyReport());} catch (Exception e) {e.printStackTrace();}}}上面的程序將具有以下輸出:
********************Location X Daily Report********************Location ID: 012Today's Date: Sun Sep 14 00:11:23 IST 2014Total Pizza Sell: 112Total Sale: $2534Net Profit: $1985***************************************************************上面的類進行命名查找,并檢索用于生成每日報告的對象。 您需要提供主機名的IP和用于綁定服務的名稱。 其余的看起來就像是一個常規的舊方法調用。
總之,遠程代理充當生活在不同JVM中的對象的本地代表。 代理上的方法調用導致該調用通過有線傳輸,遠程調用,并將結果返回給代理,然后返回給客戶端。
4.虛擬代理
虛擬代理模式是一種節省內存的技術,建議將對象創建推遲到需要時再進行。 它在創建對象時使用,在內存使用或所涉及的處理方面非常昂貴。 在典型的應用程序中,不同的對象組成功能的不同部分。 啟動應用程序時,它可能不需要其所有對象立即可用。 在這種情況下,虛擬代理模式建議將對象創建推遲到應用程序需要時再進行。 第一次創建的對象在應用程序中被引用,并且從該點開始重復使用同一實例。 這種方法的優點是應用程序啟動時間更快,因為不需要創建和加載所有應用程序對象。
假設您的應用程序中有一個Company對象,并且此對象在ContactList對象中包含該公司的雇員列表。 公司中可能有數千名員工。 從數據庫中加載Company對象以及ContactList對象中所有雇員的列表可能非常耗時。 在某些情況下,您甚至不需要雇員列表,但是您不得不等到公司及其雇員列表加載到內存中。
節省時間和內存的一種方法是避免在需要之前加載員工對象,而這是使用虛擬代理完成的。 該技術也稱為延遲加載,您僅在需要時才獲取數據。
package com.javacodegeeks.patterns.proxypattern.virtualproxy;public class Company {private String companyName;private String companyAddress;private String companyContactNo;private ContactList contactList ;public Company(String companyName,String companyAddress, String companyContactNo,ContactList contactList){this.companyName = companyName;this.companyAddress = companyAddress;this.companyContactNo = companyContactNo;this.contactList = contactList;System.out.println("Company object created...");}public String getCompanyName() {return companyName;}public String getCompanyAddress() {return companyAddress;}public String getCompanyContactNo() {return companyContactNo;}public ContactList getContactList(){return contactList;}}上面的Company類具有對ContactList接口的引用,該接口的真實對象僅在請求調用getContactList()方法時才加載。
package com.javacodegeeks.patterns.proxypattern.virtualproxy;import java.util.List;public interface ContactList {public List<Employee> getEmployeeList(); }ContactList接口僅包含一種方法getEmployeeList() ,該方法用于獲取公司的員工列表。
package com.javacodegeeks.patterns.proxypattern.virtualproxy;import java.util.ArrayList; import java.util.List;public class ContactListImpl implements ContactList{@Overridepublic List<Employee> getEmployeeList() {return getEmpList();}private static List<Employee>getEmpList(){List<Employee> empList = new ArrayList<Employee>(5);empList.add(new Employee("Employee A", 2565.55, "SE"));empList.add(new Employee("Employee B", 22574, "Manager"));empList.add(new Employee("Employee C", 3256.77, "SSE"));empList.add(new Employee("Employee D", 4875.54, "SSE"));empList.add(new Employee("Employee E", 2847.01, "SE"));return empList;}}上面的類將創建一個實際的ContactList對象,該對象將返回公司的員工列表。 僅在需要時才按需加載對象。
package com.javacodegeeks.patterns.proxypattern.virtualproxy;import java.util.List;public class ContactListProxyImpl implements ContactList{private ContactList contactList;@Overridepublic List<Employee> getEmployeeList() {if(contactList == null){System.out.println("Creating contact list and fetching list of employees...");contactList = new ContactListImpl();}return contactList.getEmployeeList();}}ContactListProxyImpl是代理類,它也實現ContactList并保存對實際ContactList對象的引用。 在方法getEmployeeList()的實現上,它將檢查contactList引用是否為null,然后將創建一個實際的ContactList對象,然后將對其調用getEmployeeList()方法以獲取員工列表。
Employee類看起來像這樣。
package com.javacodegeeks.patterns.proxypattern.virtualproxy;public class Employee {private String employeeName;private double employeeSalary;private String employeeDesignation;public Employee(String employeeName,double employeeSalary,String employeeDesignation){this.employeeName = employeeName;this.employeeSalary = employeeSalary;this.employeeDesignation = employeeDesignation;}public String getEmployeeName() {return employeeName;}public double getEmployeeSalary() {return employeeSalary;}public String getEmployeeDesignation() {return employeeDesignation;}public String toString(){return "Employee Name: "+employeeName+", EmployeeDesignation: "+employeeDesignation+", Employee Salary: "+employeeSalary;}}package com.javacodegeeks.patterns.proxypattern.virtualproxy;import java.util.List;public class TestVirtualProxy {public static void main(String[] args) {ContactList contactList = new ContactListProxyImpl();Company company = new Company("ABC Company", "India", "+91-011-28458965", contactList);System.out.println("Company Name: "+company.getCompanyName());System.out.println("Company Address: "+company.getCompanyAddress());System.out.println("Company Contact No.: "+company.getCompanyContactNo());System.out.println("Requesting for contact list");contactList = company.getContactList();List<Employee>empList = contactList.getEmployeeList();for(Employee emp : empList){System.out.println(emp);}}}上面的程序將具有以下輸出:
Company object created... Company Name: ABC Company Company Address: India Company Contact No.: +91-011-28458965 Requesting for contact list Creating contact list and fetching list of employees... Employee Name: Employee A, EmployeeDesignation: SE, Employee Salary: 2565.55 Employee Name: Employee B, EmployeeDesignation: Manager, Employee Salary: 22574.0 Employee Name: Employee C, EmployeeDesignation: SSE, Employee Salary: 3256.77 Employee Name: Employee D, EmployeeDesignation: SSE, Employee Salary: 4875.54 Employee Name: Employee E, EmployeeDesignation: SE, Employee Salary: 2847.01如您在TestVirtualProxy生成的輸出中所TestVirtualProxy ,首先我們創建了一個帶有代理ContactList對象的Company對象。 此時, Company對象保存一個代理引用,而不是真實的ContactList對象的引用,因此內存中沒有加載任何員工列表。 我們對公司對象進行了一些調用,然后使用getEmployeeList()方法從聯系人列表代理對象中請求雇員列表。 調用此方法時,代理對象將創建一個實際的ContactList對象并提供雇員列表。
5.保護代理
通常,應用程序中的對象相互交互以實現整體應用程序功能。 通常,大多數應用程序對象可由應用程序中的所有其他對象訪問。 有時,可能有必要根據對象的訪問權限將對象的可訪問性僅限制為一組有限的客戶端對象。 當客戶端對象嘗試訪問此類對象時,只有在客戶端可以提供適當的身份驗證憑據的情況下,客戶端才可以訪問該對象提供的服務。 在這種情況下,可以指定一個單獨的對象,負責驗證不同客戶端對象訪問實際對象時的訪問特權。 換句話說,每個客戶端都必須使用此指定對象成功進行身份驗證才能訪問實際的對象功能。 客戶端需要通過身份驗證才能訪問實際對象的此類對象可以稱為使用保護代理實現的對象身份驗證器。
返回到我們為披薩公司開發的ReportGenerator應用程序,所有者現在要求只有他才能生成每日報告。 沒有其他員工應該能夠這樣做。
為了實現此安全功能,我們使用了保護代理,該代理可以檢查試圖生成報告的對象是否為所有者; 在這種情況下,將生成報告,否則不會生成。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;public interface Staff {public boolean isOwner();public void setReportGenerator(ReportGeneratorProxy reportGenerator); }Staff接口由Owner和Employee類Owner ,并且該接口具有兩個方法: isOwner()返回一個boolean以檢查調用對象是否為所有者。 另一個方法用于設置ReportGeneratorProxy ,它是用于生成報告的保護代理, isOwner()方法返回true。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;public class Employee implements Staff{private ReportGeneratorProxy reportGenerator;@Overridepublic void setReportGenerator(ReportGeneratorProxy reportGenerator){this.reportGenerator = reportGenerator;}@Overridepublic boolean isOwner() {return false;}public String generateDailyReport(){try {return reportGenerator.generateDailyReport();} catch (Exception e) {e.printStackTrace();}return "";}}Employee類實現了Staff接口,因為它是一個雇員isOwner()方法,返回false 。 generateDailyReport()方法要求ReportGenerator生成每日報告。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;public class Owner implements Staff {private boolean isOwner=true;private ReportGeneratorProxy reportGenerator;@Overridepublic void setReportGenerator(ReportGeneratorProxy reportGenerator){this.reportGenerator = reportGenerator;}@Overridepublic boolean isOwner(){return isOwner;}public String generateDailyReport(){try {return reportGenerator.generateDailyReport();} catch (Exception e) {e.printStackTrace();}return "";}}Owner類看起來與Employee類幾乎相同,唯一的區別是isOwner()方法返回true 。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;public interface ReportGeneratorProxy {public String generateDailyReport(); }ReportGeneratorProxy充當保護代理,它只有一種用于生成報告的方法generateDailyReport() 。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;import java.rmi.Naming;import com.javacodegeeks.patterns.proxypattern.remoteproxy.ReportGenerator;public class ReportGeneratorProtectionProxy implements ReportGeneratorProxy{ReportGenerator reportGenerator;Staff staff;public ReportGeneratorProtectionProxy(Staff staff){this.staff = staff;}@Overridepublic String generateDailyReport() {if(staff.isOwner()){ReportGenerator reportGenerator = null;try {reportGenerator = (ReportGenerator)Naming.lookup("rmi://127.0.0.1/PizzaCoRemoteGenerator");return reportGenerator.generateDailyReport();} catch (Exception e) {e.printStackTrace();}return "";}else{return "Not Authorized to view report.";}} }上面的類是ReportGeneratorProxy的具體實現,其中包含對作為遠程代理的ReportGenerator接口的引用。 在generateDailyReport()方法中,它檢查工作人員是否正在引用所有者,然后要求遠程代理生成報告,否則它返回帶有“未授權查看報告”的字符串作為消息。
package com.javacodegeeks.patterns.proxypattern.protectionproxy;public class TestProtectionProxy {public static void main(String[] args) {Owner owner = new Owner();ReportGeneratorProxy reportGenerator = new ReportGeneratorProtectionProxy(owner);owner.setReportGenerator(reportGenerator);Employee employee = new Employee();reportGenerator = new ReportGeneratorProtectionProxy(employee);employee.setReportGenerator(reportGenerator);System.out.println("For owner:");System.out.println(owner.generateDailyReport());System.out.println("For employee:");System.out.println(employee.generateDailyReport());}}上面的程序將具有以下輸出:
For owner: ********************Location X Daily Report********************Location ID: 012Today's Date: Sun Sep 14 13:28:12 IST 2014Total Pizza Sell: 112Total Sale: $2534Net Profit: $1985*************************************************************** For employee: Not Authorized to view report.以上輸出清楚地表明,所有者可以生成報告,而員工則不能。 保護代理保護訪問生成報告的權限,并且僅允許授權對象生成報告。
6.何時使用代理模式
只要需要對對象的引用比簡單的指針更通用或更復雜,就可以使用代理。 這是代理模式適用的幾種常見情況:
7.其他代理
除了上面討論的三個主要代理外,還有其他一些代理。
8. JDK中的代理模式
以下情況是在JDK中使用代理模式的示例。
9.下載源代碼
這是關于代理模式的課程。 您可以在此處下載源代碼: ProxyPattern-Project.zip
翻譯自: https://www.javacodegeeks.com/2015/09/proxy-design-pattern.html
設計模式示例
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的设计模式示例_代理设计模式示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目前最具性价比的手机有哪些
- 下一篇: 苹果13充电时屏幕不灵敏怎么回事