20

In some of my methods, there are Too Many Parameters and very hard to maintain and read source code. And sometimes I am worried about the question "are they passing the appropriate values in the appropriate order?"

I am using Checkstyle as my Eclipse plugin and that gives me warning for more than 7 parameters.

I am not sure it may just be a coding standard and I don't care about it. But when passing many parameters through a view, service or dao, I have noticed that it was very hard to read and hard to modify at later times.

So, I am trying to pass these parameters with...

  1. A number of Objects or Beans. But this give me another problems because my parameters wouldn't get any guarantee (not sure whether they are present or not).

  2. HashMap type parameters. But this may force me to check some validations and try to match keys from method-call sides.

Above two approaches may also lose compile-time errors checking. Are there any suggestions to reduce parameter counts?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Cataclysm
  • 7,592
  • 21
  • 74
  • 123
  • 1
    For the specific case of constructors, see also http://stackoverflow.com/questions/222214/managing-constructors-with-many-parameters-in-java-1-4 – Raedwald Feb 17 '15 at 09:32
  • @Raedwald Thank you for your very useful question(or answers) link. **The Builder Design Pattern** of accepted answer will help my problem. Thanks thanks and thank....... – Cataclysm Feb 17 '15 at 10:15

8 Answers8

18

Passing a HashMap is a common practice in untyped scripting languages but is a bad practice in Java. It defeats the advantages of strong typing which is part of how you gain productivity in Java. To put it a different way, the Java compiler won't be able to help you spot mistakes during development and you'll be more likely to catch them at runtime instead.

If the parameters you're passing are related conceptually, you could as you mention group them into an appropriate object. For example if you're passing parameters such as, say, firstName, lastName, dateOfBirth and so on, you can instead pass a Person object which has those properties. This uses OO to make your design easier to think about and maintain.

If I understand what you mean about this: "But this give me another troubles because my parameters would not get any guarantee (not sure will be contain or not)", you can enforce the guarantee you need when your Person or etc. object is instantiated. One approach would be to use an immutable Person (etc.) object: no setters, instead pass all params via the constructor. Throw IllegalArgumentException if they're not all correct.

Good luck!

Mark Phillips
  • 1,451
  • 13
  • 17
  • `..object: no setters, instead pass all params via the constructor.` I like it in most.Thanks for very useful facts. – Cataclysm Mar 24 '14 at 04:28
  • 2
    Glad my response is useful. Please do accept the answer if it's the one which works best for you. Thx! – Mark Phillips Mar 24 '14 at 04:39
  • 1
    Hi Cataclysm: about the bounty. There really is no detailed, canonical answer to a question like yours. You're asking for design advice, and design is all about trade-offs: what do you gain versus what do you lose. Trying to gain more than you give up, of course. So your choice will depend on what's easiest to read / maintain, what the rest of your program looks like, whether your organization asks you to conform to a particular coding style, and even what coding style you simply prefer. One "canonical" thought, though: don't use HashMap to pass parameters. Good luck! – Mark Phillips Mar 26 '14 at 15:35
  • Hello. Continuing the bounty topic, I personally find the bounty well-justified. Until few years back I also haven't been aware of the optimal solution to this very common problem. Unfortunately the popular sources don't present satisfactory solutions. It shows in the answers presented - each one solving only a part of the problem. I am glad to be able to share my findings in this area (see my answer). Hopefully more developers get familiar with this elegant and maintainable solution. – Przemek Pokrywka Apr 01 '14 at 01:14
  • Hi Przemek: Your solution seems interesting - although posting Scala code to answer a Java question seems inappropriate to me. But, the bounty specifically seeks "a detailed canonical answer", and of course, there's nothing canonical about your proposal. In fact you bend over backward to note how not-canonical it is, because, as you stress in implicit agreement with me, there really is no detailed, canonical answer to a question like this. The question is about design, and as you ably demonstrate, design is about trade-offs, not canon. – Mark Phillips Apr 01 '14 at 02:10
  • Hi Mark. You're correct about canonical answer. I just don't think it's useful to focus on this aspect too much. Questions like this one are best answered by listing all important design alternatives, so you can pick the optimal trade-off. Without the best alternative in the mix the trade-off will always be net-negative. Therefore I think the question is compatible with the spirit of the bounty (while not with the letter as you noticed). – Przemek Pokrywka Apr 01 '14 at 07:38
  • You supported me with very useful comments , my bounty award is for you :) – Cataclysm Apr 02 '14 at 03:03
  • Thank you! I'm glad it's been helpful for you. – Mark Phillips Apr 02 '14 at 16:44
  • I'm curious about the advice to use an immutable person object. Doesn't that defeat the purpose, by forcing a long parameter list in the constructor? – B2K Jun 24 '14 at 14:32
  • @B2K: No, I made up the Person object example simply to explain the idea. The OP doesn't specify what or how many parameters are necessary, just that there are a lot. The point I'm making is to not defeat OO via HashMaps or other practices common in scripting languages. – Mark Phillips Jun 24 '14 at 18:06
13

There are some techniques reduce the # of parameters;

  1. Use minimized methods (break the method into multiple methods, each which require only a subset of the parameters)
  2. Use utility classes (helper classes) to hold group of parameters (typically static member classes)
  3. adapt the Builder pattern from object construction to method invocation.
  4. Try to reduce the data flow between separate packages with better architectural design.

Refer some standard java book;

  • Java: How To Program
  • Head First Java
  • Effective Java

Also try to learn design patterns it'll extremely useful as best coding practices.

  • Head First Design Patterns
Buddhika
  • 419
  • 2
  • 12
  • 3
    Sorry, but I thought I explained what you need. If you are paying attention, 1. Use minimized methods; you can separate the validation from normal classes maintain as a separate. 2. Use utility classes; When the data flow happens more frequently, you can use helper utility class to hold the required data needs and also you can use helper functions in it(parameter conversion, ... (may be the validation) ). And if you tell the exact problem and what level of details you need, we can surely help. – Buddhika Mar 28 '14 at 05:38
  • 2
    I pointed out these books where you can find more details about this, because even after these suggestions you need to know exactly how to implement them and these answers can't provide all the details you need. Ex: Effective Java, Chap 7, Item 40: explains it. – Buddhika Mar 28 '14 at 05:39
  • Thanks , I really appreciate all of your suggestions. – Cataclysm Mar 28 '14 at 06:07
7

Let me start with some caveats regarding the presented proposals before I present mine. I take the liberty of skipping the "smelly" solutions - HashMap and beans, because you can clearly see their disadvantages.

Be careful with

  1. Blindly using helper classes to hold groups of parameters. They can really shine when the group is cohesive (like in Mark Phillips' answer), but they will cause problems otherwise (essentially acting just like a typed HashMap). I doubt that they'd apply to the problem of passing 7 params from view to DAO layer.

  2. Minimized methods are also great, when they make sense (like in List example from Effective Java book). I just rarely see places when they do, so I doubt they'd solve your problem.

  3. The Builder pattern is often very clean, however it only solves the problem of one layer - it doesn't tell you how to pass parameters further down. And when you have a parameter from view, that's needed in DAO, the Builder will just bloat your code and you will need to pass parameters anyway.

And before I finally present the solution, let me challenge a common implicit assumption, that all data needs to be passed via stack in form of method parameters. That's only true if you make your processing objects application- or session-scoped. The constraint goes away when you create all your relevant objects at the start of request processing. Then you can use objects' constructors to pass only necessary information to them.

The optimal way: Use objects of request lifecycle

To some degree this resembles Method Object or Command pattern.

In order to apply this solution, you need to change the entry point to your system - usually that's a method in some view-layer object. Make it responsible for two things:

  • request object graph creation
  • calling the object graph root's execute/run method

The first step is crucial. This is the place, where you construct request-scoped objects from each layer: view, service and DAO. For each of those objects you only pass the required data to their constructors (for example, if parameter "userIP" is only needed in DAO - say for auditing DB access, pass it to DAO request object only). The request objects also need references to their collaborators (like service needing the DAO) - pass those via constructor accordingly.

The second step: when you have your object graph set up, just call the execute/run method on the first of them (usually it's an object from view layer).

/** The example (in Scala) shows how your app's entry point could look like.
 *  The presented method belongs to an app-scoped view-layer object.      
 */
def delete(itemId: Id, userIP: IPAddress) {
  // Note, that only RepositoryHelperReq class is interested in the 
  // "itemId" and "userIP" parameters
  val repoReq = MainRepositoryReq(RepositoryHelperReq(itemId, userIP))
  val serviceReq = MainServiceReq(ServiceHelperReq(repoReq))
  val viewReq = MainViewReq(ViewHelperReq(serviceReq))

  viewReq.execute()
}

Now let me respond to some anticipated criticism of this pattern.

Criticism rebuttal

  1. Some will say, that performance will suffer, because there will be more object on the heap to garbage collect. I would ask those for measurements, because usually it's not object allocation, that costs performance, but object retention (see last presentation of Simon Ritter).

  2. Some will ask about application- or session-scoped data, like datasources or shopping basket object. Those objects still can be used - you just inject those into your request-scoped objects.

  3. Some will criticise the dependency structure, saying that view should depend only on service and not on DAO. That's valid remark, only note, that in classic webapps you still have a central place, that depends on every layer used (place often called "end of the world"). Sometimes it's web.xml, other times it's Spring application context or Guice module. If you care for proper dependencies, I advise you to put all the factory logic in such place, to let it implement some View-layer interface and to be injected to the view. That way your whole dependency structure will stay clean and modular.

  4. Some will say, that popular DI frameworks (mainly Spring) support that pattern very poorly. That's true, you'll need to either use a decent DI library (Guice, Dagger for Java or Macwire if you fancy Scala) or prepare to fight Spring in order to do it right.

The benefits

  1. No long-parameter-list smell
  2. No incohesive "request context" objects (the MagicContainer antipattern)
  3. No "stamp"-coupling - intermediate layers need not to depend on passed parameters, so they can be released independently, are more testable and cleaner
  4. Data is used only where it's needed - easier testing, less mocking
  5. Can be used even where other methods fail, like when you do not have cohesive ParameterObject to extract or when you cannot easily split methods into minimal, orthogonal ones

Credits

The solution to this particular problem was introduced to me by Miško Hevery in his excellent blog post How to do Everything Wrong with Servlets and precisely nailed down in Managing Object Lifetimes (see "More common violation" fragment). I want to thank him for his posts, because it's hard to find precise guidance about this specific problem in other sources.

Przemek Pokrywka
  • 2,219
  • 17
  • 23
4

First approach is the way to go, it is essentially encapsulation (one of the basic principals of OO). As far as "But this give me another troubles because my parameters wouldn't get any guarantee (not sure will be contain or not)." - that is a very common problem and has been addressed using JSR 303. Here is a very basic example of a bean with JSR 303 validation annotations:

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class Book {

   @NotNull 
   private String title;

   @NotNull
   private String author;

   @Min(value=100)
   private int numOfPages;

   @NotNull
   private String isbn;

   ... ... ...
}

Here is how to validate it:

Book book = new Book();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Book>> violations = validator.validate(book);
for (ConstraintViolation<Book> violation : violations) {
   System.out.format("%s: %s%n",violation.getPropertyPath(), violation.getMessage());
}

And here is the output of running validation:

isbn: may not be null
numOfPages: must be greater than or equal to 100
author: may not be null
title: may not be null

Further reading: http://www.jroller.com/eyallupu/entry/jsr_303_beans_validation_using

You don't have to do such manual validation however, for example in Spring MVC you can put @javax.validation.Valid on a bean being passed into a method and it will get validated automatically.

SergeyB
  • 9,478
  • 4
  • 33
  • 47
2

In general, I would say, try to reduce the responsibilities for a method/class to reduce the amount of parameters. But if they are really needed, don´t let a plugin stop you.

Ale Zalazar
  • 1,875
  • 1
  • 11
  • 14
  • I didn't worry about plugin's warning messages , I am trying to describe _it may be coding standard_ but I am not sure. In most case , I think I had used if nessary. So , I assume we add parameters these are actually needed. – Cataclysm Mar 24 '14 at 04:04
  • The plugin is probably right in the scenario Cataclysm described. When you pass data from view to DAO it's unlikely that all parameters are used in all the methods along the call chain. Most often people pass the parameters along because they don't realize the alternative (which is a pity). – Przemek Pokrywka Apr 01 '14 at 01:22
2

You must be able to come out of this problem with below options.
1. Builder pattern
2. Pseudo named parameter approach will be a good way of doing it.(refer to http://java.dzone.com/articles/named-parameters-java) (Works best for immutable objects)
3. Command Pattern.

The above options are valid only if you are consuming all the 7 parameters in your method. if not there is a flaw in the way this api has been designed.

i found the below link helpful while thinking about designing methods and api's,
http://www.infoq.com/presentations/effective-api-design

shreyas K N
  • 155
  • 1
  • 3
  • 12
2

It is quite typical to see constructors with long lists of parameters, particularly for DAO. Very often, I find that adhering to similar naming conventions for properties can be a big help to preventing that.

It's been quite a while since I've written any java code, so I'll need to pass on a java specific example. Basically, if you have properties with identical names and assignable types, you can implement a cloning function that accepts any object and maps it to another.

In C#, I discussed this here: Best way to clone properties of disparate objects

As far as type safety is concerned, you are in control of your naming conventions. If a personId always means the same thing, and always has the same type, you'll avoid some common gotchas which can cause problems down the road.

I would be remiss if I didn't mention that using reflection is not always a good approach. You'll need to consider the performance implications. Another consideration would be that you validate the resulting object before persisting it. And lastly, beware shallow copies.

Community
  • 1
  • 1
B2K
  • 2,541
  • 1
  • 22
  • 34
0

I don't know how your packages separated,while when the transport data contains too many parameters,My advice is to create a new Object that calls DTO(data transport object) in javaee.As you said,it's not easy to guarantee. Otherwise,to use CALLBACK is a better way that could be easily guaranteed and extended.Nothing is perfect,you'd choose which aspect is your attention.
callback seen as

void function(Callback callback){
    callback.do();
}

This is my advice but not a exactly correct way.Maybe it's helpful for you.

hirra
  • 867
  • 7
  • 14
  • DTO is a new layer that between Controller(Web) or Service. – hirra Mar 24 '14 at 04:18
  • can you support me with some links ? Please . And I would request to you describe with some example for you described **DTO** and **Callback**. :) – Cataclysm Mar 24 '14 at 04:21
  • I don't think the concept of callbacks addresses the OP's question. The issue in the OP is about API clutter and the maintenance / readability issues that result. Callbacks are more about structuring logical flow of control under particular circumstances. – Mark Phillips Mar 24 '14 at 04:32