从王者荣耀看设计模式(保护模式)

一.简介

打开王者荣耀,点击右上角头像标志可进入我的信息主页。在我的个人主页中,我可以可设置玩家的游戏昵称,性别,常用英雄。除此之外,我还可以查看自己得到别人点赞的次数(不能给自己点赞);其他玩家亦可访问我的主页,可以给我点赞,可以查看我设置的所有信息(不能更改)。

模式动机
在本实例中,使用保护代理来限制用户的使用权限。我可以修改做自己的主页信息却不能给自己点赞。其他玩家可以给我点赞却不能修改我的主页信息。

二.保护代理(控制用户的访问权限)

保护代理,是提供某些级别的保护,根据客户的权限和决定客户可否访问哪些特定的方法,所以保护代理可能只提供给客户部分接口。

三.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);}
}

运行结果:

七.源代码下载

从王者荣耀看设计模式(保护代理)

从王者荣耀看设计模式(保护代理模式)相关推荐

  1. 从王者荣耀看设计模式(虚拟代理模式)

    从王者荣耀看设计模式(虚拟代理模式) 一.简介 王者荣耀游戏设置了很多种游戏模式,比如:王者模拟战.无限乱斗.梦境大乱斗.火焰山大战等.当从王者荣耀的主界面进入各类模式的界面时,由于网络原因,会存在一 ...

  2. 从王者荣耀看设计模式(远程代理模式)

    从王者荣耀看设计模式(远程代理模式) 一.简介 每位王者荣耀玩家都有一个属于自己的游戏账号.为了提升游戏等级或者增加游戏体验感,会存在多个游戏玩家同时共享一个游戏账号的情况.当一位玩家使用账号正在游戏 ...

  3. 从王者荣耀看设计模式(六.状态模式)

    从王者荣耀看设计模式(状态模式) 一.简介 英雄项羽在敌方英雄的攻击下存在3种不同的状态. 1.在健康生命值下--普通状态,在每次被攻击时,当前生命值=剩余生命值-敌方英雄伤害值 2.在生命值低于某一 ...

  4. 从王者荣耀看设计模式(五.组合模式)

    从王者荣耀看设计模式(组合模式) 一.简介 在王者荣耀这款游戏中,我们可以在商店中购买英雄.英雄由法师.射手.打野等职业组合而成,其中各个职业由中国元素的英雄和外国元素的英雄组成,玩家可根据需要挑选并 ...

  5. 从王者荣耀看设计模式(一.策略模式)

    从王者荣耀看设计模式(策略模式) 一:简介 游戏开始前,玩家需要选择英雄,再根据所选择的阵容自由选择召唤师技能,游戏开始,玩家可以控制英雄进行普通攻击和使用召唤师技能攻击 二:策略模式 策略模式将可变 ...

  6. 从王者荣耀看设计模式(十.外观模式)

    ##从王者荣耀看设计模式(外观模式) 一.简介 王者荣耀是由多人协同开发而成,每个人负责游戏的一个或多个子功能,一个完整的功能是由很多已开发的子功能组合成的.我们要玩游戏时,只需要打开王者荣耀APP, ...

  7. 从王者荣耀看设计模式(二十二.备忘录模式)

    从王者荣耀看设计模式 一.简介 点击王者荣耀商店法术的装备栏,存在有"贤者的庇护"这一装备.在对战中,当玩家处于死亡状态时,会触发此装备的效果,能够让英雄重新复活. ┬┴┬┌─ ● ...

  8. 从王者荣耀看设计模式(十七.桥接模式)

    从王者荣耀看设计模式(桥接模式) 一.简介 王者荣耀是一款RGB游戏,玩家操控自己选择的英雄进行竞赛.在游戏正式开始前,玩家在挑选想要操作的英雄的同时,也会挑选适合该英雄的召唤师技能.召唤师技能是通用 ...

  9. 从王者荣耀看设计模式(四.简单工厂模式)

    从王者荣耀看设计模式(简单工厂模式) 一.简介 游戏开始前,玩家可从英雄池自由挑选将要出战的英雄 二.简单工厂模式 简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫 ...

最新文章

  1. Dz0724补丁补掉的一个xss+补掉的另外一个xss
  2. 你确定你真的喜欢编程吗??
  3. C#操作Access数据库 增删改查
  4. 唯一约束 mysql
  5. 18.海量分布式存储系统 Doris 的高可用架构设计分析
  6. java收到邮件后短信提醒_java邮件发送和短信发送(二)
  7. 【转】Java多线程面试问题集锦
  8. c语言联盟,程序设计(C语言)(山东联盟)
  9. 个人微信api接口调用,微信好友收发消息
  10. 2018通达信l2服务器源码,很后悔购买了通达信L2看盘软件,大家不要再买进这个软件了...
  11. RUBY发送验证码通知短信(互亿无线)
  12. IE 浏览器旧版本下载
  13. comment hive_Hive中基本语法
  14. lol8月7号服务器维护,LOL8月7日更新了什么内容 8.15新版本更新维护公告
  15. 无损批量合并视频 附工具
  16. Unity 制作旋转门 推拉门 柜门 抽屉 点击自动开门效果 开关门自动播放音效 (附带编辑器扩展代码)
  17. Game Hacking Fundamentals 学习笔记1
  18. 怎么把图片转gif表情包?
  19. js常见面试题及简单回答
  20. 梦幻西游网络诊断找不到服务器,《梦幻西游》电脑版出现网络故障 受影响者可寻找恢复使者进行恢复...

热门文章

  1. windows下protoc的安装配置
  2. 扩展欧几里得算法证明及代码
  3. Scratch二次开发:关于增加的扩展导出sb3文件后,导入文件失败问题
  4. 使用文本卷积神经网络,并使用MovieLens数据集完成电影推荐的任务
  5. Hi3531DV200 Hi3535AV100 Hi3559AV100 Hi3519AV100性能比对选型参考
  6. WordPress主题:知更鸟Begin5.2免授权版
  7. 如何使用网络调试助手调试UDP
  8. 《八股文》计算机网络,温故而知新
  9. 省市区县三级联动后台接口设计
  10. R语言重读微积分(一):极限