Null Object Patterns How They Can Save You Time Refactoring

Share on FacebookTweet about this on TwitterShare on LinkedInShare on RedditShare on Google+Pin on PinterestShare on TumblrBuffer this pageEmail this to someone

Null Object Pattern

How It Can Speed Up Refactoring

Screenshot from mobile version of Ace Academy: Black Flight
Screenshot from the mobile version of Ace Academy: Black Flight

While porting Black Flight Mobile to Gear VR, I noticed that some elements would need to simply be removed such as certain UI touchscreen buttons whose functionality is now in physical controller buttons. Take the shoot button for example.

Transitioning UI From Touch To Controller

The phasing-out of traditional UI touchscreen buttons is good news for developers! Removing something is usually easier than adapting or replacing it. Alas, I cannot just find the shoot button and delete it. Much code depends on finding that button, applying changes to it and calling functions of it.

The first challenge is, much of that code looks as follows:

function UseTheShootButton ( ) : void {
  var a : int = 0 ;
  GameObject.FindByType<ShootButton>().enabled = true ;
  //Code in between uses of ShootButton
  a ++ ;
  GameObject.FindByType<ShootButton>().bullets = 10 ;
  //Code in between uses of ShootButton
  a = a + 4 ;
  GameObject.FindByType<ShootButton>().shoot() ;
  //Code in between uses of ShootButton
  a = 0 ;
}

Why Disabling Code Is Better Than Deleting

This is just an example of one function in one file. In reality, this is repeated all over the project. I could delete the code, but I would be cutting off the functionality completely, which might not be ideal. I just want to disable it. The other option would be to wrap each use of the ShootButton object with a null check, but that would be very difficult and look ugly in code:

//Bad idea, for each ShootButton use:
if (GameObject.FindBytType<ShootButton>() != null )
  GameObject.FindByType<ShootButton>().shoot() ;

How To Disable Code

There are two steps to take in solving this problem:

  1. Centralize the reference to the ShootButton object.
  2. Create a Null Object to replace the original, so it accepts all the same interactions.

If I rewrite the same function with a centralized reference, it would look like this:

function UseTheShootButton ( ) : void {
  var a : int = 0 ;
  var shootButtonObj : ShootButton ;
  shootButtonObj = GameObject.FindByType<ShootButton>();
  shootButtonObj .enabled = true ;
  //Code in between uses of ShootButton
  a ++ ;
  shootButtonObj .bullets = 10 ;
  //Code in between uses of ShootButton
  a = a + 4 ;
  shootButtonObj .shoot() ;
  //Code in between uses of ShootButton
  a = 0 ;
}

Better Readability & Performance

Not only is that more readable, but It will also perform better, as finding objects is a slow operation in Unity. We still have the problem of nulls though. If the object is not found by the FindByType function, this function will break with many null references.

Enter The Null Object Pattern

  1. Change the name of the ShootButton class to ConcreteShootButton.
  2. Create an interface named ShootButton, with declarations of all of the functions that ConcreteShootButton already has, like so:
interface ShootButton {
  enabled { set ; get ; } : bool ;
  bullets { set ; get ; } : int ;
  function shoot ( ) : void ;
} ;

3. We make ConcreteShootButton implement the Interface ShootButton. ConcreteShootButton already has all the necessary functions and properties, so this requires no extra coding beyond “ConcreteShotButton implements ShootButton”.

So our original function looks like this now:

function UseTheShootButton ( ) : void {
  var a : int = 0 ;
  var shootButtonObj : ShootButton ;
  shootButtonObj = GameObject.FindByType<ConcreteShootButton>();
  shootButtonObj .enabled = true ;
  //Code in between uses of ShootButton
  a ++ ;
  shootButtonObj .bullets = 10 ;
  //Code in between uses of ShootButton
  a = a + 4 ;
  shootButtonObj .shoot() ;
  //Code in between uses of ShootButton
  a = 0 ;
}

I could create any type of object that implements ShootButton and assign it to the shootButtonObj reference, and this function would work just fine. The point is, I would only have to change one line of code. Hmm… That’s exactly what I’ll do.

class NullShootButton implements ShootButton {

  public enabled : bool {
    set { } //does nothing
    get { return false ; } //doesnt do much either.
  }

  public bullets {
    set { } //does nothing
    get { return 0 ; }
  }

  Public function shoot ( ) : void { } //doesnt do anything.
}

Replacing The Shoot Button

If I want to replace the actual ShootButton (now renamed ConcreteShootButton) for a “Null Shoot Button” I only need to change one line of code in the original function:

function UseTheShootButton ( ) : void {
  var a : int = 0 ;
  var shootButtonObj : ShootButton ;
  shootButtonObj = new NullShootButton();

  shootButtonObj .enabled = true ;
  //Code in between uses of ShootButton
  a ++ ;
  shootButtonObj .bullets = 10 ;
  //Code in between uses of ShootButton
  a = a + 4 ;
  shootButtonObj .shoot() ;
  //Code in between uses of ShootButton
  a = 0 ;
}

You’re Done!

Now all the desired function calls will work and will do nothing. If you wanted to go back to the button, you just revert that one line and you’re done. That’s the beauty of non-destructive workarounds in game development.

Let us know in the comments section below if this post helped you, or if you’d like to see more like this!

 

SEED is recognized as one of top AR/VR Companies on DesignRush

Share on FacebookTweet about this on TwitterShare on LinkedInShare on RedditShare on Google+Pin on PinterestShare on TumblrBuffer this pageEmail this to someone

Leave a Reply

Your email address will not be published. Required fields are marked *