How should I design a method that allows for optional operations?
For example, suppose I this:
class Gundam00 extends Gundam impleme开发者_Python百科nts MobileSuit {
...
public void fight(final List<MobileSuit> mobiruSuitso, final List<Gundam> theOtherDudes, final List<Person> casualities) {
....
}
}
Suppose theOtherDudes and casualities parameters are optional. How can I make this method as clean as possible? I thought about having booleans indicating if they're null, and then checking them as needed.
I could also have different versions of the method for each combination of parameters but there would be a lot of code duplication I think.
Any suggestions?
I find that past 2-3 arguments, the ability to remember what all the arguments to a function are suffers. And comprehensibility along with it.
Passing named arguments can help. Languages with a convenient hash-like literal syntax make this really easy. Take JavaScript:
g = new Gundam00();
g.fight({opponent: enemy, casualties: 'numerous'});
You can also take advantage of variable length argument features to work this in (treat odd arguments as names, even arguments as the actual parameters).
g.fight('opponent',enemy,'casualties', 'numerous');
And some languages actually support named arguments straight-out (see: http://en.wikipedia.org/wiki/Named_parameter#Use_in_programming_languages ).
Finally, you might want to consider adding other methods for this using what some call a Fluent Interface (http://en.wikipedia.org/wiki/Fluent_interface ). Basically, you've got method call which return the object itself, so you can chain calls together:
g.opponent(enemy).casualties('numerous').fight();
This might be the easiest option if you're working in a manifestly/statically-typed class-focused language.
Update
Responding to Setsuna's comment... in that last example, if you've got the luxury, you can make methods like opponent
and casualties
simple setters that don't affect any internal state or computation in any other way than setting a parameter for which they're named. They simply set internal properties up, and then all of the real work happens inside action methods like fight
.
If you can't do that (or if you don't like writing methods whose operations are sub-atomic), you could stake out a half-way spot between this idea with the hash-like literal idea, and create your own collection class specifically for invoking named arguments:
n = new NArgs();
g.fight(n.arg('opponent',enemy).arg('casualties','numerous').arg('motion','slow'));
A little more unwieldy, but it separates out the named arguments problem and lets you keep your methods a bit more atomic, and NArgs is probably something you could implement pretty easily just wrapping some methods around one type of Collection (HashTable?) or another that's available in your language.
Add the methods. Overloading methods is generally an antipattern and a refactoring opportunity for someone else.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
I thought about having booleans indicating if they're null, and then checking them inside and reacting accordingly.
Or ... you could just check if they're null.
if(theOtherDudes == null)
...
If there is only one "main method" in your class, then you can implement the optional arguments as getter/setter functions. Example:
public void setOtherDudes(final List<Gundam> theOtherDudes) {} // for input arguments
public List<Person> getCasualities() {} // for output arguments
And then, in your documentation, mention that if the caller has any optional input arguments it has to be passed in before calling fight()
, and the optional output values will be available when fight()
has been called.
This is worthwhile if there are dozens of optional arguments. Otherwise, I suggest overloading the method as the simplest way.
精彩评论