Best practices – Draft

These are my ideas so far around Best Practices for the v1.0 of the Big, Strange, How-To. Let me know what else we might want to say. What have I forgotten? What am I dead wrong about? Let’s work up a great list to help users get Stranger than ever.

My idea of a Strange Best Practice.

My idea of a Strange Best Practice.

General

“Render unto Unity that which is Unity’s”

What is the core purpose of Strange?

Dependency injection? That’s just one of many features. Decoupling? That’s simply a means to an end. No, the core purpose of Strange is to make your life easier. It’s a tool, like any tool. And like any tool it should be applied to the correct set of jobs. However much you like drills, you wouldn’t drive a nail with one. In the end, all the DI/IoC/Decoupling magic has to be judged against that standard. So after giving it a reasonable try, consider every recommendation, “rule”, and philosophical tenet here by evaluating exactly that. I already know the extreme value of what I’m advocating. It won’t hurt my feelings if you disagree (well, maybe a little).

Does the feature you’re working on fit well into Strange’s structure? If you’re trying to rethink physics into a Strange controller because that’s “proper” MVC, then I guarantee you’re doing it wrong. Unity already has a very nice physics system. Let it do its job. A third-party AI designed to be attached to a specific GameObject is more of a grey area. So ask yourself: is this likely to change? Is there a sensible way to decouple (perhaps apply the AI MonoBehaviour as a secondary Mediator)? If you can do these things, make it Strange. If you can’t, you have my permission to let it be.

Injection

Minimize injection at performance-critical moments

Strange is at its best for application-level architecture, and at its worst in your game loop. Supplying Mediators and injections to thousands of enemies at a performance critical moment would not be a very efficient use of resources. Be realistic about your performance requirements. If your game is entirely turn-based, maybe the performance issue is irrelevant to you. If you’re trying to create a massively multiplayer action game that runs on an older smartphone, make sure you think this through!

Wrap a third-party Singleton

Lots of very fine (and some not so fine) third-party libraries are written as Singletons. While we disagree with this choice architecturally, it’s a simple fact of the Unity world. If you need to use a third-party library written as a Singleton, simply wrap it in an interface that is useful to you. We provide instructions on exactly how to do this in the injection deep dive.

If you need named bindings, use supply-binding where possible.

As of v1.0, you can apply named bindings through inversion.

Signals/Commands

Models and services should never listen to Signals

When models and services listen to Signals, they make two critical errors. First, they become tightly coupled to the rules of the specific game/application, which limits portability. Second, they bypass Commands, which tends to result in models and services bloated with logic. Logic is for Commands. Models handle state. Services communicate outside the app.

Dispatching Signals (as opposed to listening) is a bit more of a gray area. It still limits portability, but there’s less of a tendency to bypass Command logic. Stylistically, some people prefer to make Commands completely stateless, so that they are never retained. I prefer the following pattern.

Use Promises in retained Commands

The most common reason for retaining a Command is that you are calling outside Unity, waiting for some response. The new Promises package makes this sort of callback much cleaner and lowers the number of dependencies. It also strengthens the argument for keeping Signals out of services and models altogether.

Consider the following Command:

public class FetchLevelNameCommand : Command
{
    [Inject]
    public IServerAPI service { get; set;}
    override public void Execute()
    {
        Retain();
        service.FetchLevelName().Then(OnName);
    }

    private void OnName(string name)
    {
        //Do something with the result
        Release();
    }
}

What I like about this pattern is (a) it’s very clean, readable code, (b) your service needs no injected Signals, (c) any adapter logic (up, down, or both) gets handled in the same place.

Using clear naming conventions, match Signal names to Commands

Ideally, Signals and Commands should be named such that they obviously connect. Obviously, the entire point of decoupling them is to allow you not to do this. That doesn’t mean you need to set out to make things any harder than they have to be. Start things in alignment, and don’t lose sleep when design changes dictate that they drift.

Pool Commands when possible

Every object created and destroyed must be handled by garbage collection. Marking Commands as Pooled() eliminates some of this collection. Provided you are careful to clean up after yourself, it’s usually best to mark Commands this way. See the section on Command pooling for details.

Follow the single-responsibility principle in your Commands

Treat each Command like a method that does one thing. If a single Signal results in ten different things happening, ganging all that up into one Command makes that code complicated, non-portable and inflexible. On the other hand, if you break all those responsibilities down, it becomes much easier to reuse the bits that may apply to other Signals. You’ll get less code duplication and more reuse, and other programmers (including you in three months’ time) will be able to understand with greater ease what the hell you were trying to accomplish.

Avoid injecting into Views

This is definitely a best practice, not a requirement. When a View goes through the mediation process we explicitly check whether it needs injection. So why tell you not to do a thing that we explicitly allow and provide a mechanism for? This goes to a philosophy that I call “I’m not your mother”.

There are plausible reasons for injecting directly into Views: you might have a bit of decoupled code you want accessible by the View; you might not buy our argument about the value of mediation; perhaps you want to couple a model directly to a View for convenience. I might disagree with these choices (or maybe not, depending on circumstance), but the choice on how to use our tool is ultimately yours.

Now, why is it “best” not to do this? Read the mediation section in the deep dive: keeping your Views as pure Unity probably serves you best most of the time.

“Gang up” a View into a useful component

We often observe that new users misconstrue the purpose of mediation, thinking that each GameObject, each button, each line of text must be its own View with a corresponding Mediator. This is manifestly incorrect. The point of mediation is to allow you a clean separation between the View and the app…nothing in that specifies a 1:1 control:View relationship. Create useful components, small or large, that meet your needs, and mediate those. With UI, I often find that this correlates to 1 screen = 1 View.

Keep Mediators thin

Mediators aren’t intended to do the work of the View. Nor are they intended to provide (much) logic. The sole role of the Mediator is to adapt between View and application. Keep it thin.

Consider using multiple Mediators

It is commonly assumed that each View needs only one Mediator. This is usually sufficient, but there are excellent cases for applying multiple Mediators. My favorite is for analytics. Rather than mixing up your analytics logic (was this button clicked? how many times? before or after the user did this other thing?) with either View or Mediator, consider adding an extra Mediator solely for the purpose of listening out for button clicks and the like. This can make the agonizing process of instrumenting your game clean and trivial.

Contexts

Use clear naming conventions plus implicit bindings to keep Contexts clean

In even a moderately complex application, your Context can start to look like a very long shopping list. We find that many bindings start to crystallize as development proceeds. Rather than enumerate the blindingly obvious, we recommend using Strange’s implicit binding feature to keep your Context spare. Implicit bindings are always overridden by explicit bindings, so you can make changes when special cases arise.

Part of the trick to keeping this easy, especially in large teams, is to maintain sensible naming conventions so you can intuit the most likely implementing class.

//Sensible naming convention
public class PlayerModel : IPlayerModel

//Less sensible naming convention
public class PlayerModel : IPlayer

//Full-on insanity naming convention
public class DefaultPlayerModel : IShip

Build platform variants at the Class level; put all platform-dependent statements in the Context

Have you ever waded through code like this?

public class FaceRegognizer {
 void Start () {


    #if UNITY_EDITOR
     Debug.Log("Doing a thing in Unity Editor");
    #elif UNITY_IPHONE
     Debug.Log("Doing a thing on Iphone");
    #elif UNITY_STANDALONE_OSX
    Debug.Log("Doing a thing on Stand Alone OSX");
    #else UNITY_STANDALONE_WIN
     Debug.Log("Doing a thing on Stand Alone Windows");
    #endif
 }        
}

And let’s be honest: it’s NEVER that clean. Instead of having this code mixed-in and littered throughout your code, we recommend sectioning off all platform-specific behavior into well-named classes. You’ll still have all that #if…#elif magic, but it’ll all be in one place, and each implementing class will tell a consistent, readable story for its use case.

//All common capabilities in an abstract class
abstract public class AbstractFaceRecognizer : IFaceRecognizer{}

//Each of these implements the functionality as required by the platform
public class DefaultFaceRecognizer : AbstractFaceRecognizer{}
public class IosFaceRecognizer : AbstractFaceRecognizer{}
public class WindowsFaceRecognizer : AbstractFaceRecognizer{}
public class OSXFaceRecognizer : AbstractFaceRecognizer{}

//Now bind with all the #iffyness
#if UNITY_EDITOR
injectionBinder.Bind<IFaceRecognizer>().To<DefaultFaceRecognizer>().ToSingleton();
#elif UNITY_IPHONE
injectionBinder.Bind<IFaceRecognizer>().To<IosFaceRecognizer>().ToSingleton();
#elif UNITY_STANDALONE_OSX
injectionBinder.Bind<IFaceRecognizer>().To<OSXFaceRecognizer>().ToSingleton();
#else UNITY_STANDALONE_WIN
injectionBinder.Bind<IFaceRecognizer>().To<WindowsFaceRecognizer>().ToSingleton();
#endif

Stick to a strict, Context-based hierarchy

As we explained in the Mediation deep-dive, the Context-based hierarchy — i.e., childing a GameObject to a chain that can “bubble up” to a ContextView — is only required in a multi-Context situation. That said, starting this way, even in a single-Context situation, gives you the flexibility to choose the multi-Context approach at a later time.

Build modularity using multiple scenes, each in its own Context

One of Strange’s superpowers is connecting multiple Contexts. We often talk about it as a special use-case, but the utility of this concept shouldn’t be underestimated, especially when working in teams. This basically brings decoupling to the macro level, since one person/team can work on one chunk of a game, completely abstracted from others. A scene can be a mini-game, level, store, UI screen, what-have-you, built to stand all on its own, yet able to be integrated into the larger whole when necessary. This can greatly speed up workflow and even make it possible to share whole sections of a project between multiple games. I mean, seriously, once you’ve created an in-game store for Age of Jellyfish, do you really want to build it again for Age of Anchovies?

Advertisements

Promises, promises

Pinky PromiseWill Corwin strikes again, offering a powerful new feature to the Strange feature set. This time, we introduce ‘Promises’, a simple, powerful pattern for handling asynchronous callback behavior. If you’re familiar with q-promise (or the AngularJS equivalent), you’ll know exactly where we’re going with this.

[EDIT] June 8. Attentive reader Stephan identified that I had oversimplified my examples by forgetting to inject RoutineRunner. I’ve quickly added in the missing bits — early on a Monday morning — hopefully I repaired it all correctly![/EDIT]

This will sound crazy at first (bear with me for a moment) inasmuch as we’ve always told you about dependency inversion, but with promises we will now “invert the inversion”. That does sound crazy doesn’t it? But it actually makes sense! We practice inversion because the proper job of setting dependencies lies outside the client class. Promises turn this on its head, because the responsibility for determining what to do with a job the client requested lies with the client itself.

“I went to the bakery and ordered a cake.” The baker doesn’t know why you asked (though you might add some detail so he can decorate), nor should he. Decoupling in this case means both client and service know as little about each other as possible. Client calls service’s API, asking for the “cake”. Perhaps a cake is already available. Perhaps not.

The baker/service responds with a promise to deliver the cake now, or whenever it’s ready. Responding with a promise for a thing instead of the thing itself allows greater flexibility whenever you know or suspect that the thing might not be instantly available.

Let’s take this from the theoretical to the concrete with something everyone is certainly familiar with: an HTTP call.

We all know this would fail:

string GetLevelName() {
    return new WWW('http://myserver/get/the/level');
}

This fails because this isn’t how WWW works. WWW doesn’t have the result sitting around for you. It has to go to a server to get it, and that takes time. To handle this, we write WWW with a yield:

IEnumerator FetchLevelName() {
    WWW www = new WWW('http://myserver/get/the/level');
    yield return www;
    CurrentLevel = www.text;
    //Now do something with the level name
}

So in a spaghetti-coded world, this is fine. You could drop this into any MonoBehaviour and be off to the races. But we’re in Strange, and that means we like to separate this sort of behavior into a service. So imagine a Command that triggers this service and wants to hang around until the result is ready. How do we do this?

Note that throughout the following examples we use the same RoutineRunner you can find in StrangeRocks.

 // The Command
 using System;
 using strange.extensions.command.impl;
 namespace strange.test
 {
     public class FetchLevelNameCommand : Command
     {
         [Inject]
         public IServerAPI service { get; set;}
         override public void Execute()
         {
             Retain();
             service.FetchLevelName();
         }
     }
 }

 // The Service
 using System;
 using System.Collections;
 namespace strange.test
 {
     public class ServerAPI : IServerAPI
     {
         [Inject]
         public IRoutineRunner routineRunner { get; set; }
         private string CurrentLevel;
         public void FetchLevelName()
         {
             routineRunner.StartCoroutine(Fetch);
         }

         private IEnumerator Fetch()
         {
             WWW www = new WWW('http://myserver/get/the/level');
             yield return www;
             CurrentLevel = www.text;
             //Now do something with the level name
         }
     }
 }

In the code above, I’ve obviously left a great big hole. The Command is Retained, waiting for the Service to return a value. But there’s not yet any way for the Command to get the data back, finish its business and release. Since the yield is inside the Service, there’s no real way for the Command to know when the data it’s awaiting is ready.

We could handle this by polling the service:

// The Command
 using System;
 using System.Collections;
 using UnityEngine;
 using strange.extensions.command.impl;
 namespace strange.test
 {
     public class FetchLevelNameCommand : Command
     {
         [Inject]
         public IServerAPI service { get; set;}
         [Inject]
         public IRoutineRunner routine { get; set; }
         override public void Execute()
         {
             Retain();
             routine.StartCoroutine(CheckForName());
             service.FetchLevelName();
         }
         private IEnumerator CheckForName()
         {
             yield return new WaitForSeconds(.2f);
             if (service.CurrentLevel != null)
             {
                 //Finally do something with the data
                 Release();
             }
             else
             {
                 routine.StartCoroutine(CheckForName());
             }
         }
     }
 }

 // The Service
 using System;
 using System.Collections;
 namespace strange.test
 {
     public class ServerAPI : IServerAPI
     {
         [Inject]
         public IRoutineRunner routineRunner { get; set; }
         public string CurrentLevel { get; set; }
         public void FetchLevelName()
         {
             routineRunner.StartCoroutine(Fetch);
         }

         private IEnumerator Fetch()
         {
             WWW www = new WWW('http://myserver/get/the/level');
             yield return www;
             CurrentLevel = www.text;
         }
     }
 }

Ok, that works for a definition of “works” that means “ugly, inefficient and unmaintainable.” It requires that the client knows a lot more about the inner workings of the service than you would ever want.

We can improve on this with a signal from the service.

 // The Command
 using System;
 using strange.extensions.command.impl;
 namespace strange.test
 {
     public class FetchLevelNameCommand : Command
     {
         [Inject]
         public IServerAPI service { get; set;}
         override public void Execute()
         {
             Retain();
             service.NameReadySignal.AddOnce(OnName);
             service.FetchLevelName();
         }

         private void OnName(string name)
         {
             //Do something with the data
             Release();
         }
     }
 }

 // The Service
 using System;
 using System.Collections;
 using strange.extensions.signal.impl
 namespace strange.test
 {
     public class ServerAPI : IServerAPI
     {
         [Inject]
         public IRoutineRunner routineRunner { get; set; }
         private string CurrentLevel;
         public Signal NameReadySignal = new Signal();
         public void FetchLevelName()
         {
             routineRunner.StartCoroutine(Fetch);
         }

         private IEnumerator Fetch()
         {
             WWW www = new WWW('http://myserver/get/the/level');
             yield return www;
             CurrentLevel = www.text;
             NameReadySignal.Dispatch(CurrentLevel);
         }
     }
 }

This a substantial improvement. The service has a defined API, which the client accesses. When the data is ready, the Service fires its signal and the client is informed.

The promise pattern is similar, but it trims the API considerably.

 // The Command
 using System;
 using strange.extensions.command.impl;
 namespace strange.test
 {
     public class FetchLevelNameCommand : Command
     {
         [Inject]
         public IServerAPI service { get; set;}
         override public void Execute()
         {
             Retain();
             service.FetchLevelName().Then(OnName);
         }

         private void OnName(string name)
         {
             //Do something with the data
             Release();
         }
     }
 }

 // The Service
 using System;
 using System.Collections;
 using strange.extensions.promise.api;
 using strange.extensions.promise.impl;
 namespace strange.test
 {
     public class ServerAPI : IServerAPI
     {
         [Inject]
         public IRoutineRunner routineRunner { get; set; }
         private IPromise promise = new Promise();
         public IPromise FetchLevelName()
         {
             routineRunner.StartCoroutine(FetchLevelNameFromServer);
             return promise;
         }
         private IEnumerator FetchLevelNameFromServer ()
         {
             WWW www = new WWW('http://myserver/get/the/level');
             yield return www;
             promise.Dispatch(www.text);
         }
     }
 }

Note how clean the Promise structure is from outside the service. Just a simple promise.Then().

One beautiful aspect of this pattern is that the promise can be (but does not have to be) resolved immediately. So imagine we now cache our server data. This change means that the data may or may not be local at any given moment. The Promise handles this possibility very elegantly (note changes in red):

 // The Command
 using System;
 using strange.extensions.command.impl;
 namespace strange.test
 {
     public class FetchLevelNameCommand : Command
     {
         [Inject]
         public IServerAPI service { get; set;}
         override public void Execute()
         {
             Retain();
             service.FetchLevelName().Then(OnName);
         }

         private void OnName(string name)
         {
             //Do something with the data
             Release();
         }
     }
 }

 // The Service
 using System;
 using System.Collections;
 using strange.extensions.promise.api;
 using strange.extensions.promise.impl;
 namespace strange.test
 {
     public class ServerAPI : IServerAPI
     {
         [Inject]
         public IRoutineRunner routineRunner { get; set; }
         private string CurrentLevel;
         private IPromise promise = new Promise();
         public IPromise FetchLevelName()
         {
             if (CurrentLevel != null)
             {
                 promise.Dispatch(CurrentLevel);
             }
             else
             {
                 routineRunner.StartCoroutine(FetchLevelNameFromServer);
             }
             return promise;
         }
         private IEnumerator FetchLevelNameFromServer ()
         {
                 WWW www = new WWW('http://myserver/get/the/level');
                 yield return www;
                 CurrentLevel = www.text;
                 promise.Dispatch(CurrentLevel);
         }
     }
 }

See how that change is completely invisible to the client? Whether that data is available or not is no longer the client’s responsibility. The contract between promisor and promisee is “I’ll deliver it when it’s ready.” That’s all.

The promise structure is very simple and flexible, every promise method returns a promise. In the same way that a binder can Bind.To.To.To… a promise can Promise.Then.Then.Then… allowing you to create chains of resulting actions.

There are also methods for Progress, Fail and Finally which you can use for reporting percentage of job completion, errors and irrespective conclusion, respectively.

Promise is yet another new feature in the upcoming next release of StrangeIoC, now available on the tip branch. For the full set of planned features see this wiki page. As always, we encourage you to test out the new features and help us find any problems before we make this build official.

Thanks!

Is Strange Right for You?

Ask your Doctor

A user on our forum — going through the teething pains most of us experience when learning IoC — asked us about Strange’s suitability for use with Unity. I was happy to oblige with something of an empirical defense: look at our user base, all the studios, all the cred we have inside and outside Unity. Of course Strange is suitable!

But still, I conceded, suitability for some cases doesn’t automatically imply that Strange is right for him or his project. It’s not a one-size-fits-all question. Rather, the answer lies somewhere on a continuum. So how do you know if Strange is the tool for you and your project? So glad you asked!

Strange has several advantages — and a few disadvantages. The more your intended project plays into these strengths, the more likely you would want to apply it. And even when applying it, a sensible understanding of the weaknesses should help you understand exactly where in your product Strange is best applied. In this post, I want to suggest some metrics by which you can make these decisions, none of which will be affected by the upcoming release of StrangeIoC 1.0.

Complexity (yours)

Perhaps more than anything, Strange is a tool for dealing with code complexity. Is your project likely to be complex? If I’m throwing together a quick example, or building Tic-Tac-Toe, I’m probably not going to be motivated to use Strange. The bigger my project, though, the more likely I will want something to handle my dependencies in an elegant manner.

Complexity (ours)

There can be no denying that Strange has a learning curve. For some, it’s pretty steep. To use Strange, you and your team need to learn to use it. They need to understand not just how Strange works, but why. The end benefits are pretty impressive, but only if they’re used according to the manufacturing instructions. If you’re just learning how to code, it might be good enough simply to know that we exist. Learn the basics and come back when you start to understand why code decoupling is a problem you want to solve. Similarly, if you doubt your team’s ability to circle up around a framework, don’t worry. Just put the blame on us.

Programming AbilityTeam Size & Complexity

Team size is also an issue of code complexity, with a slightly different twist. In this case, we’re thinking about the myriad styles in which different coders code. This difficulty can be mitigated with coding standards, but just as every writer has a distinct voice and syntax, so every coder implements ideas in her own inimitable style. That’s great up to a point. But when you have a large team and a tight deadline, ironing out these differences such that I can jump into your code and you can jump into mine can be the difference between making a deadline or not. Opinionated  frameworks like Strange tend to urge you to code in a particular way, leading to greater homogeneity. For lots of common problems, the architectural solutions become obvious and automatic, so you don’t need to spend time thinking about them. When you move from class-to-class (or even from project-to-project!) the code looks familiar and you can get right into it with minimal learning.

Additionally, Strange’s management of multiple Contexts means that teams or team members can break a project into sub-modules. Each module can be built independently, then joined up near the end of the development process.

Team Size & ComplexityVolatility

Another key selling point for Strange is its inherent resilience to change. A simple project is unlikely to change much. A real product operating under real world circumstances (with customers, producers, product managers, data analysts, etc. all urging you for fixes, new features and enhancements) might alter considerably. And these alterations may not be predictable or under your control.

The more volatile your project and environment, the greater the odds that you need the ability to adapt.

Code VolatilityPerformance

“How much performance do you need?” The knee-jerk reaction, naturally, is “all of it”, and that’s fair enough. Strange manages performance hits related to reflection, but even with that, there’s always going to be modest overhead related to requesting an injection. Usually, this is tiny when compared with driving your visuals or running an AI algorithm, but even so, we, like you, want to minimize overheads. For this reason we typically suggest keeping your Strange-related activities to a minimum in your most performance-hungry loops.

So be realistic about your performance needs. In a turn-based game or even for occasional events in a shooter, Strange’s overhead is negligible. The nearer your game approaches the realm of performance junkie, however, the more you want to limit its activities.

Performance requirementsTestability

Unit testing is a huge win…some of the time. It can also be a burdensome time-sink. Which one it is for you is a matter of experience, expertise, and personal preference. Generally, I find that testing my models and services is advisable. Testing controllers depends on complexity. Views are often hard or impossible to test. Whatever your opinion, if you value testing at all then offloading code away from MonoBehaviours makes unit testing possible. You don’t need Strange to do this, but it sure helps.

Code testabilityIntegration with other Assets

I’ve not had much trouble with this, but it’s possible you might. One of the very first wins I personally got from using Strange was isolating nGUi into a handful of View classes, thereby keeping it away from everything else. When uGui came along, refactoring was a breeze. Some assets unsurprisingly employ tactics that Strange philosophically opposes, such as using Singletons or attaching non-View logic to MonoBehaviours. Again, for me this has been a net win for Strange, because I can isolate those “bad” practices, wrap them in an interface, and keep them from infecting my code base. If I someday decided to swap one asset for another, I have minimal refactoring to do, since I never let them get out of control.

Now, I can’t guarantee that you’ll never run across an asset that doesn’t play well with Strange. Let’s assume you do. In that very rare case, you may need to shoehorn some logic in a weird, crazy patch. Disaster? Not in my book. If 90% of my code is well-managed, I’ll gladly accept 10% patched together. I’m still beating the odds handily.

Plays well with other assetsPlatforms

Unity deploys to an awful lot of platforms. Strange hasn’t been tested on all of them. We’re good for the obvious stuff (iOS, Android, standalone and web). Follow this link for the complete list of platforms where we’ve validated our framework.

Are we Strange yet?

As you can see, Strange offers many advantages as your code gets complex and volatile. Among these are insulation, flexibility, modularity, and testability. It also comes with a learning curve and some very modest performance costs. So is Strange right for you? I think you’re ready to answer for yourself.

ListensTo: The Sound of His Master’s Voice

Just a quickie post to introduce you to a nice enhancement, now part of the upcoming Strange release.

The sound of his master's voice.

Good Will Corwin, author of Strange Signals and my co-owner of StrangeIoC, has added a feature that allows you to remove some unnecessary legwork from your Mediators.

How many times have you typed some variation of this?

[Inject]
public MySignal mySignal {get; set;}

override public OnRegister()
{
    mySignal.AddListener(listenerMethod);
}
override public OnRemove()
{
    mySignal.RemoveListener(listenerMethod);
}
private void listenerMethod()
{
    //Took a lot of lines of code to get here
}

Now in the whole, vast scheme of things, this isn’t the worst thing in the known universe (I reserve that kind of derision for blended whisky). But it’s a lot of boilerplate, and pretty annoying when you’re trying to code as fast as you think.

Will’s latest work gets us to the same result a lot faster, and just as clearly:

[ListensTo(typeof(MySignal))]
public void listenerMethod()
{
    //Wow! That was fast!
}

[edit]I changed the method accessor above to ‘public’. Only public methods are  scanned for ListensTo. ‘private’ and ‘protected’ methods aren’t scanned (we always try to minimize the costs associated with reflection), so there’s no simple way at present to throw an error and alert you if you mark the method incorrectly. We’re looking at whether we can do this better.[/edit]

The ListensTo attribute allows us to implicitly handle injection, and the adding and removing of Signal listeners at the default moments, thereby making your code that bit more concise and manageable. As with anything to do with reflection, we encourage you to be cautious with this use in your main game loop (i.e., don’t create and destroy lots of Mediators at performance-critical moments…this has always been the case).

The new version of Strange is fast approaching. You can see the evolving feature list here: https://github.com/strangeioc/strangeioc/wiki/TODO-list-for-next-version.

It’s probably worth pointing out that these features are in flux…and we’re not ruling out more. If there’s something you need or just really want, please let us know!

JSON and the (A/B)rgonauts

argonauts
As far back as Robotlegs days, I’ve been wanting to drive my bindings from JSON. It would be cool to change the behavior of my app without requiring a recompile, just by editing a text file. And in these days of mobile apps, the utility of doing this could be greater than ever before.

Since this is now a feature in the works (on branch json-binding), let’s talk about a key way you might put this to use.

The basic paradigm

The new feature allows you to take a properly formatted blob of JSON and feed it to a binder. For example, some JSON like this…

[
    {
        "Bind":"strange.examples.strangerocks.game.IGameModel",
        "To":"strange.examples.strangerocks.game.GameModel",
        "Options":["ToSingleton", "CrossContext"]
    },
    {
        "Bind":"strange.examples.strangerocks.game.IGameConfig",
        "To":"strange.examples.strangerocks.game.GameConfig",
        "Options":["ToSingleton"]
    }
]

…can be loaded from a text file or server and fed into Strange like this.

//after I've loaded the JSON file...
injectionBinder.ConsumeBindings(injectionBindingJson);

Once consumed, your binder will implement these bindings as if they had been written out by the following code:

injectionBinder.Bind<IGameModel>().To<GameModel>()
    .ToSingleton().CrossContext();
injectionBinder.Bind<IGameConfig>().To<GameConfig>()
    .ToSingleton();

With this new feature, you can write most instructions you’d write in the Context, such as injections, View/Mediator bindings and Signal/Command bindings, but in JSON instead of in C#.

Side-track: a quick intro to A/B Testing

(If you’re already clear on what A/B testing is, you might want to jump to the next section.)

To my thinking, one of the best uses of runtime JSON bindings is A/B testing. A/B Testing is an analysis technique for tuning application or game features. You have an idea that some feature in your game could be better than it is, so you experiment. You segment your user base: some users will see the feature one way (the ‘A’ segment), others will see it a different way (the ‘B’ segment).

A simple example:

There’s a button you really want your player to click, maybe the entrance to your in-app store. Your UI designer says the button is in the wrong spot. OK. Test it.

Create a segment of users who see the button in the new spot (‘A’ segment) and a control group (‘B’ segment) who see it in the old way. Who goes into the store more? The ‘A’ group or the ‘B’ group? You can use an analytics tool like Unity Analytics to find out, and decide whether or not the UI designer was correct.

This technique removes a lot of the guesswork from understanding how your players behave.

The fault in our binaries

In the traditional approach to A/B testing, the app contacts a server which sends back some numbers/strings to express the terms of the test (in our example above, where to place the button). If the test is particularly radical, the server might return a completely different binary. That might be acceptable in a web app, but a different binary is impractical and usually against the terms of service on mobile and consoles. This means that for really powerful A/B tests, you need to release a new binary for each test…possibly even multiple binaries.

At this year’s GDC, one speaker told our audience that they don’t do A/B testing on iOS. Why? The release cycle through Apple’s App Store is too slow. Sure, a server can feed in a few numbers and strings, but their tests are too complex for that. They need to alter how the game runs, and they just can’t release new binaries fast enough to work effectively with iOS. So they test on Android. Needless to say, this pretty much surrenders any hope of understanding how their iOS market differs from their Android market; and it’s well understood that these two platforms differ in many important ways.

So what if you could run a battery of tests without releasing a new binary?

This is where I think JSON bindings become really useful. With a little foresight, I can include numerous permutations within a single binary, all ready to be bound together based on instructions from a server.

Example:

Hypothesis: The user will be more likely to make an in-app purchase if they receive a call to action when they fail at a level.

Test: Do users upgrade if presented with a screen directly after failing a level?

Segment ‘A’: (Test Group)

[
    {
        "Bind": "mygame.signals.LevelLostSignal",
        "To": "[mygame.commands.LevelCompleteCommand, 
                mygame.commands.PresentUpgradeOptionCommand]"
    }
]

Segment ‘B’ (control GROUP):

[
    {
        "Bind": "mygame.signals.LevelLostSignal",
        "To": "[mygame.commands.LevelCompleteCommand]"
    }
]

Example:

Hypothesis: The AI on level 7 is too hard.

Test: Which AI allows an acceptable number of players to beat level 7?

Segment ‘A’: (Test Group)

[
    {
        "Bind": "mygame.ai.IEnemyAI",
        "To": "mygame.ai.EasierLevel7EnemyAI"
    }
]

Segment ‘B’ (control GROUP):

[
    {
        "Bind": "mygame.ai.IEnemyAI",
        "To": "mygame.ai.Level7EnemyAI"
    }
]

Segment ‘C’ (A/B tests can be A/B/C/D/E…So this is another test group):

[
    {
        "Bind": "mygame.ai.IEnemyAI",
        "To": "mygame.ai.SuperEasyLevel7EnemyAI"
    }
]

As you can see from these two trivial examples, runtime JSON bindings open up the whole of your binding infrastructure for testing and tuning without the need to release a new binary. This isn’t a magic bullet, of course. You aren’t going to turn Pac-Man into Assassin’s Creed on the strength of a few binding changes. But the ability to broaden the scope of what you can consider testable, and even compose fundamentally new tests without a new release cycle, should put a little extra power into your hands.

When does JSON set sail?

The code is available today on the branch mentioned at the top. I’d love to have it tested and any bugs exposed before we make it officially a part of the next release of StrangeIoC.