Entries
RSS 2.0

Comments
RSS 2.0

The Problem With New

This article comes from an entry I made on the wiki for the project I’m currently working on. I thought it might be a good way to introduce others to the concepts (dependency injection and inversion of control) that I’m working with these days. I’m trying to get as much of this information down off of my whiteboards and into actual text as po/ssible, so hopefully there will be more similar articles in the near future.

Overview

C# is an object-oriented language. As a result, one of the first things we learn is the way to create objects: the new operator. Since everything in C# is defined as an object, the new operator is one of the most important keywords in the language, and its use is vital. However, depending on how it is used, that tiny three letter word has serious design implications can create major development headaches. In this article, we explore what I’ll refer to as the problem with new.

Composition and Dependencies

Let’s begin with a simple example: we need to build a car. For simplicity’s sake, let’s say a car has an engine, four wheels, and a radio. In C#, each of these concepts could be defined as a separate class, and instances of them could be contained inside a Car class, like this:

public class Engine { ... }
public class Wheel { ... }
public class Radio { ... }

public class Car
{
  private Engine _engine;
  private Wheel[4] _wheels;
  private Radio _radio;
}

This illustrates the design principle of composition, wherein one object contains instances of another object. The objects contained inside our Car objects are dependencies, meaning that our Car requires them to function. However, just defining the fields inside the objects isn’t enough; we have to populate them. One way to do this is to create instances inside the Car class’s constructor:

public class Car
{
  private Engine _engine;
  private Wheel[4] _wheels;
  private Radio _radio;

  public Car()
  {
    _engine = new Engine();
    _wheels = new Wheel[4];

    for (int ii = 0; ii < 4; ii++)
      _wheels[ii] = new Wheel();

    _radio = new Radio();
  }
}

Now our Car has its necessary dependencies fulfilled. What happens, though, if you want to upgrade the parts of the car? Let’s say you want to build an Aston Martin Vanquish. In order to do this, we’d have to give the car a V12Engine instead of just the stock Engine. How would you make this change? To support this, we have to change the _engine field to store a V12Engine, and change the constructor to create an instance of the V12Engine class:

public class V12Engine { ... }

public class Car
{
  private V12Engine _engine;
  ...

  public Car()
  {
    _engine = new V12Engine();
    ...
  }
}

That works, but not everyone can afford an Aston Martin, so not all cars should be given a V12Engine. What’s going on here?

Object Graphs

The problem that we’ve introduced with this example is that our Car class is in charge of resolving its own dependencies. Inside the Car constructor, we’re creating concrete instances of the dependencies. This means that our design looks like this:

Figure 1

The dependencies between objects in your application is called an object graph. Inside this graph, when links are created between two concrete classes (such as the link between our Car and Car classes), the classes are coupled together. When two classes are coupled, they cannot be interchanged without changing the code inside the classes, as we saw when we wanted to use the V12Engine. Generally speaking, the more coupling that exists in your software, the less flexible your code will be, and the more difficult it will be to make modifications when they are required.

This is what’s meant by the problem with new. It can be summed up by the following:

Using the new operator inside a class couples that class to the class that you are instantiating.

So, how do we avoid this problem? Obviously, we have to be able to create instances of concrete types in our application. The new operator itself really isn’t the source of the problem — it’s where and how you use the operator that makes the difference.

The Issue of Control

Essentially, the debate over the use of the new operator is an issue of control: specifically, control over resolving the dependencies of an object. In our example, the Car object has total control over its composition, because it resolves its own dependencies in its constructor by creating instances of the concrete types that fulfill those dependencies. This is not a flexible solution, because to change the makeup of a Car instance (that is, to alter its composition), we have to alter its implementation.

A solution to this problem is to follow the inversion of control (IoC) design principle. Basically, it says that as much as is possible, separate the means to build an object’s composition from the object’s implementation. An easy way to implement IoC is through the dependency injection design pattern, wherein an object receives the objects it depends on from some external force.

Let’s take another look at our Car class with this in mind. First, let’s create some contract interfaces representing the different components in our design. This way, we can interact with objects based on their functionality, rather than their actual identity:

public interface IEngine { ... }
public interface IWheel { ... }
public interface IRadio { ... }

public interface ICar
{
  IEngine Engine { get; set; }
  IRadio Radio { get; set; }
  IList<IWheel> Wheels { get; set; }
}

Notice the use of IList instead of the array, because not all cars are required to have four wheels! Now, we can re-implement our car to take advantage of our new interfaces:

public class Car : ICar
{
  private IEngine _engine;
  private IRadio _radio;
  private IList<IWheel> _wheels;

  public IEngine Engine
  {
    get { return _engine; }
    set { _engine = value; }
  }

  public IRadio Radio
  {
    get { return _radio; }
    set { _radio= value; }
  }

  public IList<IWheel> Wheels
  {
    get { return _wheels; }
    set { _wheels = value; }
  }

  public Car(IEngine engine, IRadio radio, IList<IWheel> wheels)
  {
    _engine = engine;
    _radio = radio;
    _wheels = wheels;
  }
}

Now, we’ve made it so instead of the Car creating its own dependencies, they are ”injected” from some exterior force. This illustrates constructor injection, but there are also other means, such as property injection, wherein dependencies are injecting using the class’s property setters. Now that we’ve got our Car talking contractually, we can create a bunch of different concrete implementations:

public class Engine : IEngine { ... }
public class V12Engine : IEngine { ... }

public class Radio : IRadio { ... }
public class SatelliteRadio : IRadio { ... }

public class Wheel : IWheel { ... }
public class 22InchWheelWithSpinners : IWheel { ... }

Our design now looks like this:

Figure 2

Using our new flexible design, we can interchange the dependencies of the car. This means we can finally create our dream car:

List<IWheel> wheels = new List<IWheel>();

for (int ii = 0; ii < 4; ii++)
  wheel.Add(new 22InchWheelWithSpinners());

ICar car = new Car(new V12Engine(), new SatelliteRadio(), wheels);

We could also change the components of the Car after it is created, by using the properties exposed on the object. Thus, our code is much more flexible and can react much better to changing conditions and requirements.

This leads us to the design principle that solves the problem with new:

Within reason, no two concrete classes should ever interact in an application, except through an interface. Also, if a class requires an instance of another class, it should be provided externally.

The key phrase in that sentence is “within reason”. Be careful not to overuse this principle, because it can result in a complex design. Very simple interactions between types that are relatively unchanging are acceptable. For larger modules or more volatile interactions between types, however, following this principle will result in a far superior design.

Conclusion

While the new operator is a vital aspect of C#, its overuse or use in the wrong situations can cause our code to become rigid and inflexible. Following the principle of inversion of control, and using the dependency injection pattern, we can make our code more malleable and able to change as requirements shift.

  • Google
  • del.icio.us
  • DotNetKicks
  • DZone
  • Digg
  • Reddit
  • StumbleUpon

Leave a comment

Please be polite and on topic. Your e-mail will never be published.