This really depends. If the values your helpers operate on are primitives, then static methods are a good choice, as Péter pointed out.
If they are complex, then SOLID applies, more specifically the S, the I and the D.
Example:
class CookieJar {
function takeCookies(count:Int):Array<Cookie> { ... }
function countCookies():Int { ... }
function ressuplyCookies(cookies:Array<Cookie>
... // lot of stuff we don't care about now
}
class CookieFan {
function getHunger():Float;
function eatCookies(cookies:Array<Cookie>):Smile { ... }
}
class OurHouse {
var jake:CookieFan;
var jane:CookieFan;
var cookies:CookieJar;
function makeEveryBodyAsHappyAsPossible():Void {
//perform a lot of operations on jake, jane and the cookies
}
public function cookieTime():Void {
makeEveryBodyAsHappyAsPossible();
}
}
This would be about your problem. You can make makeEveryBodyAsHappyAsPossible
a static method, that will take in the necessary parameters. Another option is:
interface CookieDistributor {
function distributeCookies(to:Array<CookieFan>):Array<Smile>;
}
class HappynessMaximizingDistributor implements CookieDistributor {
var jar:CookieJar;
function distributeCookies(to:Array<CookieFan>):Array<Smile> {
//put the logic of makeEveryBodyAsHappyAsPossible here
}
}
//and make a change here
class OurHouse {
var jake:CookieFan;
var jane:CookieFan;
var cookies:CookieDistributor;
public function cookieTime():Void {
cookies.distributeCookies([jake, jane]);
}
}
Now OurHouse
need not know about the intricacies of cookie distribution rules. It must only now an object, which implements a rule. The implementation is abstracted away into an object, who's sole responsibility is to apply the rule. This object can be tested in isolation. OurHouse
can be tested with using a mere mock of the CookieDistributor
. And you can easily decide to change cookie distribution rules.
However, take care that you don't overdo it. For example having a complex system of 30 classes act as the implementation of CookieDistributor
, where each class merely fulfills a tiny task, doesn't really make sense. My interpretation of the SRP is that it doesn't only dictate that each class may only have one responsibility, but also that a single responsibility should be carried out by a single class.
In the case of primitives or objects you use like primitives (for example objects representing points in space, matrices or something), static helper classes make a lot of sense. If you have the choice, and it really makes sense, then you might actually consider adding a method to the class representing the data, e.g. it's sensible for a Point
to have an add
method. Again, don't overdo it.
So depending on your problem, there are different ways to go about it.
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.
Best Answer
A really easy way out would be to just fob this off onto .NET configuration and use the parameterless constructor which will grab the SMTP settings out of this app's configuration. This will let you inject appropriate values as required and probably the most SOLID thing you could do if you are staying static. See microsoft's man page on System.Net.Mail configuration for more info.
But since you want to learn here I would start by eliminating the whole static method for sending email approach -- it is definitely better than handling all of this by hand anytime you want to send an email. You would be much better served using an interface to provide a facade for sending email and then swapping in implementations as you see fit. So you'll want something like:
Which might have an implementation that looks like:
The main advantage here is testability and swappability. Testabiltiy inasmuch as you can easily build a fake implementation that doesn't send mail but could be used in your unit tests to check the message is correct. Swappability meaning you can do things like swap in a completely different implementation like backhauling your mail out over something like mailgun or mandrill because you realized delivery matters.