Babel Naming

Journey From BabelThis just tickled my funny bone, and I thought I’d share. As I’m working on the documentation for the next version of Strange, I thought I’d go through some of my early notes, looking for ideas. And I stumbled across this blast from the past.

The development codename for what eventually became Strange was Babel. As the framework was nearing completion — and we knew we were going to open-source it — we decided we needed a cool name to get it noticed, so we threw together a brainstorming document. Submitted for your amusement, here are all the Stranges that could-have-been…


Babel is the working title for the framework I’ve been building. We’re looking for a name. Feel free to add ideas.

Things to consider

  1. At its heart, Babel is not an injection library. It’s a binding framework. The most important thing Babel does is bind something to something else. One (very important) application of that binding is dependency injection.
  2. Consideration #1 might not matter.
  3. It might be nice to use our initials, TM, to reflect back upon us.*
  4. Consideration #3 might be unnecessary.
  5. Babel is lightweight and modular, something that appeals to developers.
  6. Babel is written for C# and Unity, and will be (so far as we know) the first “serious” attempt to provide a Inversion-of-Control/Dependency Injection framework for this purpose.
  7. If you care, you can see the names of some IoC/DI libraries here.

Name ideas (feel free to add)

  • TieFighter
  • TinMan
  • ThinMan
  • TinType
  • Babel
  • Babylon
  • Unification
  • UncleBob (a reference to the guy who devised the Dependency Inversion Principle)
  • Stingray
    • Ampule
  • IssacNewtonsBinding or the bindingofIssac
    • FirstMotion
    • Newton
  • Boson – “the ‘glue’ that holds matter together”
    • Higgs
    • DarkMatter
  • Fondant (UK: /ˈfɒndənt/, US: /ˈfɑndənt/ or /ˈfɑndɑnt/, from the French: /fɔ̃.dɑ̃/) is one of several kinds of icing-like substances used to decorate or sculpt pastries.
  • Agar or agar-agar is a gelatinous substance derived by boiling[1] a polysaccharide in red algae, where it accumulates in the cell walls of agarophyte and serves as the primary structural support for the algae’s cell walls
  • A carboy (or demijohn) is a rigid container with a typical capacity of 20 to 60 L (5 to 15 gallons).[1] Carboys are primarily used for transporting fluids, often water or chemicals.[2] (as cider maker, I just find these to be funny words)
  • Strange – A strange attractor generates predictable patterns, often in the scope of a chaotic system.
  • Gloo
    • Glue
    • BluTac (thinking of the virtue of sticking things together, but not permanently)
    • Paste / LibraryPaste / PasteLibrary / PasteBoard
    • Duct tape
  • Vulcan MindMeld
    • My mind to your mind
    • Sneaky sci-fi references
      • Liberator
      • SonicScrewdriver
      • Tanstaafl – see here, particularly in relation to Newton’s Second Law of Thermodynamics
      • Klaatu
      • Illudium
      • Tholian (web)
      • Federation
  • Octopus
    • Tentacles
    • TentacleMotion

* The “Us” in this context was ThirdMotion, the company responsible for me bringing Strange to you.

Advertisements

It’s Big, It’s Strange, and I’m Rewriting It

[EDIT]First draft of Introduction is now open for comment[/EDIT]
[EDIT2]The revised architecture diagram is now open for comment[/EDIT2]

Gentlemen, you can't fight in here! This is the War Room!

The Big, Strange How-To has been the standard (and, er, only) textbook on Strange since its inception. But with the exception of a handful of corrections and the addition of a couple of new features, it has remained largely unchanged since it was first written way back alongside v0.5. With the upcoming release of v1.0, I reckon that it’s high time for a proper update.

With the benefit of hindsight and whisky, I’m looking at everything in the BSHT to see what needs updating, adding, or even removing. And that’s where you come in, my friends!

What could we explain better? What has frustrated you most, especially when you were first learning? I especially want to concentrate on the new user: what can we do to flatten the steepness of our learning curve?

Here are my ideas so far. Please leave comments below and I’ll keep this post updated with your input.

Things to add to the Big Strange How To

Re-work the Introduction

New section: opening Strange for the first time (walk-through)

  • Explain the broad structure of the repo.
  • Walk-through of MyFirstProject project
  • Walk-through of MultipleContexts project

Updated section: Signals

  • Beef up the Signals section.
  • Add explanation for the (new to v1.0) ListensTo feature.
  • Change all examples (across all of BSHT) from EventDispatcher to Signals.

Updated section: Mapping across Contexts

This section is really thin. Let’s clarify the concept and the execution. Particularly, explain the reasoning behind multiple Contexts, along with practical info on how to structure them.

Updated section: Reflection

I don’t want to confuse a new user with too much about this…but then the understanding the principles is important.

New section: Pools

Explain how the Pools extension works

New section: Promises

Explain the (new to v1.0) Promises package

New section: Implicit Bindings

Explain the ImplicitBind package

New section: runtime bindings

Explain the (new to v1.0) feature runtime bindings

New mini-section: Strange for extending Unity

A mini-section because it will essentially just link to a separate document specifically about using the (new to v1.0) Editor extension. The current plan is for the Editor extension to live outside the main Strange repo as an optional plug-in.

New section: Testing tutorial

Explain how to run unit tests and write your own.

Enumerate Exceptions for each package

In each package, list and explain each of the thrown errors. In an appendix at the end of the document, put all Exceptions in one place for easy reference.

Replace artwork

All that pesky text art (—–>). Let’s grow up and use PNGs or SVGs or something.

Tutorial

Lots of people asking for something more like a step-by-step tutorial. Particularly questions about uGUI. Should this be here or a separate document?

New Section: Platform Specific Issues

A request in the comments to explain iOS trampolines. Compile a set of other platform-specific gotchas?

New Section: Best Practices

Some cohesive section on Dos/Don’ts when using Strange.

Errata

Add named injection for constructors

How this will work

I’ll going to create the new BSHT as a Google Doc at first. I’ll edit this post and provide a link so everyone can weigh in. In the meantime, feel free to leave comments and ideas.

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.

Owning my responsibilities

Today’s post is not precisely about Strange so much as about keeping one’s eye the fundamentals.

As you might already know, we’ve been working on adding editor functionality so that you can create StrangeIoC-based Extensions for Unity. I usually approach a new project like this in a few simple steps:

  1. Quick and dirty proof of concept
  2. Refactor and tighten
  3. Test and correct

As you can see I’m not really a TDD guy, though I end up with a similar product.

In step one, I copied and pasted the MediationBinder and hacked a version that would work with EditorWindows. This led, in step two, to an obvious refactor: extracting commonalities between MediationBinder and the new EditorMediationBinder into an AbstractMediationBinder. And Oh Look! all those commonalities – freed from the surly shackles of MonoBehaviours – reveal big chunks of testable code!

This has always been an Achilles’ Heel for Strange. The untestability of the mediation package has meant that we have never been entirely certain of the quality of our code in this part of the framework. Because of this, we necessarily approach changes here with a lot more caution then we might do any other part of Strange.

My buddy Will saw this and immediately went to town with a further refactor. The result is amazing: virtually the whole of the (Abstract)MediationBinder is now open to unit testing. The bits that aren’t tested simply represent specific lines where we supply access to the MonoBehaviour-specific API. All the rest has been abstracted away.

The practical result for you – whether or not you care about Editor extensions or even unit testing – is safer, more reliable code in an area of the framework on which you probably rely a great deal.

The mea culpa and moral of all this: the single responsibility principle exists to help us. My failure to consider this fully when writing the original version of the MediationBinder is only coming clear to me years later. Had I broken down the elements of my methods into smaller atomic parts back then, we probably wouldn’t have written off the whole of this package as untestable.

Lesson learned.

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!

New feature: Editor Views within Editor Contexts

At the SF Unity User Group a few nights ago, one gentleman asked me (as he had in fact once before) whether Strange could be used within the Unity Editor. He wants to do a better job building Editor extensions, and he’d like it if we could assist.

sherlock_strange

Sure, I said, if you want to build in the necessary changes. I wasn’t too keen to do the work myself…how many people actually have this need? But the more I thought about it, the more I decided it was an appropriate use case…and it didn’t actually seem that hard. We’d mostly just need a specialized Context and a specialized MediationBinder.

So I’ve been whirring away at this for a day or two, and the (definitely not-ready for production) alpha is now on branch feature/editor. It principally involves a new extension package called ‘editor’ that includes the following classes:

  • EditorContext (same as Context, but specialized for Editor)
  • EditorContextException (for reporting errors)
  • EditorMediationBinder (a specialized variation of MediationBinder)
  • EditorMediator (similar to, but with important differences from, Mediator)
  • EditorMVCSContext (a specialized variation of MVCSContext)
  • EditorView (like View, but we extend EditorWindow instead of MonoBehaviour)

Please review the code. For the most part, there’s not a lot that’s surprising. But we do have some differences to discuss, which I’ll get to after I (finally) get to our actual headline.

In order to test my work, I’ve started working on a StrangeIoC inspector. The beginning of this is also on the branch. Thing is, I don’t do a lot of Editor extension work, so I’m not entirely sure how to proceed. And any way, what features would be most useful? So I’m going to start working on this tool-thingy as part of the next release, but I’ll probably come to you via this blog to generate ideas and help me fix the things I break.

OK? Ball’s in your court my friends.

Moving on, here are some things you probably want to know about the differences between regular Strange and Editor-extending Strange.

There’s a lot more working around statics

EditorWindow features seem to be built on a lot of static methods. This isn’t helpful in a Strange world. On the other hand, Strange can help isolate a lot of this silliness. For example, my StrangePanel already has a ScriptReloadService to isolate the service aspect of knowing that scripts have been updated. This helps with abstraction and keeps my StrangePanelView focused on View-related behavior.

There’s no ContextView

Traditionally, we kick off a Strange app with a MonoBehaviour at the top of a view hierarchy. This doesn’t make sense in Editorland (the most magical place on Earth), where we instead use the [InitializeOnLoad] attribute. What’s nice about this, though, is that [InitializeOnLoad] means our package is just plain up-and-running when we enter Unity. And there’s no need to track or think about which View is “on top”.

There’s no Cross-Context behavior

At least for V1, this seems OK to me. It’s a bit hard to say at this point whether we want multiple contexts in a single Editor extension. I suppose we might. Anyway, let’s cross that context when we come to it.

To be clear, I’m talking about multiple Contexts in a single extension. I’ve made sure to handle the possibility of more than one extension, both of which happen to use Strange (this is called ‘optimism’). Each extension gets it’s own Context within which to operate.

EditorMediators are not Views

In regular Strange, your Mediators are MonoBehaviours, so in some respects they’re just like Views. I’m not even sure I could make this happen in Editor-flavored Strange, but I really didn’t try. EditorMediators now actually behave much more like Mediators in Robotlegs, in that they are in contact with the View, but unable to perform actual View logic themselves. This doesn’t seem like a problem to me. You’ll have to let me know if you disagree.

For the EditorMVCSContext, Signals are the primary event bus

I’ve been thinking about doing this in the rest of Strange, where the primary event bus is still EventDispatcher. For Editor, this seems an obvious thing to do, since Signals really are a better approach, and we may as well hit this brave new world with the best tools.

Review and Comment

As always, I ask you to look at this now, before it’s a fait accompli and in the live product. It’s better for everyone if this work is challenged and vetted a lot before we use it in anger.

And let me know what you want/need in a Strange Inspector. Should we be able to inspect our bindings from there? Write them? List all our Contexts/ContextViews? Give me some ideas!