views:

196

answers:

5

Although this is a fairly common problem, I am struggling with what the best way to approach it (if it needs approached at all in this case).

I have inherited a website (ASP.NET, C#) part of which contains a class full of static methods (it's a very large class, honestly). One method in particular is for sending e-mails. It has every possible parameter I can think of and it works well enough. However, the internals of that particular method are rather cumbersome to manage and understand due to the fact that everything is shoved inside - particularly when most of the parameters aren't used. In addition, it is somewhat difficult to handle errors, again, due to all the parameters for this one method.

Would it make more sense to actually have an EMail class which is instantiated when you want to send an e-mail? This just "feels" more right to me, though I can't full explain why. What are your thoughts on the way to go in this particular case? How about in general?

Thanks.

+3  A: 

Oh my gosh yes.

It sounds like its an old Classic ASP app that was ported.

It violates the single responsibility principle. If you can refactor that class. Use overloading for that function.

Daniel A. White
I think you meant "principle". Or maybe not (non-native English speaker)
Martinho Fernandes
@Martinho - thx. im the best of spellers and my first language is english.
Daniel A. White
+4  A: 

Here are the Microsoft guidelines for when to use static types, generally.

Some things I would add, personally:

  • You must use static types to write extension methods.
  • Static types can make unit testing hard as they are difficult/impossible to mock.
  • Static types enforce immutability and referentially transparent functions, which can be a good design. So use them for things which are designed to be immutable and have no external dependencies. E.g., System.Math.
  • Some argue (e.g.) that the Singleton pattern is a bad idea. In any event, it would be wrong to think of static types as Singletons; they're much more broad than that.

This particular case has side-effects (sending e-mails) and doesn't appear to require extension methods. So it doesn't fit into what I would see as the useful case for static types. On the other hand, using an object would allow mocking the e-mail, which would be helpful for a unit test. So I think you're correct to say that a static type is inappropriate here.

Craig Stuntz
+2  A: 

That is an example of the Utils anti-pattern.

It is always a good idea to separate those methods according on their responsibility. Creating an Email class is definitely a Good Idea™. It will give you a much nicer interface to use, and it allows you to mock out the Email in tests.

Martinho Fernandes
+4  A: 

What you're describing sounds like an example of the aphorism, "You can write FORTRAN in any language."

A massive class full of static methods is often (not always) a sign that somebody just didn't "get" OOP, was stuck in a procedural-programming mindset and was trying to twist the language to do what he wanted.

As a rule of thumb: If any method, static or instance, takes more than about 5 parameters, it's often a sign that the method is trying to do too many things at once, and is a good candidate for refactoring into one or more classes.

Also, if the static methods are not really related, then they should at least be split up into classes that implement related functionality.

I'm actually wondering why you'd have a "send e-mail" method at all, given that the System.Net.Mail namespace handles just about every case, and is configurable via the app.config/web.config file, so you don't need to pass it a server name or port. Is this perchance a "notification" method - something that individual pages are supposed to call out to in order to send one of several "standard" messages based on templates with various values filled in, and certain headers/footers automatically added? If so, there are a number of designs for this type of interaction that are much easier to work with than what you seem to have inherited. (i.e. MailDefinition)

Update: Now having seen your comment that this is being used for exception handling, I think that the most appropriate solution is an actual exception handler. There are a ton of resources on this. For ASP.NET WebForms, I actually took the one Jeff Atwood wrote years ago, ported it to C# and made a few changes (like ignoring 404 errors). There are a number of good links in this previous question.

My preference these days is just to treat exception handling (and subsequent e-mailing of exception reports) as a subset of logging. log4net has an SmtpAppender that's quite capable, and you can configure it to only be used for "fatal" errors (i.e. unhandled exceptions - in your handler, you just make a LogFatal call).

The important thing, which you'll no doubt pick up from the SO link above and any referenced links, is that there are actually two anti-patterns here - the "miscellaneous" static class, and catching exceptions that you don't know how to handle. This is a poor practice in .NET - in most cases you should only catch application-specific exceptions that you can recover from, and let all other exceptions bubble up, installing a global exception handler if necessary.

Aaronaught
You pretty much described what it is for - it is used as a way to notify the development team of any crashes. Basically, the method is called within any catch blocks (so, it's called relatively frequently). Past developers wanted to quickly be able to e-mail with only one line of code - hence the static method.
JasCav
A: 

See The Little Manual of API Design, which describes the benefits of classes having minimal constructors and lots of getters/setters over the alternative of using constructor/methods having many parameters.

Since most of the parameters of the methods you mention are not used, a better approach is to use simple constructors that assume reasonable default settings for the internal variables. Having setter methods allows you to then set the few parameters (and only those parameters) that require non-default values.

Loadmaster