代理模式
定義
爲其他對象提供一種代理以控制對這個對象的訪問。
代理模式也叫委託模式,許多模式,如狀態模式、策略模式、訪問者模式、本質上都是在特殊的場景下使用了代理模式。
Subject抽象主題角色
抽象主體類可以是抽象類也可以是接口,是一個最普通的業務定義,沒有特殊的要求RealSubject具體主題角色
也叫委託角色、被代理角色,是業務邏輯的具體執行者Proxy代理主題角色
也叫做委託類、代理類。它負責對真實角色的應用,把所有抽象主題類定義的方法限制委託給真實主題角色實現,並且在真實主題角色處理完畢前後做預處理和善後處理工作。
public interface Subject {
//定義一個方法
public void request();
}
public class RealSubject implements Subject {
//實現方法
public void request() {
//業務邏輯處理,真正的業務邏輯處理類
}
}
public class Proxy implements Subject {
//要代理哪個實現類
private Subject subject = null;
//默認被代理者
public Proxy(){
this.subject = new Proxy();
}
//通過構造函數傳遞代理者
public Proxy(Object...objects ){
}
//實現接口中定義的方法
public void request() {
this.before();
this.subject.request();
this.after();
}
//預處理
private void before(){
//do something
}
//善後處理
private void after(){
//do something
}
}
舉例
我們平常在玩遊戲打BOSS的時候,抽象爲程序:
首先我們定義了一個接口,定義了遊戲中打BOSS的基本操作,比如登錄,殺死BOSS,升級。定義GamePlayer實現接口,完成具體操作。
public interface IGamePlayer {
//登錄遊戲
public void login(String user,String password);
//殺怪,網絡遊戲的主要特色
public void killBoss();
//升級
public void upgrade();
}
public class GamePlayer implements IGamePlayer {
private String name = "";
//通過構造函數傳遞名稱
public GamePlayer(String _name){
this.name = _name;
}
//打怪,最期望的就是殺老怪
public void killBoss() {
System.out.println(this.name + "在打怪!");
}
//進遊戲之前你肯定要登錄吧,這是一個必要條件
public void login(String user, String password) {
System.out.println("登錄名爲"+user+"的用戶"+this.name+"登錄成功!");
}
//升級,升級有很多方法,花錢買是一種,做任務也是一種
public void upgrade() {
System.out.println(this.name + " 又升了一級!");
}
}
public class Client {
public static void main(String[] args) {
//定義一個癡迷的玩家
IGamePlayer player = new GamePlayer("張三");
//開始打遊戲,記下時間戳
System.out.println("開始時間是:2009-8-25 10:45");
player.login("zhangSan", "password");
//開始殺怪
player.killBoss();
//升級
player.upgrade();
//記錄結束遊戲時間
System.out.println("結束時間是:2009-8-26 03:40");
}
}
程序記錄了遊戲的開始時間以及殺死BOSS升級結束時間,但是我們發現升級太特喵的慢了,而且很無聊啊,於是我們請了代練:
我們請了遊戲代練GamePlayerProxy 。
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//通過構造函數傳遞要對誰進行代練
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
//代練殺怪
public void killBoss() {
this.gamePlayer.killBoss();
}
//代練登錄
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
//代練升級
public void upgrade() {
this.gamePlayer.upgrade();
}
}
但是代練是幫我們進行升級,同時它的遊戲操作需要遵循遊戲規範,因此我們仍然實現IGamePlayer 接口,並在實例化的同時將我們的遊戲實體傳入,有代練進行操作。
代理模式的優點
職責清晰
真實的角色就是實現實際的業務邏輯,不用關心其他非本職責的事務,通過後期的代理完成一件事務,附帶的結果就是編程簡潔清晰。
高擴展性
具體主題角色是隨時都會發生變化的,只要它實現了接口,甭管它如何變化,都逃不脫如來佛的手掌(接口),那我們的代理類完全就可以在不做任何修改的情況下使用。
智能化
代理模式的使用場景
代理模式的使用是爲了減少自身的職責,本身只負責具體的業務實現,而一些瑣碎的事情則交給委託者進行實現,最經典的Spring AOP
代理模式的擴展
普通代理
普通代理要求客戶端只能訪問代理角色,而不能訪問真的角色。
我們進行重構,在GamePlayer以及GamePlayerProxy增加構造函數。
public class GamePlayer implements IGamePlayer {
private String name = "";
//構造函數限制誰能創建對象,並同時傳遞姓名
public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception{
if(_gamePlayer == null ){
throw new Exception("不能創建真實角色!");
}else{
this.name = _name;
}
}
//打怪,最期望的就是殺老怪
public void killBoss() {
System.out.println(this.name + "在打怪!");
}
//進遊戲之前你肯定要登錄吧,這是一個必要條件
public void login(String user, String password) {
System.out.println("登錄名爲"+user + "的用戶" + this.name + "登錄成功!");
}
//升級,升級有很多方法,花錢買是一種,做任務也是一種
public void upgrade() {
System.out.println(this.name + " 又升了一級!");
}
}
我們在構造函數中,傳遞一個IGamePlayer對象,檢查誰來創建對象,當然也可以進行類名的校驗,自行進行擴展。
至此我們代理,在進行創建的時候,只需要傳遞用戶名,然後在構造函數傳遞自身進行創建被代理對象即可。
強制代理
強制代理是通過被代理對象,找到代理對象。比如:我們去找某個明星做活動,我們想要繞過代理進行直接溝通,但是當我們找到了該演員,卻還是被告知需要找
我們在接口中,增加getProxy()方法,並在GamePlayer實現中, 去實例化指定的代理類,然後在KillBoss等方法中,判斷是否有代理類,沒有則要求通過指定的代理類進行訪問。
public class GamePlayer implements IGamePlayer { private String name = "";
//我的代理是誰
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = _name;
}
//找到自己的代理
public IGamePlayer getProxy(){
this.proxy = new GamePlayerProxy(this); return this.proxy;
}
//打怪,最期望的就是殺老怪
public void killBoss() {
if(this.isProxy()){ System.out.println(this.name + "在打怪!"); }else{
System.out.println("請使用指定的代理訪問");
}
}
在上述中,如果我們直接新建玩家,則會要求我們訪問指定的代理。
public class GamePlayerProxy implements IGamePlayer { private IGamePlayer gamePlayer = null;
//構造函數傳遞用戶名
public GamePlayerProxy(IGamePlayer _gamePlayer){ this.gamePlayer = _gamePlayer;
}
//代練殺怪
public void killBoss() { this.gamePlayer.killBoss();
}
//代練登錄
public void login(String user, String password) { this.gamePlayer.login(user, password);
}
//代練升級
public void upgrade() { this.gamePlayer.upgrade();
}
//代理的代理暫時還沒有,就是自己
public IGamePlayer getProxy(){
return this;
}
}