nilFM

dependency injection

Dependency injection is more or less taking a functional approach to object-oriented or procedural programming. Instead of modifying static or global variables, you always pass a reference into whatever function or class needs to acces the data. There are lots of "frameworks" that can provide you with dependency injection containers, but I recommend doing it yourself, as it gives you control and doesn't hide what's going on behind needless abstractions.

Take an example like this in C (simplified example taken from the xrxs server):

// say in main.c we have this
static UserInfo[64] usersTable;

// and we have a function that needs the UserInfo data in user.c
void join_realm(UserInfo* table, char* uname, char* realm) {
  userInfo* u = get_user(table, user);
  if (u) {
    strcpy(u->realm, realm);
  }
}

This way, instead of having the usersTable be truly global and include an extern declaration to it in user.c we keep track of which functions have access to it. This makes debugging easier.

In a language which supports interfaces, we can go a step further. Take this C# example (I've been playing Tales of the Abyss so bear with it):

struct Element {
  string Name;
}

struct ElementalEffect {
  string[] Destroys;
  string[] Activates;
}

interface IThing {
  Element Element { get; set; }
  ElementalEffect UseSorcerersRing();
}

class Mieu : IThing {
  public Element Element { get; set; }
  public ElementalEffect UseSorcerersRing() {
    switch (Element.name.toLowerCase()) {
      case "fire":
        return new ElementalEffect() {
          Destroys = new string[] {
            "ice",
            "vegetation",
            "fabric",
            "darkness",
          },
          Activates = new string[] {
            "torch",
            "candle",
            "oil"
          }
        }
      case "earth":
        return new ElementalEffect() {
          Destroys = new string[] {
            "rocks",
            "rubble"
          },
          Activates = new string[] {
            "bell",
            "tree",
            "button"
          }
        }
    }
  }
}

class Controller {

  private IThing _thing;

  public ElementalEffect Effect { get; private set; }

  public Controller(IThing thing) {
    _thing = thing;
  }

  public void PressSquare() {
    Effect = _thing.UseSorcerersRing();
  }

}

void main() {
  IThing mieu = new Mieu();
  Controller controller = new Controller(mieu);
  controller.PressSquare();
}

In this case, not only do we keep track of anything which uses the IThing interface by requiring that it be documented in the constructor arguments, but we also can swap out Mieu for any IThing if we want to change the implementation, whether that be because Mieu got on our nerves, did something wrong, or we need a different implementation for unit-testing the Controller class.