Class Instantiation Guidelines in Object Oriented Languages: When to choose Singleton, Static, Extension methods or Dependency Injection

Class instantiation dilemma singleton static factory extension method

Software Engineering is very different from any other type of engineering. While in mechanical engineering or electrical engineering there’s a definite “right” and “wrong” way of doing things, in software there just isn’t. At least as far as software design is concerned.

10 software engineers can build the same product with completely different architectures, different programming models and different languages, and nobody will be right or wrong.

With that mindset, I’d like to tackle an old dilemma: Class instantiation. Which pattern do you use to create a class? Do you always use a new statement? Do we still need to use Singleton or Factory? Should we always use dependency injection? How about static classes, are they truly evil?

This is an opinionated article, please take it in as such. But, I would love your feedback either way, even if you disagree.

Types of Instantiations

In 2019, software design evolved a little beyond the standard Factory/Singleton patterns that we learned in computer science degree. We’ve got inversion of control, static classes, and even extension methods.

Here are the options at our disposal :

All the examples are going to be written in C#, but it’s just as relevant to other object-oriented languages.

1.Regular instantiation

2. Factory methods

3. Static classes

4. Singleton

5. Dependency Injection Container

In many cases, we will inject an interface and not the class itself (for test purposes and decoupling).

6. Extension methods (in special cases and in some languages)

Types of Classes

We can’t claim one type of instantiation is better or worst because each serves different purposes. Something that would fit one type of class, won’t fit another. Let’s try to categorize class types then:

  1. Helper class with no dependencies
  2. Helper class with dependencies (for example, depends on the network or uses another class)
  3. Single-instance class with state (with or without dependencies)
  4. Multiple-instance class with state (with or without dependencies)
Helper classes would be classes that don’t have any state (data). For example, Math is a helper class, but Person with Name and Age fields isn’t.

That seems a strange way to categorize class types, but as far as instantiation is concerned, this is all that matters.

So what instantiation method first each class category?

Structure of this article

We’re going to over each combination of instantiation type and class category. That’s 24 combinations, so bear with me.

Along the way, we’ll talk about testing and mocking, dependencies, and general programming guidelines.

If you find yourself losing concentration, or you’re one of those people that likes to peek at the last pages of a book, there’s a cheat-sheet table in the end.

Let’s start with helper classes with no dependencies.

1. Helper classes with no dependencies

Helper classes with no dependencies

When a class has no state, we can define it as a “Helper class”. That means the class itself doesn’t represent any entity, but it helps other classes to achieve some functionality. For a class like that, it doesn’t matter if it has one instance or many.

A helper class with no dependencies has pure logic. For example, Math in .NET is a helper class.

There’s usually no point in mocking such classes because there’s nothing to mock, it’s just logic. For example in Math class, there’s no point to mock the Max method or Sin method.

Here’s An example of a helper class that adds functionality to arrays:

Now comes the dilemma – which type of instantiation should we use?

Let’s go over our options.

Option 1: Regular Instantiation

With regular instantiation, every time you will want to use the functionality, the code will look like this:

If you don’t want to write that additional line of code, you can create an instance once and save _arrayHelper as a member.

Pros: Simple to understand, simple to use. The class can be modified later to derive from another, implement an interface or to add dependencies.

Less readable – Needs additional code each time (or save as member).
Seems a waste to create a new instance every time.
Creating a new instance implies the class has a state, which can cause confusion.

Verdict: Regular instantiation is a good option if you’re creating an instance in just one place or very few places. If it’s a utility class that’s used in many place (like Math), it’s not the best choice.

Option 2: Factory Methods

Here you would have a factory class that creates instances according to some logic:

A factory is used to create multiple instances, which doesn’t fit helper classes because all instances are the same (it has no state).

One exception when a factory might be useful is when you want a different strategy according to some logic. For example, in day time you should use helper-class-A and in the night time you should use helper-class-B. In this case, the helpers would implement a common interface.

Verdict: Don’t use factory methods for helper classes, unless you need to implement a strategy.

Option 3: Static Classes

Helper classes don’t need more than one instance since they have no state. This leads to the question of whether they need an instance at all? Here’s an example:

Static classes have a somewhat bad reputation. They can’t be mocked for tests, they can’t implement interfaces, and they act as GC Roots (in languages with a garbage collector) causing potential memory leaks. But, all that has nothing to do with helper classes that don’t need to be tested.

Short syntax
Readable code
No waste on multiple instances, which can cause performance problems due to extra garbage collection.

If your class will need to change from a static to a regular class, it will be relatively hard to refactor. This might happen if you need to include dependencies or implement an interface.
In some rare cases, you will need to mock your helper class to fake functionality. Static classes are notoriously hard to mock.

Precedence: .NET Framework itself uses static classes extensively. These would be the classes Math, File, Path, and so on.

Verdict: If your class is going to stay a stateless helper class with no dependencies, a static class is a good choice.

Option 4: Singleton

A singleton is a great pattern to ensure there’s only a single instance of a class. It’s fallen a bit out of fashion, now that we have dependency injection frameworks, but it’s still viable in some cases. Especially if you have a small library where you don’t want to develop/include a dependency-injection mechanism.

For the purpose of a helper class, there’s really no point for a singleton pattern because there’s no point to ensure there’s a single instance.

Option 5: Dependency Injection Container

Dependency injection frameworks became very popular in recent years (many recent years). It serves several important purposes:

  • Allows dependency inversion (inversion of control)
  • Allows loose coupling – You can know the interface, but you don’t have to know the implementing class
  • Single responsibility principle – Your class doesn’t have to worry about its dependencies. It just has to do its one job, trusting that someone will implement the missing pieces (the interfaces it communicates with).

In practice, dependency injection makes it easy for us to do these:
1. Easily inject mock classes for tests
2. Easily add dependencies to any class (usually just add a parameter to the constructor)
3. Easily inject different implementation (according to configuration or whatever) to the entire application.

Getting back to the problem at hand, should we be using dependency injecting for our helper class?

Since we never need to mock our class, and it doesn’t implement any interfaces, there’s not much point.

Verdict: Dependency injection for helper classes is possible, but doesn’t add any of the advantages dependency injection offers.

Option 6: Extension Methods

Extension methods allow to add functionality to a class from different places in code, even after the original class was already compiled. Here’s how we would do it in our example:

From first glance, this is a short and readable syntax. Though it can be confusing if you’re not familiar with extension methods.

A great example of this is LINQ in C#. All the method syntax is built on extension methods and it’s been one of the most popular programming features created in the last 10 years.

It’s important to use extension methods without additional dependencies, and when the extension method is relevant to the extended type. An example of a terrible extension method might be extending int to update bank account balance:

Getting back to the problem at hand – will extension methods be a good fit for helper classes with no dependencies?

Great syntax
Help from the IDE intellisense

Extending widely used types (like primitives) can become confusing in a big application.
When creating inappropriate extension methods (like newBalance.UpdateDatabase() ) you allow for potential major mistakes.

Verdict: Extension methods are great when the code is very relevant to the extending type.

2. Helper Classes with Dependencies

Helper Classes with Dependencies

As mentioned before, helper classes have no state and it doesn’t matter whether there’s a single instance or multiple instances because all instances act the same. However, those classes might have dependencies. These might be the File System, Network, Databases, or other classes.

An example of such a class is the File class in .NET. It’s a static class that can write to files and read from files. It has a dependency on the file system but no state.

As an example, let’s create a class that does time zone conversion. It has a dependency on another class that provides the machine’s time zone.

The basic functionality is to calculate what time will be here when time in New York is X.


Before going further, let’s talk a bit about classes with dependencies. There are basically 2 ways you can work with dependencies:

1.Create-Yourself: You create a dependency yourself, either by creating an instance with new or by calling another class’s factory method or singleton method. For example:

2. Dependency-Injection: The dependency is injected to you from outside. This is mostly done in the constructor or by having someone else set your class’s property. For example:

Dependendcy Injection doesn’t have to be done with a dependency injection infrastructure (container). As you see, just passing an instance in a constructor is also dependency injection.

For test purposes, it’s always best to use dependency injection.

Let’s go over our options to instantiate a helper class with dependencies:

Option 1: Regular Instantiation

In regular instantiation, with the dependency injection method, you will do something like this:

But where will you the currentMachineTime come from?

By creating a new instance each time, you will have to take care to have the currentMachineTime as well. Not very convenient, unless it’s done in a single place where TimeHelper and its dependencies are both created.

If you’re creating the dependency yourself, regular instantiation is fine, but you’ll be paying the price when testing.

Verdict: Regular instantiation is a good approach if you have just one place (or very few places) to create the helper class.

Option 2: Factory Method

A factory method might be useful because it can be the one place to create all the dependencies, avoiding code duplication. If you’re not mocking this class, then it can be pretty convenient with a static Factory:

This is convenient but terrible for tests.

If you do need tests, then you’ll need to inject a factory interface, and in that case, you might as well inject the dependency instead of using the factory.

Verdict: When there’s no need for tests, a factory method can be convenient to manage dependencies. If you do need to mock the helper class or its dependencies, a factory is not a good option.

Option 3: Static Classes

Static classes are usually not a great choice for anything with dependencies. As mentioned, they are notoriously hard to test.

Injecting dependencies to a static class is pretty much impossible. You can set a property from outside, but there’s no guarantee in code that another class won’t use the static class beforehand.

When no injections or tests are needed, it can be very convenient. For example, when your helper class depends on something like the file system.

Verdict: Use static helper classes only when you don’t plan to test them or mock them to test other classes.

Option 4: Singleton

Like with helper classes that have no dependencies, there’s really no point for a singleton pattern because there’s no point to ensure there’s a single instance.

It can save garbage collection effort, but in almost all cases you’re better with regular instantiation, static classes or dependency injection.

Option 5: Dependency Injection Container

For any class that has dependencies, dependency injection is a great choice. This allows you to mock the class or its dependencies, and to easily change implementations.

Although it doesn’t really matter in terms of functionality, it’s best to set the injection to use a single instance each time.

Pros: Great for tests. Very convenient if you already got a dependency injection framework in place.

Cons: Requires to have a dependency injection mechanism.

Option 6: Extension Methods

Consider this code:

The syntax is decent but note that you’ll have to pass the dependencies as parameters – not very convenient.

For tests, it actually works well. Since the dependencies are passed as parameters, you’ll be able to pass mocks easily. The class itself (TimeMachine) cannot be mocked (it’s static), but it doesn’t really matter because it’s just logic.

Verdict: Not very convenient, but can be a good fit in cases where the extended type is very relevant to the method.

3. Single-instance classes with state

Single-instance classes with state

Such classes can represent an Application State, Application Configuration or something similar. Something like that should be protected to have a single instance.

These classes can also be some big manager or service type of class, that orchestrates your application’s business logic.

They often need to be mocked for test purposes (if you do have tests).

Option 1: Regular instantiation

Usually, regular instantiation doesn’t make sense for a class that has only one instance. But, in special scenarios, in a carefully reviewed program this is a valid choice. You will create that one instance somewhere in the application bootstrap and pass it (inject it) to all the necessary services. For example:

This makes sense when you don’t want to use a dependency injection framework.

Testing is very easy with the above method. Since everything is injected, you can create your own InitApp method that’s used just or tests and initializes mock instances.

I mentioned several times that you might not want a dependency injection framework. There are scenarios when this makes sense. If, for example, you’re creating a library and you don’t want to add an additional dependency.

Option 2: Factory Methods

A factory makes no sense for single instance classes.

Option 3: Static Classes

A static class with state has the same problems as mentioned before:
1. It can’t be (easily) mocked for tests.
2. You can’t inject dependencies to static classes.

Verdict: Use static helper classes only when you don’t plan to test them or mock them to test other classes. If your application has has a dependency injection framework, prefer dependency injection. I personally avoid using static classes that have a state.

Option 4: Singleton Pattern

A singleton is a very reasonable choice for single-instance classes. It ensures a single instance, available throughout the application and easily implemented.

It’s fallen a bit out of fashion in favor of dependency injection with some good reasons:
It’s hard to mock a singleton.
It’s hard to inject dependencies to a singleton. A singleton can create dependencies on its own, but then everything becomes even harder to test.

Verdict: A singleton makes sense if you don’t have tests and don’t plan to add tests. If you do have tests and don’t want to use dependency injection, you can still mock singletons with some hacky code. For example, here’s one variation:

I won’t judge you.

Option 5: Dependency Injection

All dependency injection frameworks have an option to create a single-instance class. If you already have such a network in place, this is a great choice.

Pros: You’ll be able to easily mock your class, inject other dependencies to your class, and pass the instance to other classes.

Cons: You need a widely used dependency injection in place. If you want to use your class from a place that doesn’t have access to the dependency injection container, then it will be a problem.

Option 6: Extension methods

It makes no sense to have extension methods in a class that has state. Extension methods exist just to extend the functionality of other classes.

4. Multi-instance class with state

Multi-instance class with state

Multiple instance classes always have state, otherwise there’s no point of them being multi-instanced. These can be Model classes like Customer or ShoppingCart. These also can be classes with logic inside like AlgorithmStep.

They can have dependencies or not. For example Customer might be a Plain Old CLR Objects (POCO) class, that has just fields and no dependencies (other than other POCO classes).

On the other hand, a class like AlgorithmStep can have dependencies on application settings or a bunch of other things.

Option 1: Regular instantiation

For any multi-instance class, this is the de-facto methodology. Just create an instance and use it. For example:

You can inject dependencies in the creation. For example: var customer = new Customer(_emailValidationService)

Pros: Easy to use, easy to understand.

1. When you have a lot of dependencies you want to add, this might prove to be hard.
2. When you want to add some logic to each new instantiation, then you’ll have to use other options, like a Factory.

Option 2: Factory

Although a factory was designed to create multiple instances, we don’t always need to use one. Here’s why we would use a factory instead of regular instantiation with new:

1. A factory can be a convenient way to store and inject dependencies.
2. A factory is great when you need to add a certain logic on each object creation. For example:

We can place the same logic in Customer constructor but it creates a very nice separation of concerns when using a factory.

As for tests, you should be able to mock the factory itself or mock the factory’s dependencies. A factory is a helper class, (with or without dependencies) so see above how to instantiate helper classes for tests.

Verdict: A factory shouldn’t be the default choice, but it’s a good choice for specific needs. For tests, you might need to mock the factory, which isn’t trivial.

Option 3: Static Classes

A static class can’t have multiple instances by definition, so it’s out of the question.

Option 4: Singleton

A singleton also can’t have multiple instances, so out of the question as well.

Option 5: Dependency Injection Container

With a dependency injection frameworks, you can write:

This is useful when MyClass has a lot of dependencies of its own. They will be injected automatically in the constructor by the dependency injection framework.

Another usage is when your class is required for each new instance of another class:

For tests, this method is perfect. You will be able to mock whatever you need by providing a dependency injection bootstrap of your own in the tests.

Verdict: Dependency injection is useful for testing, to easily inject dependencies, and if your class is a dependency to other classes. When there’s no need, avoid using it because it provides less intuitive syntax and overhead.

Option 6: Extension Methods

Extension methods are static classes, so they can’t have multiple instances.


Software design is one of the most interesting parts of programming. It’s challenging and hard to get right. Everything I described above is something experienced developers “feel” intuitively. It takes time to develop that feel, but if you’re a junior developer, you’ll get there.

These guidelines are great food for thought but don’t really solve the dilemma, even if you accept them as the God-given truth. For one thing, you need to decide which category your class is going to be in the first place. Is it going to have state or not? Which dependencies will it have? Will you need to mock it for tests? This is is software engineering for you, and unlike electrical engineering, there are no hard truths.

I remind you that everything I wrote is my own take on this. In software design, there are no rules set in stone, and every case is different.

Here’s a summary of all the possible combinations:

instantiation summary table


Enjoy the blog? I would love you to subscribe! Performance Optimizations in C#: 10 Best Practices (exclusive article)

Want to become an expert problem solver? Check out a chapter from my book Practical Debugging for .NET Developers

10 thoughts on “Class Instantiation Guidelines in Object Oriented Languages: When to choose Singleton, Static, Extension methods or Dependency Injection”

  1. I find singleton dangerous… I agree it is a rather good fit in the “single-instance with state” case. But even though, I think I’d rather give up the one-instance constraint or at least hide the “Class.Instance” stuff behind a provider-like facade (for ease of mocking).

    By the way, in this case DI is also my prefered solution; though, it does not prevent users from instantiating multiple times the ‘singleton’ by hand if they wish (unless the DI container can create instances by reflection from private constructors…).

    Concerning extension methods, I’m a huge fan of them. And agreeing with you, in cases where the extended type is not a primitive. It also makes very much sense in order to wrap traditional code into fluent APIs (as Linq is nothing else after all). In order to lighten my interfaces and classes, I regularly pair them with extensions that simply provide variant usage of boilerplate over the extended type.

    I also liked the “just passing an instance in a constructor is also dependency injection.”; in fact it recalls me of a rule I try to apply (and instill into my dev team): When in doubt between inheritance and composition, prefer the latter… reusing your example, many of my colleagues would have gone for TimerHelper inheriting CurrentMachineTime if it was possible. It’s quicker to write but a hassle to maintain/mock… Inheritance vs Implementation vs Composition vs Extension; maybe a subject for a future post?

    Keep up good blog posts!

    1. Thanks Olivier!

      Inheritance vs Implementation vs Composition vs Extension is a good idea for a post, maybe I will.
      I agree with regards to inheritance vs composition. I try to avoid inheritance altogether actually, implementing interfaces, inheriting just abstract classes and using composition (containment) instead.
      This might be a bit extreme, but maybe I’ll be able to express it with a rational explanation one day.

  2. David V. Corbin

    Many excellent points. One thing I did not see is a study of immutability of implementation as the overall application evolves over time. Minimizing the need to go back and revise code as a growing burden as the years pass…

    1. Hi David,
      That’s an important issue you’re raising, which I experienced many times myself. New patches are added to old code until the current design is no longer suited. Developers don’t have time to refactor or don’t want to take the risk of breaking anything. Eventually, the old code becomes very hard to maintain and the development is somewhat crippled.

      I don’t have a good answer on how to solve this. Maybe have a good test suite to allow refactorings, and evolve a culture that encourages such refactorings.

      1. David V. Corbin

        There is no magic…. But the biggest elements I have found:

        1) Avoid “new”, except for *very* stable classes (e.g. DateTime *may* be stable enough.
        2) Use many small interfaces [ISP – or the “I” in SOLID]

  3. IMO you forgot to mention that in case of Multi-instance classes with state also the lifetime of a class matters.
    For example if the parent class has long lifetime but the dependency class, for some reason, needs to have as short lifetime as possible. In most cases you can control the lifetime in your DI container, but in very specific case or if you wish not to use DI container then the right choice would be factory.

    1. Artur, I’m not sure I understand your meaning. How can the DI container control an object’s lifetime? It will just be garbage collected when it’s no longer referenced.

  4. You failed to mention the Factory Method Pattern (The one from the GoF Book) not the Factory you mention. This pattern is ideal for decoupling dependencies as well as allowing for testing if/when needed. Sadly this pattern is totally confused with the Factory pattern (as you have done in this post as well). There is a difference between a method that’s a Factory (the Factory pattern you mention here) and the Factory Method Pattern as described in the Gof Book and in this video:

Comments are closed.