从王者荣耀看设计模式(保护代理模式)
从王者荣耀看设计模式(保护模式)
一.简介
打开王者荣耀,点击右上角头像标志可进入我的信息主页。在我的个人主页中,我可以可设置玩家的游戏昵称,性别,常用英雄。除此之外,我还可以查看自己得到别人点赞的次数(不能给自己点赞);其他玩家亦可访问我的主页,可以给我点赞,可以查看我设置的所有信息(不能更改)。
模式动机
在本实例中,使用保护代理来限制用户的使用权限。我可以修改做自己的主页信息却不能给自己点赞。其他玩家可以给我点赞却不能修改我的主页信息。
二.保护代理(控制用户的访问权限)
保护代理,是提供某些级别的保护,根据客户的权限和决定客户可否访问哪些特定的方法,所以保护代理可能只提供给客户部分接口。
三.Java动态代理
Java在java.lang.reflect包中有自己的代理支持,利用这个包可以在运行时动态地创建一个代理类,实现一个或多个接口。并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时创建的,我们称这个Java技术为:动态代理
(
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
java的反射机制
我们需要利用Java的反射机制实现动态代理来创建保护代理。但在这之前,让我们先看一下类图,了解一下动态代理。
因为Java已经为我们创建了Proxy类,所以只需要有方法来告诉Proxy类要做什么。不能像以前一样把代码放进Proxy类中,以为Proxy不是我们直接实现的。既然代码不能放到Proxy类中,那么需要放到哪里?放在InvocationHandler中,InvocationHandler的工作是响应代理的任何调用。可以把InvocationHandler想成是代理收到方法调用后,请求做实际工作的对象。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
@param proxy:指代我们所代理的那个真实对象
@param method:指代的是我们所要调用真实对象的某个方法的Method对象
@param args:指代的是调用真实对象某个方法时接收的参数。
(_−)☆o(´`)oo(╥﹏╥)oヽ(ー_ー)ノ(•́へ•́╬)ψ(*`ー´)ψ!!!∑(゚Д゚ノ)ノ|ू・ω・` )
在让我们看看Proxy这个类:
Proxy provides static methods for creating dynamic proxy classes and instances, and is also the superclass of all dynamic proxy classes created by those methods.
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多方法,但是我们用的最多的就是newProxyInstance这个方法:
public static Object newProxyInstance(ClassLoader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,让我们来看看这三个参数所代表的意义:
public static Object newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException
@param loader:一个ClassLoader对象,定义了那个ClassLoader对象来对生成的代理对象进行加载
@param interface:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么借口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用遮住接口中的方法了。**我们给代理对象提供了一组什么借口,那么我这个代理对象就会实现这组接口)
@param h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
四.结构图
五.设计类图
六.代码实现
步骤一:编写通用方法接口以及接口实现
保护代理模式目标是实现对访问类的控制
PersonBean类(主题接口)
package com.practice.Player;
/** 这个接口可以设置和取得人的名字、性别、常用英雄和赞数量*/
public interface PersonBean {String getPlayerID();String getGender();String getCommonHero();int getYesNumber();void setPlayerID(String PlayerID);void setGender(String gender);void setCommonHero(String CommonHero);void setYesNumber();void setAllCount(int Count);
}
PersonBeanImpl类(主题类)
package com.practice.Person.Impl;import com.practice.Player.PersonBean;public class PersonBeanImpl implements PersonBean {String playerID;String gender;String CommonHero;int yesAllCount;public String getPlayerID() {return playerID;}public String getGender() {return gender;}public String getCommonHero() {return CommonHero;}public int getYesNumber() {return yesAllCount;}public void setPlayerID(String playerID) {this.playerID = playerID;}public void setGender(String gender) {this.gender = gender;}public void setCommonHero(String CommonHero) {this.CommonHero = CommonHero;}public void setYesNumber() {yesAllCount++;}public void setAllCount(int Count) {this.yesAllCount = Count;}
}
步骤二:创建两个InvocationHandler
InvocationHandler实现了代理的行为,Java负责创建真实代理类和对象。我们只需提供在方法调用发生时知道做什么的handler。我们需要写两个InvocationHandler(调用处理器),其中OwnerHandler类给调用者使用.NonOwnerHandler给非拥有者使用。InvocationHandler::当代理方法被调用时,代理就会把这个调用转发给InvocaionHandler,但是这并不是通过调用InvocationHandler的对应方法做到的。而是给拥有者使用的OwnerHandler类
OwnerInvacationHandler类(给拥有者使用)
package com.practice.Proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;import com.practice.Player.PersonBean;public class OwnerInvocationHandler implements InvocationHandler {PersonBean person;public OwnerInvocationHandler(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("setYesNumber")) {throw new IllegalAccessException();}else if(method.getName().startsWith("set")) {return method.invoke(person, args);}}catch(InvocationTargetException e) {e.printStackTrace();}return null;}}
NonOwnerInvocationHandler类(给非拥有者使用)
package com.practice.Proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;import com.practice.Player.PersonBean;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("setYesNumber")){return method.invoke(person, args);}else if(method.getName().startsWith("set")) {throw new IllegalAccessException();}}catch(InvocationTargetException e) {e.printStackTrace();}// TODO Auto-generated method stubreturn null;}}
步骤三. 创建Proxy类并实例化Proxy对象
开始编写一个以PersonBean为参数,并知道如何为PersonBean对象创建拥有者代理的方法,也就是说,我们要创建一个代理,将它的方法调用转发给OwnerInvocationHandler
获取拥有者方法代理
PersonBean getOwnerProxy(PersonBean person) {// 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)return (PersonBean) Proxy.newProxyInstance( person.getClass().getClassLoader(), // 将personBean的类载入器当作参数person.getClass().getInterfaces(), // 代理需要实现的接口new OwnerInvocationHandler(person)); // 调用非拥有者的处理器}
获取非拥有者方法代理
PersonBean getNonOwnerProxy(PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person));}
步骤四.测试类
测试配对服务系统。利用适当的代理包装任何PersonBean对象
MatchMakingTestDrive类
package com.practice.Test;
import java.lang.reflect.*;
import java.util.*;import com.practice.Person.Impl.PersonBeanImpl;
import com.practice.Player.PersonBean;
import com.practice.Proxy.NonOwnerInvocationHandler;
import com.practice.Proxy.OwnerInvocationHandler;public class MatchMakingTestDrive {// 实例变量, 当作是保存顾客的“数据库”Hashtable<String, PersonBean> datingDB = new Hashtable<String, PersonBean>();public static void main(String[] args) {MatchMakingTestDrive test = new MatchMakingTestDrive();test.drive();}public MatchMakingTestDrive() {// 在构造器中初始化数据库initializeDatabase();}public void drive() {PersonBean MW = getPersonFromDatabase("妙乌"); //从数据库中取出一个人PersonBean ownerProxy = getOwnerProxy(MW); // 创建这个人的拥有者代理System.out.println("账号拥有者的游戏ID " + ownerProxy.getPlayerID()); // 输出玩家昵称ownerProxy.setCommonHero("王昭君"); // 使用拥有者代理来设置自己常用英雄System.out.println("账号拥有者设置游戏常用英雄");System.out.println("账号拥有者常用的英雄:"+ownerProxy.getCommonHero());try {// 尝试用拥有者代理来给自己点赞ownerProxy.setYesNumber();} catch (Exception e) {// 如果给自己点赞会出错System.out.println("账号拥有者不能够给自己点赞");}System.out.println("账号总赞数为: " + ownerProxy.getYesNumber());System.out.println();// 创建一个非拥有者的代理PersonBean nonOwnerProxy = getNonOwnerProxy(MW);System.out.println("账号拥有者的游戏ID " + nonOwnerProxy.getPlayerID());try {// 尝试用非拥有者代理来设置常用英雄nonOwnerProxy.setCommonHero("妲己");} catch (Exception e) {// 不可以给别人设置常用英雄System.out.println("非拥有者不能设置别人的常用英雄");}// 可以给别人点赞nonOwnerProxy.setYesNumber();// 查看信息System.out.println("非拥有者玩家查看账号所有者信息:");System.out.println("昵称:" + nonOwnerProxy.getPlayerID() + "\t性别:" + nonOwnerProxy.getGender()+ "\t常用英雄:" + nonOwnerProxy.getCommonHero());System.out.println("非拥有者玩家点赞");System.out.println("总赞数为: " + nonOwnerProxy.getYesNumber());}// 此方法需要一个person对象作为参数,然后返回该对象的代理// 因为代理和主题有相同的接口,所以我们返回接口PersonBeanPersonBean getOwnerProxy(PersonBean person) {// 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)return (PersonBean) Proxy.newProxyInstance( person.getClass().getClassLoader(), // 将personBean的类载入器当作参数person.getClass().getInterfaces(), // 代理需要实现的接口new OwnerInvocationHandler(person)); // 调用非拥有者的处理器}PersonBean getNonOwnerProxy(PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person));}PersonBean getPersonFromDatabase(String name) {return (PersonBean)datingDB.get(name);}// 初始化“数据库”void initializeDatabase() {PersonBean MW = new PersonBeanImpl();MW.setPlayerID("妙乌");MW.setGender("男");MW.setCommonHero("狄仁杰");MW.setAllCount(7);datingDB.put(MW.getPlayerID(), MW);PersonBean ZX = new PersonBeanImpl();ZX.setPlayerID("Kelly Klosure");ZX.setCommonHero("ebay, movies, music");ZX.setYesNumber();datingDB.put(ZX.getPlayerID(), ZX);}
}
运行结果:
七.源代码下载
从王者荣耀看设计模式(保护代理)
从王者荣耀看设计模式(保护代理模式)相关推荐
- 从王者荣耀看设计模式(虚拟代理模式)
从王者荣耀看设计模式(虚拟代理模式) 一.简介 王者荣耀游戏设置了很多种游戏模式,比如:王者模拟战.无限乱斗.梦境大乱斗.火焰山大战等.当从王者荣耀的主界面进入各类模式的界面时,由于网络原因,会存在一 ...
- 从王者荣耀看设计模式(远程代理模式)
从王者荣耀看设计模式(远程代理模式) 一.简介 每位王者荣耀玩家都有一个属于自己的游戏账号.为了提升游戏等级或者增加游戏体验感,会存在多个游戏玩家同时共享一个游戏账号的情况.当一位玩家使用账号正在游戏 ...
- 从王者荣耀看设计模式(六.状态模式)
从王者荣耀看设计模式(状态模式) 一.简介 英雄项羽在敌方英雄的攻击下存在3种不同的状态. 1.在健康生命值下--普通状态,在每次被攻击时,当前生命值=剩余生命值-敌方英雄伤害值 2.在生命值低于某一 ...
- 从王者荣耀看设计模式(五.组合模式)
从王者荣耀看设计模式(组合模式) 一.简介 在王者荣耀这款游戏中,我们可以在商店中购买英雄.英雄由法师.射手.打野等职业组合而成,其中各个职业由中国元素的英雄和外国元素的英雄组成,玩家可根据需要挑选并 ...
- 从王者荣耀看设计模式(一.策略模式)
从王者荣耀看设计模式(策略模式) 一:简介 游戏开始前,玩家需要选择英雄,再根据所选择的阵容自由选择召唤师技能,游戏开始,玩家可以控制英雄进行普通攻击和使用召唤师技能攻击 二:策略模式 策略模式将可变 ...
- 从王者荣耀看设计模式(十.外观模式)
##从王者荣耀看设计模式(外观模式) 一.简介 王者荣耀是由多人协同开发而成,每个人负责游戏的一个或多个子功能,一个完整的功能是由很多已开发的子功能组合成的.我们要玩游戏时,只需要打开王者荣耀APP, ...
- 从王者荣耀看设计模式(二十二.备忘录模式)
从王者荣耀看设计模式 一.简介 点击王者荣耀商店法术的装备栏,存在有"贤者的庇护"这一装备.在对战中,当玩家处于死亡状态时,会触发此装备的效果,能够让英雄重新复活. ┬┴┬┌─ ● ...
- 从王者荣耀看设计模式(十七.桥接模式)
从王者荣耀看设计模式(桥接模式) 一.简介 王者荣耀是一款RGB游戏,玩家操控自己选择的英雄进行竞赛.在游戏正式开始前,玩家在挑选想要操作的英雄的同时,也会挑选适合该英雄的召唤师技能.召唤师技能是通用 ...
- 从王者荣耀看设计模式(四.简单工厂模式)
从王者荣耀看设计模式(简单工厂模式) 一.简介 游戏开始前,玩家可从英雄池自由挑选将要出战的英雄 二.简单工厂模式 简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫 ...
最新文章
- Dz0724补丁补掉的一个xss+补掉的另外一个xss
- 你确定你真的喜欢编程吗??
- C#操作Access数据库 增删改查
- 唯一约束 mysql
- 18.海量分布式存储系统 Doris 的高可用架构设计分析
- java收到邮件后短信提醒_java邮件发送和短信发送(二)
- 【转】Java多线程面试问题集锦
- c语言联盟,程序设计(C语言)(山东联盟)
- 个人微信api接口调用,微信好友收发消息
- 2018通达信l2服务器源码,很后悔购买了通达信L2看盘软件,大家不要再买进这个软件了...
- RUBY发送验证码通知短信(互亿无线)
- IE 浏览器旧版本下载
- comment hive_Hive中基本语法
- lol8月7号服务器维护,LOL8月7日更新了什么内容 8.15新版本更新维护公告
- 无损批量合并视频 附工具
- Unity 制作旋转门 推拉门 柜门 抽屉 点击自动开门效果 开关门自动播放音效 (附带编辑器扩展代码)
- Game Hacking Fundamentals 学习笔记1
- 怎么把图片转gif表情包?
- js常见面试题及简单回答
- 梦幻西游网络诊断找不到服务器,《梦幻西游》电脑版出现网络故障 受影响者可寻找恢复使者进行恢复...