提到对象之间的通讯,我想到了设计模式中的责任链模式,他为请求创建了一个接受者对象的链.这种模式通过请求类型,在责任链上的对象来判断是否进行处理,对请求的发送者和接受者进行解耦
下面是转自菜鸟教程的实例UML图
但是我的项目中,对象请求和处理对象一般是一一对应的,并没有用到责任链模式,而是以下的几种处理方法:
- 类的静态变量
- 请求者对象包含了处理者对象的引用并直接调用其处理函数
- 请求者对象包含了处理者对象的引用但处理函数只改变标记变量,处理者对象自动检查标记变量判断是否进行处理
- 创建事件的监听器,事件发生时回调处理者的处理函数,并将请求者对象作为参数传入
下面我以项目中的代码来展示这四种实现
类的静态变量
将要请求的变量设置为类的静态变量,这样所有的请求者可以直接通过类名来访问,而不需要包含处理者的对象
// 分数
private static Integer score = 0;
// 显示分数的标签
private static Label scoreLabel;
public static void reSetScore(){
score = 0;
}
/**
* 加分
*/
public static void addScore(int value){
score += value;
scoreLabel.setText(String.format("%06d",score));
}
这种方法的好处是实现简单,且操作的结果可以直接反应出来.但静态变量相当于全局变量破坏了封装性,在多个请求者进行调用时,会在在安全性方面大大降低
请求者直接调用处理函数
当请求者对象中的逻辑发出了请求时,直接调用内部响应者对象的处理函数,在下面例子中,当主类判断马里奥应该死亡时,便直接调用了马里奥对象(die())方法
/**
* 马里奥死亡
*/
public void die(){
if(!isDead()){
Mario.manager.get("assets/audio/music/mario_music.ogg", Music.class).stop();
Mario.manager.get("assets/audio/sounds/mariodie.wav", Sound.class).play();
marioIsDead = true; //设置死亡状态
Filter filter = new Filter();
filter.maskBits = Mario.NOTHING_BIT; //不可碰撞
for (Fixture fixture : b2body.getFixtureList()) {
fixture.setFilterData(filter);
}
// 向上的速度
b2body.applyLinearImpulse(new Vector2(0, 4f), b2body.getWorldCenter(), true);
}
}
这种实现的好处是逻辑简单,逻辑调用与实际执行是同一个方法,实现也比较简单,但这种方法提高了函数功能的复杂程度.改变人物图像的功能应在特定函数中执行,而在(die())函数中却掺杂了这一功能.
请求者调用改变其标记变量
该方式与上种方式其请求者对象都含有与其对应的相应者的引用,但该方式在发出请求时,响应者只改变其内部的标记变量,在他自身的更新函数执行时才真正的处理该请求
下面例子中马里奥吃到蘑菇,边调用马里奥的(grow())方法,而在该方法中只对其标记变量进行改变,在更新时根据标记变量,来调用生成大马里奥的函数
/**
* 马里奥增长
*/
public void grow() {
// 当前为大马里奥则不需要变大
if(marioIsBig) return;
runGrowAnimation = true;
marioIsBig = true;
timeToDefineBigMario = true;
setBounds(getX(), getY(), getWidth(), getHeight() * 2);
Mario.manager.get("assets/audio/sounds/powerup.wav", Sound.class).play();
}
// 更新函数中生成大马里奥的代码
if (timeToDefineBigMario) {
// 实际重新生成大马里奥的逻辑
defineMario(b2body.getPosition().add(0,10/Mario.PPM),true);
}
监听器处理
我们在事件的发生源添加一个监听器,当事件发生时由监听器捕捉,并传递给响应者
下面是个处理刚体碰撞的例子,我们为所有能产生碰撞的对象中添加碰撞监听器,发生碰撞时通过刚体自带的碰撞掩码来确定碰撞源,之后调用不同碰撞源的处理函数
Fixture fixA = contact.getFixtureA(); // A碰撞源
Fixture fixB = contact.getFixtureB(); // B碰撞源
// 获得碰撞码
int cDef = fixA.getFilterData().categoryBits | fixB.getFilterData().categoryBits;
// 敌人 与 敌人
// 敌人 调用 敌人碰撞方法 并传入另一碰撞源
case Mario.ENEMY_BIT :
((Enemy)fixA.getUserData()).onEnemyHit((Enemy)fixB.getUserData());
((Enemy)fixB.getUserData()).onEnemyHit((Enemy)fixA.getUserData());
break;
该方法是java常见的事件处理方法,在为对象添加监听器后,在监听器中处理事件,但在本例中将重新确认事件源后再调用事件源本身的处理函数,看起来是多余. 但这种做法一是为了适配碰撞监听器,二是保持了模块的内聚,使得碰撞处理模块更加通用和可扩充.
以上大致是我项目中的对象通讯的实践,说是对象的通讯,其实也就只是函数之间的互相调用,加上自己的一些想法算是一个总结.