1

I have a struct which I want to add static methods to. Yes, you guessed right I'm talking about Datetime.

It's a pretty typical requirement to add MyCustomParse. It happens to return Datetime?, so it's not a constructor, but it's in that area, a static method that returns an instance. The content of this method has already been written and currently happy lives as a instance method of a class that uses it. I want to bring it out of that class so I can use it elsewhere.

Traditionally I would put it in a helper class (which is what I've done here), but apparently they're evil.

What I really want to do is tie Datetime.MyCustomParse(mystring). But the only way I can see is to make a new struct that composes Datetime. I then add in my method, write the implicit conversions and then put in all the other Datetime methods etc. This would be a pain to code (Being verbose also causes problems wrt xml docs and naive code coverage metrics, if that counts for anything).

I have considered extension methods, but I don't think the methods make sense on instances.

What is the strict OO position? Do I get a pass on writing methods that should belong to classes in separate helper classes when I want to extend structs in this fashion?

What is the pragmatic position (I'm not saying OO isn't pragmatic), is this code you would write, or have I missed a trick?

internal static class DateTimeHelper
{

    public static DateTime? MyCustomParse(string utcDateTimeText, string[] formats)
    {
        DateTime utcDateTime;
        if (DateTime.TryParseExact(utcDateTimeText, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out utcDateTime))
        {
            return utcDateTime;
        }

        return null;
    }
}

PS I know there are more nuanced views on helper classes, but I still feel I need to justify my use here.

  • Umm what? DateTime already has a Parse... – Telastyn Aug 7 '14 at 13:08
  • @Telastyn. Yeah, I can see how that's unclear. I would have to rename it if I stuck it in with DateTime. I've named to to MyCustomParse to prevent confusion. – Nathan Cooper Aug 7 '14 at 13:11
  • Traditionally I would put it in a helper class (which is what I've done here), but apparently they're evil. I think you've created a problem for yourself by buying into this opinion. If you have some frequently-used computation involving some class, but doesn't need access to any class's private state, it only makes sense to make it a static method. Why introduce some isomorphic type every time you want a new helper function and then jump through hoops converting back and forth? Also, note that an extension method is just syntax sugar. – Doval Aug 7 '14 at 13:12
  • Helper classes are not evil. The article mentioned uses logical fallacies. So, really it is NOT the subject of discussion. But still I'll give an example. If you want to extend the functionality for the non-extendable classes, for example, String, the helper classes are the only solution. Oh, since Java 8 the interfaces can serve, too. It is only one example, but there are many of them. – Gangnus Aug 7 '14 at 13:55
  • Helpers as a place to shove code that doesn't fit anywhere else could be described as less than ideal (not sure "evil" works here). That is typically an indicator of a bad design. Classes that are full of static methods (as the author defines helpers) are not bad, however. They are quite useful in creational design patterns, but not strictly necessary. – user22815 Aug 7 '14 at 21:22
3

What is the strict OO position?

"strict" is undefined in OO space?

What is the pragmatic position? I want to bring it out of that class so I can use it elsewhere. The content of this method has already been written and currently happy lives as a instance method of a class that uses it.

OO Rationalization

Well, one approach may be to think about creational design patterns. I see this as counterpoint to the "helper class" idea. I see the "helper" as a smorgasbord - incoherent in terms of domain object design.

But a Factory, Builder, etc. composes behavior within an object- a domain-coherent thing. How is this different from a helper class? Intent.

OO Pragmatism meets Intent

Refactor to turn the method into a delegate. You can declare the delegate at the namespace level if necessary.

Write a instance-building something that registers/binds/whatever the delegate method. I expect the method's implementation will be inside this something.

Somthing's purpose in life is to instantiate fully-realized objects of the class/struct. Something does not contain those "random" type-specific (but not instance-owned) helper methods.

And because of intent we do not want the pragmatic approach of a class static method - which you have already dismissed.

  • An object is overkill for his example. He doesn't need any internal state, he just needs 2 inputs in and 1 output out. – Doval Aug 7 '14 at 19:24
  • So... Rename DateTimeHelper to DateTimeFactory? – Nathan Cooper Aug 7 '14 at 21:18
  • 1
    @NathanCooper Essentially, your DateTimeHelper is acting exactly like a DateTimeFactory would, so here, yes, just rename it, so that the class's intent is clearer. If your class had more, unrelated helpers on it, you would instead split off the factory method into a factory class. The problem with "helpers" is that often they do not adhere to Single Responsibility, whereas a factory has the single responsibility of object construction. Some (myself included) prefer factory instances, but some (myself included) will make static factory methods instead. YMMV. – cbojar Aug 8 '14 at 0:58
2

Why not use an extension method?

public static class DateTimeExtensions
{
   public static DateTime? AsDateTime(this string value, string[] formats)
   {
       DateTime utcDateTime;

       if (DateTime.TryParseExact(utcDateTimeText, formats,CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out utcDateTime))
       {
            return utcDateTime;
       }

       return null;
   }
 }

This will extend the string type adding the method AsDateTime.

string[] formats= {"M/d/yyyy h:mm:ss tt", "MM/dd/yyyy h:mm:ss tt" }; 
string unParsedDateTime = "05/01/2009 6:32:05 PM";

DateTime parsed = unParsedDateTime.AsDateTime(formats);
// or  
parsed = "05/01/2009 6:32:05 PM".AsDateTime(formats);
  • This answer is code-only. Code-only answers are considered bad quality here. Could you elaborate about how your solution works ? – Tulains Córdova Aug 7 '14 at 21:13
1

Helper classes can be an organizational anti-pattern when you have some generic Helpers file where you dump every single miscellaneous static function all your projects have ever used. Don't do that. Regardless, they're not evil. A static method is the right place to put some frequently-used computation that don't need to look at any object's private state. Functions like MyCustomParse don't really belong in any class - it can get the job done without looking at anyone's implementation details, so it's not part of any given abstraction. It's just one of the many ways you could choose to convert between two representations of dates.

Creating wrapper classes just because it "feels" more OOP if the function is attached to an instance of something will just cause you and future code maintainers headaches. Besides having to convert back and forth between the wrapped and wrapper types, you'll either have to edit the wrapper or create new ones every time you need a new helper method - and in the latter case you'll have even more conversions. It doesn't scale.

Extension methods can let you use helper functions with the syntax of method calls, which can be nice for Intellisense or when a function call looks better in infix form. This is pure syntax sugar; semantically there's not much of a difference between extension methods and regular static methods.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.