views:

347

answers:

9

I have just refactored a colleague's code that, roughly, looked like this...

public class Utility
  public void AddHistoryEntry(int userID, HistoryType Historytype, int companyID)
  {
    // Do something...
  }
  public void AddHistoryEntry(int userID, HistoryType historyType, int companyID, string notes)
  {
    // Do something...
  }
}

To this...

public class HistoryEntry
{
  public long UserID { get; private set; }
  public HistoryType HistoryType { get; private set; }
  public long CompanyID { get; set; }
  public string Notes { get; set; }

  public HistoryEntry(long userID, HistoryType historyType)
  {
    this.UserID = userID;
    this.HistoryType = historyType;
  }
}

public class Utility
{
  public void AddHistoryEntry(HistoryEntry entry)
  {
    // Do something...
  }
}

}

Now, this is much better code design and is an Uncle Bob favourite. However, my colleague argues that it is so much more expensive to new-up an object every time we want to call this method.

Is he correct?

Further Explanation

Thanks for all the responses so far. I left out many details in the hope of brevity, but some of them have caused issues with the answers.

  • Utility class doesn't exist. I just wanted to put the methods in a class for you all to see. In actuality it's in a sensible class.
  • There were, in fact, 5 AddHistoryEntry methods in the original code. All of which took a lot of int parameters. One reason for the refactoring was that AddHistory(0, 1, 45, 3); doesn't really tell you much!
  • AddHistoryEntry is not called from a tight loop, but it is widely used throughout the application.

Corrections

I've now updated the code examples as I had made a mistake with some of the parameters.

+5  A: 

Why not a static method in the class itself?

public static void AddHistoryEntry(HistoryEntry entry)

Saves you the utility class. The new object may be expensive, it may not--but it probably doesn't matter. Good defaults allow you to extend your code much more easily, and you'll save more time in maintenance than you do in performance, unless you're doing this in a loop.

Broam
AddHistoryEntry should really be a member of SOME useful object, no? It doesn't operate in a vacuum. But if you absolutely need a static utility class, ok... fine. Would AddHistoryEntry be better off as part of a class like History.. so you could say myHistory.addHistoryEntry(HistoryEntry entry) ?
Chris Kannon
We really can't assume that it's possible to make that into a static method when we don't know anything about what the method really does. Static methods with side-effects are also generally a bad idea. Sorry, but that's a -1.
Aaronaught
I've responded to this in my main question.
Chris Arnold
+1  A: 

Is this really a performance bottleneck? If not, then it would be a clear case of premature optimization to avoid using objects in this case. Maintainability and design should take priority over performance until you start hitting significant performance issues.

Will Vousden
I think @Chris Arnold was just curious.
Jim G.
+19  A: 

He might be correct if you have millions of these objects in memory simultaneously. But if you don't, then he's bringing up what is almost certainly a moot point. Always choose a better design first, and then modify it only if you're not meeting performance requirements.

Kaleb Brasee
19 votes? Wow. I'm impressed.
David Lively
+13  A: 

Creating a new object is very cheap. You'd have to create a lot of objects in a very tight loop to notice any difference... but at the same time, it's not free. You also need to bear in mind that the costs are half hidden - you have some cost upfront when you create the object, but also it means the garbage collector has more work to do.

So is a cleaner design worth the performance hit? We can't tell you that - it depends on how your code is used. I strongly suspect the performance hit will be insignificant, but if you are using this in a tight loop, it may not be. There's only one way to find out though: measure it if you're concerned. Personally I'd probably just go for the cleaner design and only check the performance when it looked like it was becoming a problem.

(If this method is hitting disk or a database, by the way, the difference is almost bound to be insignificant.)

One suggestion, by the way: does your HistoryEntry type actually need to be mutable? Could you make all the properties read-only, backed by private read-only variables?

Just to mention Will's point - I somewhat agree with him. If these two methods are the only places you need this concept, I'm not at all sure it's worth creating a new type for the sake of it. Not because of the performance aspect, but just because you're introducing an extra bunch of code for no demonstrated benefit. The key word here is demonstrated - if you're actually using the same collection of parameters in other places, or could usefully put logic into the HistoryEntry class, that's a different matter entirely.

EDIT: Just to respond to the point about multiple integer arguments - C# 4 will allow named arguments which should make this easier. So you could call:

AddHistoryEntry(userId: 1, companyId: 10);

Making the names visible with an additional class needs one of:

  • Named constructor arguments from C# 4 (in which case you're no better off than with the method)
  • Mutable types (urgh - that could lead to hard to trace bugs which wouldn't have been in the original version)
  • Long static method names to construct instances ("FromUserAndCompanyId") which become unwieldy with more parameters
  • A mutable builder type
  • A bunch of "With" methods: new HistoryEntry().WithCompanyId(...).WithUserId(...) - this makes it harder to validate at compile-time that you've provided all the required values.
Jon Skeet
+1 for mentioning refactoring with very little DRY benefit (conceptual benefit notwithstanding).
gWiz
Hmmm, I'm not so sure about the refactoring remarks (talking to you too here, Will!). The single most beneficial refactoring technique (well, *potentially* beneficial, and I *might* be exaggerating a little) doesn't have any DRY benefit: http://www.refactoring.com/catalog/renameMethod.html.
Jeff Sternal
@Jeff: Note that I didn't actually mention DRY - I just said there was no demonstrated benefit, and I still believe that's the case. The term "HistoryEntry" is already present in the first form (in the method name) so it's not adding any context there...
Jon Skeet
@Jon - agreed, and in retrospect I realize my remark was a non-sequiter; the primary motivation for the extract class refactoring is to avoid repeating ones' self, while that's not true for rename method (which would have to justify itself by actually correcting a bad method name).
Jeff Sternal
My wife has been reading this over my shoulder and asked 'What does DRY mean again?'. Priceless.
Chris Arnold
+4  A: 

You're both wrong, but he's more wrong than you.

Will
Okay, I can see it coming, so I'll be more clear. You're refactoring (it appears) for refactoring's sake and he's optimizing prematurely.
Will
+1, although far more for the comment than the actual answer. (I suggest you edit.) I've touched on this in an edit to my answer, too.
Jon Skeet
@jon I know, its more appropriate in the answer. However, the artist in me refuses to destroy a beautiful (eye, beholder) reply for practical reasons. It can be read so many ways and therefore spurs the mind to think about the implications of the path you take through another's code. Or something.
Will
"He" may be "optimizing prematurely" or just be ignorant of design.
gWiz
The reason I refactored it was that it was hard to understand in its current state.
Chris Arnold
@chris just going on the tip of the code-iceberg, you know. It does seem a bit pointless... but I have to say pointless refactoring is one of my guilty pleasures.
Will
+4  A: 

It's expensive relative to not creating objects in .NET, in the same way that 1 is a large number relative to 0. It all depends on your perspective. If this is in a loop that's being run 20 million times, I might worry; otherwise, I wouldn't care.

Having said that, I'd like to point out that Utility is not a good name for a class, and refactoring into a HistoryEntry also likely requires you to verify that the parameter being passed in is not null, a requirement that you did not have originally when all parameters were value types.

It's also commonly considered an anti-pattern to have objects with data but no behaviour, which HistoryEntry seems to be. Try to avoid doing this too often.

Aaronaught
+1 for `Utility`. Just chunk everything into a single class called "Stuff" whydontcha?
Will
quixoto
A: 

It is slightly more computationally expensive, but for that cost you get the benefit of cleaner, more conceptual code that can more quickly respond to future business demands. Performance vs agility.

gWiz
+2  A: 

It is not really expensive to create objects in .NET. In fact, object creation is very fast, even if you create millions of them.

However, garbage collection is not cheap. If you create a lot of short-lived objects, you may experience considerable slowdown when gc kicks in. It makes sense to avoid creating new short-lived objects if creation runs thousands or millions of times. If, for example, you create several objects with every network packet received by a server with thousands of connections.

That said, it is never wise to avoid calling new before demonstrating that lots of objects really do slow down gc. In your example, they most probably would not.

Nevermind
A: 

No. The garbage collector is optimized to be highly efficient when dealing with short-lived objects.

Rafa Castaneda