·
观察者模式在王者荣耀中的应用
·
一、简述
王者荣耀是一款5v5的团队竞技游戏,在一局游戏当中,必要的系统提示有利于玩家对实时的战况有更好地把握。比如,当游戏开局时,系统会提示“敌军还有5秒到达战场,请做好准备”;当有英雄被击杀时或者敌我双方防御塔被摧毁时,我方队友和敌方收到的系统提示是不同的。
于是,此类问题就可以用观察者模式很好的实现当防御塔被摧毁后敌我双方英雄分别收到不同的消息的结果。这里再简单描述一下这个具体问题:当敌方高低防御塔被我方娜可露露摧毁时,我方全部队友收到系统提示消息“(娜可露露)摧毁敌方防御塔”,而敌方英雄收到的则是“(娜可露露)摧毁我方防御塔”。
这里所述的
“摧毁防御塔”相当于观察者模式中的一个具体“主题”
“敌我双方每位英雄”相当于观察者模式中的一个具体“观察者”
二、观察者模式(Observer Pattern)
观察者模式理解:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。
观察者模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
观察者模式结构中的四种角色:
主题(Subject):是一个接口,规定了具体主题需要实现的方法
观察者(Observer): 是一个接口,规定了具体观察者用来更新数据的方法
具体主题(ConcreteSubject) :是实现主题接口类的一个实例
具体观察者(ConcreteObserver):是实现观察者接口类的一个实例
观察者模式的UML类图:
观察者模式的优缺点:
优点:
①容易扩展
②满足“开—闭原则”
③具体主题和具体观察者是松耦合关系
缺点:
①多级触发效率较低
②因为是顺序执行,一个观察者卡壳,会影响整体的执行效率
三、王者荣耀角度下实现观察者模式结构图及代码
实现此观察者模式的UML类图
eclipse结构图
主函数【应用(Application)】
package angle_observer;
import angle_observer.TeammatesReceive;
import angle_observer.TurretBeenDestroyed;
/*
使用了观察者模式中所涉及的类,应用程序在使用观察者模式时,需要创建具体主题和该主题的观察者
当系统消息提示“敌方防御塔被摧毁”时,我方和敌方分别得到内容不同的通知
*/
public class Application {
public static void main(String args[]){
TurretBeenDestroyed message=new TurretBeenDestroyed(); //具体主题message
System.out.println(" ");
TeammatesReceive teammates=new TeammatesReceive(message,"收到系统消息"); //具体观察者teammates
EnemiesReceive enemies=new EnemiesReceive(message,"收到系统消息"); //具体观察者enemies
message.giveNewMess("“敌方防御塔被摧毁”"); //具体主题给出新信息
message.notifyObservers(); //具体主题通知信息
}
}
主题接口
Subject.java
package angle_observer;
import angle_observer.Observer;
/*
角色1:主题:是一个接口,规定了具体主题需要实现的方法
如:添加、删除观察者以及通知观察者更新数据的方法
*/
public interface Subject {
public void addObserver(Observer o); //规定了具体主题需要实现的添加观察者更新数据的方法
public void deleteObserver(Observer o); //规定了具体主题需要实现的删除观察者更新数据的方法
public void notifyObservers(); //规定了具体主题需要实现的通知观察者更新数据的方法
}
观察者接口
Observer.java
package angle_observer;
/*
角色2:观察者:是一个接口,规定了具体观察者用来更新数据的方法
*/
public interface Observer {
public void hearMessage(String mess); //相当于观察者模式类图中的update()方法
//要求观察者都通过实现hearMessage()方法(模拟接收系统消息)来更新数据
}
具体主题
TurretBeenDestroyed.java
package angle_observer;
/*
角色3:具体主题:是实现主题接口类的一个实例(包含可能经常发生变化的数据)
具体主题需要使用一个集合如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
*/
import java.util.ArrayList;
public class TurretBeenDestroyed implements Subject{
String mess; //用来表示队友接收到的消息内容
boolean changed;
ArrayList<Observer>informationList; //存放观察者引用的数组线性表
TurretBeenDestroyed(){
informationList=new ArrayList<Observer>();
mess="";
changed=false;
}
public void addObserver(Observer o){
if(!(informationList.contains(o)))
informationList.add(o); //把观察者的引用添加到数组线性表
}
public void deleteObserver(Observer o){
if(informationList.contains(o))
informationList.remove(o);
}
public void notifyObservers(){
if(changed){ //通知所有的观察者
for(int i=0;i<informationList.size();i++){ //遍历具体主题中用来存放观察者引用的集合
Observer observer=informationList.get(i);
observer.hearMessage(mess); //让观察者接收系统消息(执行观察者接口规定更新数据的方法)
}
changed=false;
}
}
public void giveNewMess(String str){
if(str.equals(mess))
changed=false;
else{
mess=str;
changed=true;
}
}
}
具体观察者
EnemiesReceive.java
package angle_observer;
/*
角色4.2:具体观察者:是实现观察者接口类的一个实例(包含有可以存放具体主题引用的主题接口变量)
让具体主题引用或删除自己,使得自己成为或不再是它的观察者
*/
import java.io.*;
import java.util.regex.*;
public class EnemiesReceive implements Observer{ //实现观察者接口的第一个类EnemiesReceive
Subject subject;
File myFile;
EnemiesReceive(Subject subject,String fileName){
this.subject=subject;
subject.addObserver(this); //使当前实例成为subject所引用的具体主题的观察者
myFile=new File(fileName);
}
public void hearMessage(String heardMess){ //EnemiesReceive类的实例调用hearMessage(String heardMess)方法
try{
boolean boo=heardMess.contains("敌方");
if(boo){
RandomAccessFile out=new RandomAccessFile(myFile,"rw"); //若参数引用的字符串中包含有“敌方”,就将信息保存到一个文件中
out.seek(out.length());
byte[]b=heardMess.getBytes();
out.write(b);
System.out.println("-------------------------------------------");
System.out.print("【敌方】裴擒虎"+myFile.getName());
System.out.println("“我方防御塔被摧毁”");
System.out.print("【敌方】不知火舞"+myFile.getName());
System.out.println("“我方防御塔被摧毁”");
System.out.print("【敌方】钟馗"+myFile.getName());
System.out.println("“我方防御塔被摧毁”");
System.out.print("【敌方】李元芳"+myFile.getName());
System.out.println("“我方防御塔被摧毁”");
System.out.print("【敌方】典韦"+myFile.getName());
System.out.println("“我方防御塔被摧毁”");
}
}
catch(IOException exp){
System.out.println(exp.toString());
}
}
}
TeammatesReceive.java
package angle_observer;
/*
角色4:具体观察者:是实现观察者接口类的一个实例(包含有可以存放具体主题引用的主题接口变量)
让具体主题引用或删除自己,使得自己成为或不再是它的观察者
*/
import java.io.*;
public class TeammatesReceive implements Observer{ //实现观察者接口的第一个类TeammatesReceive
Subject subject;
File myFile;
TeammatesReceive(Subject subject,String fileName){
this.subject=subject;
subject.addObserver(this); //使当前实例成为subject所引用的具体主题的观察者
myFile= new File(fileName);
}
public void hearMessage(String heardMess){ //TeammatesReceive类的实例调用hearMessage(String heardMess)方法
try{
RandomAccessFile out=new RandomAccessFile(myFile,"rw"); //将参数引用的字符串保存到一个文件中
out.seek(out.length());
byte[]b=heardMess.getBytes();
out.write(b); //更新文件内容
System.out.println("【系统消息】敌方防御塔被摧毁");
System.out.println("-------------------------------------------");
System.out.print("【我方】娜可露露"+myFile.getName());
System.out.println(heardMess);
System.out.print("【我方】上官婉儿"+myFile.getName());
System.out.println(heardMess);
System.out.print("【我方】鬼谷子"+myFile.getName());
System.out.println(heardMess);
System.out.print("【我方】百里玄策"+myFile.getName());
System.out.println(heardMess);
System.out.print("【我方】马可波罗"+myFile.getName());
System.out.println(heardMess);
}
catch(IOException exp){
System.out.println(exp.toString());
}
}
}
运行结果截图
这是前段时间更新的“命令模式”在王者荣耀中的应用:
“命令模式”在王者荣耀中的应用
现已更新 “命令模式 ” 和 “观察者模式 ”
余下的21种程序设计模式不定时更新,敬请期待
感谢阅读