Named Parameters in Java
java.dzone.com
java.dzone.com
Why? I think this code is better; to demonstrate why, I'll deconstruct the first example -- names.
Names aren't strings. Names are multi-component entities comprised of strings with rather complex internationalization rules regarding composition.
To properly represent a name, you need the set of fields that compose the name, along with a 'full name' (historically in x.500 and LDAP this is called the 'commonName') that is pre-composed according to the users preference.
Some of the individual components you'll need if you wish to compose names:
- Surname [may be more than one]
- Middle name [may be more than one]
- Given name [may be more than one]
- Prefix (title, etc) [may be more than one]
- Suffix (IIrd, etc) [may be more than one]
- Nick name
The rules for composition can be complicated, so you'll want to centralize them somewhere. Also, you may not want to build a super-complex name class now, but later you might need to start sending out e-mails with "Mr. Psuedonym" at the top.
So how do you handle this?
CREATE A CLASS.
Names aren't strings. Most things aren't strings, though they may be represented as strings. Create a class now and make it easy to extend types later.
Treating everything as strings and ints throws away more than just the type system -- it throws away much of the code maintenance value of OO!
That's just names. His example also uses file paths, and file paths are actually quite similar to names; they're composed of multiple components, they have very specific rules regarding composition and normalization, and unlike names, you can easily introduce security issues by incorrectly handling file paths (eg, failure to correctly normalize a path before applying security checks).
This is why we create File classes that handle normalization/composition/decomposition of path names. As a side effect, it also helps constrain the types of your method calls.
Personally, I'm "über-pissed" when I trace through code that uses raw "string & int programming" and does not properly leverage types. It makes for messy code that does not properly take advantage of OO encapsulation and is difficult to read, difficult to type-check, is difficult to maintain, and even more difficult to build on.
object.setName("Alfred E. Neumann") .setLink("http://blog.schauderhaft.de) .setUltimateAnswer(42) .setTempFile("c:\\temp\\x.txt") .setZip(23);
Javascript APIs hack this in by taking Objects as params so that you can do {name='Alfred E. Neuman'...}. Unfortunately, Java's syntax isn't nice for creating Maps either, but maybe you could do something with that.
In the end, this seems like a problem with the language syntax and any fix other than one at the language level is going to be a hack.
It's not addressing named parameters issue, but I find it better like this.
o.doSomething({
name : "Alfred E. Neumann",
link : "http://blog.schauderhaft.de,
ultimateAnswer: 42,
tempFile: "c:\\temp\\x.txt",
zip : 23});This importance of this method is that it allows the you to update the information passed in via the data object (when ID needs change, and they will over the course of a project) without breaking the interface. And that's the point of it all.
I don't know about you, but one of the reasons I like languages such as Python and JavaScript is I don't have to create a wrapper object for every type I'm using. The fact I had to, for instance, create URL and IP Address and such objects in C# drove me crazy.
I wonder how easy it is, though, to modify a namedtuple constructor to do argument validation and to allow for default field values.
namedtuple uses eval(). It really shouldn't, since there are far better ways to do what it does, but it does.
I actually do this in javascript, too. I'll pass in object (it's nice in js because you can just write them in json) and refer to the variable by object property
verifyID({idNum: 1234, surname: 'Himes', firstName: 'Dan'});
{where the signature is verifyID(idObect)
}This has the advantage of not requiring you to remember the order of parameters.
On the gripping hand: click on the method name (in an IDE), and let the IDE tell you what the params are to the method. Why bother with static typing if you aren't going to use the information?
Though I'm guessing that "primitive types" would include algebraic data types, which serve the same purpose here as little objects like IDObject. And since Java doesn't have algebraic data types.... All the more reason to switch to Scala.
Edit: I should have written "built-in types" above, rather than "primitive types".
The Law of Demeter is also sometimes summarized by "One dot: good. Two dots: bad."
Or from Wikipidia:
In particular, an object should avoid invoking methods of a member object returned by another method. For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as "use only one dot". That is, the code a.b.Method() breaks the law where a.Method() does not.
Edit: I should have written "built-in types" above, rather than "primitive types".
As a simple example, when one wants to walk a dog, one would not command the dog's legs to walk directly; instead one commands the dog which then commands its own legs.
i.e. this is violating the law: dog.getLegs().walk()
and should instead be written: dog.walk()
with this implementation:
class Dog {
void walk() { legs.walk(); }
}Though I think that the question of whether algebraic datatypes should be allowed is an interesting one. Also, clearly allowed would be any objects provided by the standard library, since returning those would not couple your code to the code of the API.
As Rich Hickey expresses it, I believe, you should only return from an API data that you could send over the wire without sharing any of the API's code. If you stick to this rule, then, for instance, it is much easier to make your application distributed.
(Though I'm not sure to what degree Hickey and The Law of Demeter would agree on everything. E.g., returning some hairy nested dictionary of dictionaries of dictionaries of strings to represent a book, or what have you. I don't know what Rich Hickey would say about that.)
In other words, with objects X, Y and Z. Assume that X has Y. You want to run method fz on Z. Don't do:
Y.getZ().fz
Do do: Y.runFz() // calls Z.fz
or instantiate a copy of Z in X and simply Z.fz
The data encapsulation principle is also about keeping things loosely coupled. By passing data around as an object, the interested parties need to care far less about the details (that is, their signatures don't change EDIT:TYPO _as_ the code changes).This is a really good thing to do in the "build early and try it" style of building projects as adding data (parameters) to the signature is relatively painless.
val id: Id = db.findUserBySsNumber(ssNumber)
println("id=" + id.toString()) // Violates Demeter!
Instead we should do something like val id: Id = db.findUserBySsNumber(ssNumber)
println("id=" + db.idToString(id)) // Demeter is happy.
Boy, it would be a lot easier if `id` were just a string to begin with!Also, now we're physically dependent on db's data structures (i.e., `Id`). If we were to want to move `db` to be on a server, we would have to share at least some of the server's code. If `id` were just a string, we would be less tightly coupled to `db`.
But that would violate the Law of Demeter, as I understand it: E.g., you shouldn't fetch an Id object from a Db object, and then call a method on the Id object. I.e., you should only talk directly to the Db object.
Not that the Law of Demeter is necessarily the best way to do everything, but it has its merits in terms of decoupling.
I'm not sure where the code snippet that I provided wasn't clear. It looks up someone by Social Security Number in a database and gets back an ID. It thens prints out the ID.
I'm also not clear on just what you are asserting: That my understanding here of the Law of Demeter is incorrect? Or that I shouldn't be following The Law of Demeter here.
If your snippet doesn't return a local id object, then the violating portion should be
idstring = db.findUserBySsNumber(ssNumber).toString();
println("id=" + idstring)
A (perhaps) more clear illustration is when you go the other way with the data. Suppose you want to change the last name of the user. You would want to consider that id.setLastName('Himes')
db.updateUser(id)
may be preferable to db.findUserBySsNumber(ssNumber).updateLastName('Himes')
The first case is more robust (but, of course, not infallibly so) to changes in the api for db when, for example, the api providers decide it's too dangerous to throw SSNs around. It's also more robust against changes in the content of id.These are the kinds of things that OO guidelines can protect against. The tradeoff is added overhead (data objects)-- which means more testing, debugging, etc.
I'm not sure what you mean by a "local object" (unless you are referring to C++'s stack-allocated objects). `Id` is a class defined by the service. The service doesn't know anything about the client code.
But anyway, the guideline takeaway is: work locally. This isn't so important perhaps in the stuff we showed here, but if you had many operations on it, then you risk being fragile to changes in db's api. That's what you are trying to avoid (because they do change, and in ways that can break things).
It's probably so obvious now that you're scratching your head trying to figure out what the hell I'm talking about he couldn't be saying something _that_ stupid!. But back when OO was just going mainstream I would imagine that guidelines like these were useful as all the new programmers likely had the same questions and the guidelines answered them.
That sounds awfull like YAGNI would apply:
This minimizes changes down the road and leads to maintainability.
If you do need to pass in additional information, refactor and change the primitive to an object when you really need to, not because you might need it at some point in the future (and the chances are you won't).
That's a lot harder than doing it right to begin with, and requires significant API-breaking changes across the code base.
Due to the cost of doing so, it's far more likely that the code will be hacked poorly to incorporate the necessary addition, as the substantial changes now required would be much too costly.
YAGNI is misused to justify being lazy. You're always going to need maintenance (except when you have the rare piece of throw-away code), certain approaches are always going to incur high costs to maintenance and future development. YAGNI doesn't apply when past experience provides sufficient evidence that you do need it, and you need it now, when you're writing the code.
I agree.
They are to be taken in the same vein as "prefer implementation to extension (program to interfaces rather than classes)" and so on. Not gospel, but advice from some of those who have been down both roads and are calling back to us. Advice meant to give you pause as you set about building that subclass.
It should be obvious that design strategies that allow change without breaking signatures are good things. Maybe they are not needed or wanted absolutely everywhere, but certainly, if there is any doubt, they should be used.
So for example, in a bank transfer method the named parameter would help distinguish the from-account from the to-account. The accounts are already not simple types, but their usages (roles in the sentence underlying the semantics of the method) are different.
You could create a "transfer" class of course for the pair of accounts, but are we going to do this for each combination of usages of our types. Hopefully not - we'd basically be encoding each combination of parameters that our methods accept into an object - not practical or productive in my opinion.
public void doSomething(Name name, Link link, UltimateAnswer ultimateAnswer, TempFile tempFile, Zip zip) {
String name = name.name;
String link = link.link;
int ultimateAnswer = ultimateAnswer.ultimateAnswer;
String tempFile = tempFile.tempFile;
int zip = zip.zip;
}
If the problem is "I don't like these SAME values being used all over the place" then use DEFAULTS. o.doSomething1(); //defaults to "Alfred","c:\\temp",42,"shadurhaft.de",23
That or pass in an object.
o.doSomething1(mySomethingObject);If the values are genuinely different then the only way to solve this problem is to validate the options (paths should be a valid file location and not a website, websites should be a valid website etc) and then (possibly) pass in an object instead of primitives.
I don't see encapsulation doing anything really, aside from increasing the number of "junk" classed by about a hundred fold.
Why throw standard convention out the window?
Also, I don't think the original problem was the same values being used all over the place. The problem is how to be clear about which parameters you're providing, and accepting static types and giving the type a simple way of constructing it solves that.
int zip = zip.zip;
is just silly, even by Java's standards.> o.doSomething1("Alfred E. Neumann", "http://blog.schauderhaft.de, 42, "c:\\temp\\x.txt", 23);
As other commenters have pointed out, I think the builder pattern is one of the two solutions I would use. The other solution is to simply create an object that represents the values to be passed in to the method. Consider:
User user = new User("Alfred E. Neumann");
user.setLink("http://blog.schauderhaft.de);
user.setUltimateAnswer(42);
user.setTempFile("c:\\temp\\x.txt");
user.setZip(23);
o.doSomething(user);
Even with named parameters, I would say the latter approach is still superior than having a long parameter list.But yes, long parameter lists (whether represented as argument structs or not) generally indicate a problem.
Edit:
* http://openjdk.java.net/jeps/118 proposes run-time storing of parameter names -- not what is desired, but in the same ballpark.
* http://web.archiveorange.com/archive/v/bobySzLnuDWgr47zqwU9 proposes named arguments for making clean code.
The other problem is that the compiler currently discards the parameter names, so older binaries are not going to be compatible.
I like it as well, and have done similar things by creating input/output classes to use as "structs" when the list of parameters gets unweildy or when I want multiple returns.
datatype name = Name of string
and this is just the way to express that in Java.Examples like Guava:
Cache<String, User> userCache = CacheBuilder.newBuilder()
.initialCapacity(7)
.expireAfterWrite(20, TimeUnit.MINUTES)
.build();
Or Hamcrest: assertThat(result, containsString("OK"));
Or Rest-Assured: expect().statusCode(200).when().get("/user/1");For performance, clarity and reusability I would simply create a class that holds the values needed. Add an @Entity tag and now it can be used as a Hibernate persistence class too.