I think it would really shine with IDE support. It probably wouldn't even be that hard to do (famous last words). I think I'll take a stab at it.
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.
When "the list of parameters gets unweildy" and "I want multiple returns" are to major indicators that you got some anti-patterns going on. I think the refactoring that you need might a new class with a single responsibility.
Or they are indicators that you have experience in languages that allow such designs, and would like java to introduce them. My biggest problem with java is that it tried to be a single paradigm langues: object oriented. While it was doing that, people worked on developing other methods. Concepts from these other systems are beggining to creep into java. For example, generics are a functional concept, as are lambdas which will be introduced in the next version.
I've seen a pretty big move in popular libraries to fluent interfaces. It does a pretty good job of getting over the verbosity in Java (at least where the verbosity hurts readability).
Objective-C doesn't have named parameters (that is, Lisp-like "keyword" parameters). It has a hack which allows a method to be so named that it has to be used in a named way. Named parameters can be in any order and may or may not exist in the function call. Obj-C can't do this because its "names" are actually just part of the method signature. It's fake.
It may be "fake", but it still has the property that this article was looking for, in that it is obvious to the reader what each of the arguments being passed to the method are for.
Whoever wrote the original code is missing a major point in OOP: you should (almost) never be passing in simple types. Create an object that holds the data (class IDObject, for instance), and pass an instance of this class in for the parameter. Then, the parameters are de facto named in the data object.
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.
When I program in Python I would do it the same way, create a class that represents the types. The class can be made into a persistence class easily, this approach is clear, readable and maintainable. Oh and I try to avoid JavaScript, too much clever coding makes it a hard language to maintain.
The idea is to write a wrapper that encapsulates all of the (related) data. This is useful in type-checked languages where changing the signature can have repercussions across multiple classes.
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
OTOH, I kind of like languages that support map literals and/or tuples, rather than making me define a new class for use in a single place as a parameter object or return value.
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?
On the other hand, the Law of Demeter says that you should almost always be passing in and fetching primitive types. At least in an API. Does it not?
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".
I've heard the Law of Demeter at times stated explicitly as that you should usually return only primitive data types from an API. Rich Hickey certainly claims this too, although he doesn't refer to it as the Law of Demeter.
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".
That has nothing to do with primitive types. Otherwise in Java it would mean you shouldn't return a String ever because it's not primitive! It has nothing to do with primitive types whatsoever. It's about encapsulation, i.e. you should not depend on the internal structure of an object.
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()
By "primitive data types" I didn't mean to exclude built-in collections of primitive data types. Point taken, though: I probably should have said "built-in data types", or some such. I didn't even mean to exclude algebraic data types, as I explicitly mentioned.
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.)
The Law of Demeter isn't about data encapsulation. It's trying to solve the problem of tightly-coupled code methods. It is a close cousin of "Don't get data from objects and operate on them- ask the contain object to do the operation for you." The Law of Demeter says, "Don't call a method on an object that was returned by a method of a different object." When you do this you are creating a tightly-woven daisy-chain which will lead to inter-dependency hell should something change in the future. Instead, you should either (1) ask the object you are calling to call the other object for you, or (2) call the third object method directly, without relying on the intermediary to get it.
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.
If I understand you correctly about the Law of Demeter, and we were to use an `Id` object to represent an ID, then the following would be verboten:
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`.
If I knew what your code was trying to do I might be able to write to it directly. But these guidelines would suggest that if you are going to call methods on the id object, it's best, if possible, to put the methods in the id object itself.
> But these guidelines would suggest that if you are going to call methods on the id object, it's best, if possible, to put the methods in the id object itself.
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 the code snippet returns a local ID object, then calling a method on the ID object doesn't violate the law of Demeter. It can be, in fact, the preferred method in the case that you don't have control over the db object.
If your snippet doesn't return a local id object, then the violating portion should be
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
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 sorry for not being more clear. `db` is a service and the code snippet is client code.
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.
By local object, I mean an object defined in whatever object the above code is written.
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.
There is a small but meaningful distinction: Encapsulating the data in OOP fashion allows you to respond when the need arises. You don't build it until you need it, but when/if you do ever need it you won't have to refactor the interface (and the interfaces of all the classes that use these data).
This minimizes changes down the road and leads to maintainability.
But it also means that you are introducing a lot of extra classes that do almost nothing but wrap primitives and provide little or no additional behaviour.
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).
> 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.
And you are correct that there are cases where this is true - but the best approach is to use an element of judgement and make a call when this is likely to pay off in the long term. Always using primitives for everything or never using primitives because you might need to augment them structurally at some point are both silly extremes that are equally bad.
> Always using primitives for everything or never using primitives because you might need to augment them structurally at some point are both silly extremes that are equally bad.
True, they are silly extremes. But as guidelines they are not equal. One may lead to a few extra "waste" classes, but the other can fook things up.
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.
Well, my experience redesigning large-scale enterprise applications built to be "generic" is that almost nobody gets the balance right but by far the worst applications to work with are the over-architected over-generic solutions.
I think types are orthogonal to why named parameters are helpful, because parameters don't just indicate a type but a particular usage of a type.
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.
Wow this would make for some crazy code to try to read thru. I would be über-pissed to trace thru code like this only to find all of these wrapper classes. This is what Javadocs are for, it's much easier and cleaner just to document your code as you write it.
> I would be über-pissed to trace thru code like this only to find all of these wrapper classes.
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.
Interesting. But it looks like it is solving a problem with another problem. The receiving class would then look like this:
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.
I'm not sure I see the "another problem" you're referring to. Can you describe the problem with the new implementation of doSomething()?
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.
That would definitely be a nicer way to do it. Although, using the builder pattern to create parameters for every single method in your API would be a gigantic pain.
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.
This is a good observation, people who seem to like named parameters strike me as people who might code in plain text editors a lot. Nothing wrong with that but an IDE makes things so much easier and faster.
I think there's a kernel of truth to that but it's not entirely right. I use Pycharm for my python coding and still like named parameters because it makes clear at a glance what each parameter is in the function call. Otherwise, I'd need to click through to each function definition or use the "show doc" command to figure it out. It's much better to be able to scan through the code and understand it without intervention from IDE features.
I guess I could see that but it might take me a few thousand lines for it to sink in. Makes sense stated the way you did. If you have to add a parameter though then you have to locate all of the locations and update them. With an object you just add the new field and its accessors if you want and then only the new section of code interested in that new field would be changed; but it's new so.
I would not use a separate class per parameter though that is a clever solution. It might hurt performance and it just feels a bit heavy.
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.
Although the solution produces too many problems, I agree that named parameters are highly desirable for readability and the prevention of bugs. Have there been any proposals for Java 8 to included named parameters?
Edit:
* http://openjdk.java.net/jeps/118 proposes run-time storing of parameter names -- not what is desired, but in the same ballpark.
Since long parameter list is considered a code smell, people should be discouraged from writing this kind of code anyways. Namely, this example from the article is in fact a bad practice:
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.
This, like many of the proposed solutions here exposes the issue that putting the (not particularly) long list of parameters into the ctor, that of not every being able to call doSomething without all the required values. What is the caller of your builder doesn't remember to set the zip and the doSomething method then uses it? You've only really added syntactic sugar and sacrificed functionality.
61 comments
[ 3.2 ms ] story [ 97.4 ms ] threadI 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.
Examples like Guava:
Or Hamcrest: Or Rest-Assured: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
{where the signature is }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:
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:
Do do: or instantiate a copy of Z in X and simply 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.
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
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 may be preferable to 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:
http://en.wikipedia.org/wiki/You_ain%27t_gonna_need_it
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.
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.
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.
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.
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.
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.
> 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:
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.