马里奥项目中对象直接通讯小结

提到对象之间的通讯,我想到了设计模式中的责任链模式,他为请求创建了一个接受者对象的链.这种模式通过请求类型,在责任链上的对象来判断是否进行处理,对请求的发送者和接受者进行解耦
下面是转自菜鸟教程的实例UML图

但是我的项目中,对象请求和处理对象一般是一一对应的,并没有用到责任链模式,而是以下的几种处理方法:

  1. 类的静态变量
  2. 请求者对象包含了处理者对象的引用并直接调用其处理函数
  3. 请求者对象包含了处理者对象的引用但处理函数只改变标记变量,处理者对象自动检查标记变量判断是否进行处理
  4. 创建事件的监听器,事件发生时回调处理者的处理函数,并将请求者对象作为参数传入
    下面我以项目中的代码来展示这四种实现

类的静态变量

将要请求的变量设置为类的静态变量,这样所有的请求者可以直接通过类名来访问,而不需要包含处理者的对象

	// 分数
    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常见的事件处理方法,在为对象添加监听器后,在监听器中处理事件,但在本例中将重新确认事件源后再调用事件源本身的处理函数,看起来是多余. 但这种做法一是为了适配碰撞监听器,二是保持了模块的内聚,使得碰撞处理模块更加通用和可扩充.

以上大致是我项目中的对象通讯的实践,说是对象的通讯,其实也就只是函数之间的互相调用,加上自己的一些想法算是一个总结.

原文地址:https://www.cnblogs.com/xxrlz/p/11127328.html