IOC - Should util classes with static helper methods be wired up with IOC?
Asked Answered
A

4

28

Just trying to still get my head around IOC principles.

Q1: Static Methods - Should util classes with static helper methods be wired up with IOC?

For example if I have a HttpUtils class with a number of static methods, should I be trying to pass it to other business logic classes via IOC?

Follow on questions for this might be:

Q2: Singletons - What about things like logging where you may typically get access to it via a Logger.getInstance() type call. Would you normally leave this as is, and NOT use IOC for injecting the logger into business classes that need it?

Q3: Static Classes - I haven't really used this concept, but are there any guidelines for how you'd typically handle this if you were moving to an IOC based approach.

Thanks in advance.

Aretina answered 26/3, 2010 at 20:46 Comment(0)
T
33

The funny thing about IoC is that objects written in the style are generally decoupled from those details.

Let's use the utility class as an example:

public class HttpUtils
{
    public static void DoStuff(int someValue)
    {
        // ...
    }
}

In a non-IoC-focused application, you might use that method directly:

public class Foo
{
    public int Value { get; set; }

    public void DoStuff()
    {
        HttpUtils.DoStuff(Value);
    }
}

However, that couples the definition of DoStuff directly to its implementation. IoC strives to divorce those kinds of details, so instead we define the operation on its own:

public interface IDoesStuff
{
    void DoStuff(int someValue);
}

Then, we leave room in Foo for the implementation to change:

public class Foo
{
    private readonly IDoesStuff _doesStuff;

    public Foo(IDoesStuff doesStuff)
    {
        _doesStuff = doesStuff;
    }

    public int Value { get; set; }

    public void DoStuff()
    {
        _doesStuff.DoStuff(Value);
    }
}

This decouples Foo from HttpUtils. The implementer of the concept of DoStuff is now a configuration detail, and not an inherent dependency (as it is with the static method).

Notice that Foo has no idea if its IDoesStuff is a singleton or not. That lifetime is also a configuration detail, and not an inherent detail of Foo.

To sum up, IoC and static are generally at odds, since IoC promotes change and static, by definition, prevents it. Declare your dependencies in your constructors, and you'll find you almost never have you use static functionality.

Though answered 26/3, 2010 at 21:8 Comment(11)
thanks Bryan - Does this mean there might be some more overhead of the IOC constructing things (e.g. logger) everytime? Also for the utils type classes, I'm wondering will this make refactoring harder as you refactor your helper functions amongst your helper classes? (I am using ReSharper myself) - thanksAretina
Most IoC containers allow some sort of scoping. By this, I mean you indicate whether your object is created every time (factory), created once per context (unit of work), or once per application (singleton). This means you can register the logger such that it only gets created once.Though
Ultimately you want to eliminate the utility classes. Every method on each of those classes has the same coupling issues as the HttpUtils.DoStuff method above. For that reason, you don't want to require that any code depend directly on static members. Instead, take the body of HttpUtils.DoStuff, put it behind IDoesStuff, and delete the method entirely from HttpUtils. Now, any class which may have called the static method can instead accept IDoesStuff in its constructor. Viola: no more need for the utility class!Though
oh, one last question Bryan if I may: would you normally recommend packaging utils classes into their own VS class library - would this be a useful way to go?Aretina
I recommend you organize your project elements by feature, not by technical details. This means that web-specific utility classes would go with other web-related things, and data-specific utility classes would go with other data-related things. It wouldn't make much sense to mix web- and data-related utility classes just because they are utility classes. So to answer your question, there are other ways go to which are far more useful.Though
There are situations where this is misleading and bad practice. Consider some mathematical function, e.g. if Math.cos was not part of the core language. Then if you implement a class, "Math" and inject that into each class that needs to make use of Math.cos you're implying that Math itself is something that changes to someone who reads it. Not only that but you have generated a bunch of useless code to get the dependency into the class in the process. In this case, all of the methods in Math should be static and there's no need to register it with the container.Toney
There's another slippery slope to doing things this way too - suppose that you go ahead and make all of your methods that might be static non static and inject them. There's a risk now that you're going to end up pushing methods that should be static into some class that is inherently non static. Now, every time someone implements that interface they have to define an implementation (or inherit one) for your (should be) static method.Toney
@Toney Stateless methods such as the Math functions have no dependencies; they are excluded from the conversation by default. The nature of an HTTP server is context-bound, from its location on disk to the current request. My interpretation of the question centered on these details, and not on those such as URL encoding with well-defined semantics.Though
Specifically what I'm addressing is the conclusion here: "you'll find you almost never have you use static functionality". While you don't have to use static functionality there are many situations where it's better to do so because it's semantically appropriate and less cumbersome. IMO the "Math" class and a "Logger" class are similar in this respect. Loggers should not be injected because they are singular and needed in every class. Injecting them is going to result in a lot of unnecessary clutter. While Math has no dependencies, other things do depend on Math. They don't need Math injected.Toney
The definition of sine and cosine do not change. The implementation behind a log may. That makes the log context-bound in a way that Math is not. I agree with you for all context-free code, including LINQ, all the Parse and various helper methods, that static is awesome - for anything without state that changes over time. I disagree that a log, or a unit of work, or anything else context-bound should be a hidden detail. See my answer here for a better discussion of your example: #2663342Though
That said, I will certainly emphasize this distinction in the future.Though
C
9

An IoC container is typically useful to inject object that have state; or classes or interfaces that have more than one one implementation, even if the second implementation is a mock for testing purposes. If neither of these is true, you gain nothing by injecting it. the most common idiom these days is to have your class fronted by an interface that the real and mock implementation can both implement.

1) Static methods on helper classes - No, these do not often get injected by IoC. Typically they are stateless utilities.

To use a very simple example, you don't need two versions of a utility method called StringUtils.Reverse(). You only need one, and you can easily write tests around it because it has no state or dependencies, so there's absolutely no benefit to mocking it out. Example test:

string reversedString = StringUtils.Reverse("input");
Assert.AreEqual("tupni", reversedString)

If the utility isn't really stateless (e.g. depends on HttpContext.Current), then you should make the dependency explicit by injecting it, and not making the utility static.

2) Singletons: Often yes, singletons are injected. But a good thing about IoC is that you worry less about whether there's only one of something or not. You gain flexibility in instancing by using IoC. The decision to have a particular type as singleton or new instance each time becomes part of the IoC container's config and nothing else in the code needs to change.

So singleton-hood stops being a separate concern that has to be coded into the class (and classes having multiple concerns when they don't have to is bad), and it becomes the concern of the IoC container. You don't code the class "as a singleton" with anything special like a private constructor and a public static GetInstance() method any more, you just code it for the main concern, while the IoC container's config specifies if it's a singleton or not, or somewhere in between, like one instance per thread.

3) Static classes - are the natural home for static methods. Consider making the static methods extension methods where appropriate. You can't inject these classes, since you can't create them. Using static classes makes for procedural not object-oriented code. This isn't a bad thing for small helper methods, but if the majority of the code is like that, then you're not using the powerful OO features of the .Net platform.

Calyx answered 14/6, 2010 at 20:55 Comment(1)
Great suggestion to turn static methods into extension methods!Burkes
C
2

Static methods by definition do not need an instance. DI/IOC aims to satisfy interfaces with concrete classes and given that your static class along with its static methods by definition cant implement an interface or extend a class the question makes no sense. Theres no point in passing the helper class because one does not need the instance to use the static methods. You code will always execute the same static helperr methods even without an instance.

In an IOC/DI powered application one would define interfaces and have at least one implementation. Its all about managing instances and their dependencies.

Cabala answered 17/6, 2010 at 12:58 Comment(0)
B
0

The dilemma arises when the utility classes, say , need access database. While db accessor need Ioc, thus utility class has to use Ioc as, so it cannot be static.

But I really want utility class to be static so easy to be consumed. I don't want to populate constructor of each consume classes who need utility classes.

Consume classes itself might not even need db access themselves. So I don't want to inject db accessor and pass it into utilities classes as well.

Guess there is no perfect solution for now. One day I hope we go one step further, in addition to constructor/property injection, there is "global context injection/configuration", or "static injection", apply Ioc beyond object creation.

Think about, why not?

Batholomew answered 5/11, 2013 at 18:32 Comment(1)
Is this an answer, or just a comment on the question?Lowson

© 2022 - 2024 — McMap. All rights reserved.