Dependencies Are Trouble Break Them Where Possible
What is a dependency, why are they bad and how do we get rid of them?
What is a dependency?
A dependency is when your code depends on some other code. If you use a hard dependency (i.e. you create the object you want to use) then it’s difficult to change this to another, similar, object without changing your code.
As with most things in life, it’s easier to explain with an example.
Suppose we have an Auth.User class with the following methods:
namespace Auth {
public class User {
//static Constructor
public static user Current() { blah blah }
//has this user been granted this role
public bool IsInRole(string role) { blah blah }
//has this user been granted any roles?
public bool IsInAnyRole() { blah blah }
}
}
and we have a Requisition class that can only be viewed by someone with the viewer role and edited by someone with the editor role.
namespace MyProj {
public class Requisition {
public bool CanView() {
return Auth.User.Current().IsInRole('viewer');
}
public bool CanEdit() {
return Auth.User.Current().IsInRole('editor');
}
}
}
Ok, we’ve ignored implementation but we can see how this works, it’s fairly simple. Sure there could be improvements and the design isn’t great.
Why is it bad?
Suppose User.Current() was designed to work on a web application using windows authentication (or any other form of auth really). Our app doesn’t care it just uses the Current() ctor and let’s the User class deal with that, and that’s quite correct, Requisition shouldn’t care about authenticating the user, just if the user is authorised to perform and action. So why could this be considered ‘bad’?
In a word: Testing. Specifically automated testing. I want to create a dummy user, with the viewer role, and check that is can view but cannot edit a requisition. When I run a unit test, it probably won’t be authenticated, so User.Current() will throw an exception (or worse return null) so my code won’t work (either I get a nice security exception telling me the current user isn’t authenticated, or I get an awful object not set to an instance of an object exception in .net!)
I can’t easily test my code without messing around with changing (or breaking) authentication, which we don’t want to do as this is (hopefully) a mature and well tested library, or changing my Requisition class’s code for testing. Plus if we are running different code in our tests, than that in production, we aren’t really testing our code.
This whole issue is because my Requisition class has an unbreakable hard coded dependency on Auth.User.Current() which needs to be broken.
How to break them?
Luckily there is a known method for breaking dependencies: Injection. Basically when we create a Requisition we give it (inject) the User object we wan’t to use.
namespace MyProj {
public class Requisition {
private readonly User usr;
public Requistion(User currentUser) {
this.usr = currentUser;
}
public bool CanView() {
return this.usr.IsInRole('viewer');
}
public bool CanEdit() {
return this.usr.IsInRole('editor');
}
}
}
so when we use the Requisition class in our code, rather than calling it like
Requistion myReq = new Requisition();
we now use
Requistion myReq = new Requisition(Auth.User.Current());
and in our test code we would use something like:
User user_viewer = new User().GiveRole('user'); //or however we create users
Requisition testReq = new Requisition(user_viewer);
//Assert testReq.CanView() returns true
//Assert testReq.CanEdit() returns false
Our Requisition class no longer has a dependency on Auth.User.Current(), and who ever creates a new requisition decides which user to auth against.
Make it backwards compatible.
Requisition class already in use? Don’t want to change all that code? Easy, use constructor chaining
namespace MyProj {
public class Requisition {
private readonly usr;
public Requisition() : this(Auth.User.Current()) { }
public Requisition(User currentUser) {
this.usr = currentUser;
}
... etc ...
That is if you call the ctor with no params, it will call the overloaded ctor passing Auth.User.Current() as the currentUser. You existing code won’t change and will see the same functionality, but you testing code will call the overloaded (injecting) ctor (which you production code is using, indirectly).
Make them generic
Of course this assumes you can create a User and specify it’s roles (for testing). but this might not, probably won’t be the case (do one thing - your Auth library should be for checking what roles they have, not modifying them). So better for your Auth library’s User class should implement an interface and use the interface instead.
namespace Auth {
public interface IUser {
bool IsInRole(string role);
bool IsInAnyRole();
}
public class user : IUser {
... etc ...
}
}
Then your Requisition can deal with the IUser type:
namespace MyProj {
public class Requisition {
private readonly IUser usr;
public requisition(IUser currentUser) {
this.usr = currentUser;
}
... etc ...
}
}
Nothing changes for existing code using Requisition as Auth.User implements IUser, but it means you can create your own test user, which implements IUser, but lets you control which roles it has independently of the Auth library. By the way this is called the Strategy Pattern. We defined a strategy (the IUser interface) for solving the problem (of authorising a user) then our requisition class delegated the implementation of that strategy at run time not compile time (i.e. we use whatever IUser object we are given, rather than choosing which object at run time)
Happy Dance
In this way, we have eliminated the dependency on Auth.User.Current() within our Requisition class. First we injected the dependency into the constructor (provided by the Auth library) then we modified the Auth library to define its own type (interface) so we could create our own IUser to inject without relying on Auth to create one for us.
So we can now do our happy dance!