3

Assume I have an Application class with about 10 fields. In Some other utility class I have one of the two methods below. Out of these two which is the best to use? Is it better to pass in the whole application object, or pass in only which is needed for the utility method? Please explain why.

public printUsernameAndGroup(String username, String group){
    sout(username);
    sout(group);
}

public printUsernameAndGroup(Application application){
    sout(application.username);
    sout(application.group);
}
krmaj
  • 15
  • 4
DesirePRG
  • 6,122
  • 15
  • 69
  • 114
  • 1
    You are really just overloading a given method. You might actually have need for _both_ of these methods in production. All other factors being equal, passing in a single `Application` object would seem to be neat and tidy, but it would really depend on your use case. – Tim Biegeleisen Nov 15 '16 at 06:19
  • the user of these method needs to extract the username and password before calling the method, in the case of using the first method. Isn't that a hassle to the user? – DesirePRG Nov 15 '16 at 06:21
  • you should pass an interface that can provide me the info name and group.. – ΦXocę 웃 Пepeúpa ツ Nov 15 '16 at 06:21
  • Extracting isn't a hassle; you're passing a reference to an `Application` wrapper object, and then simply accessing two of its fields. – Tim Biegeleisen Nov 15 '16 at 06:22
  • 2
    Have a look at similar question: [link](http://stackoverflow.com/questions/21136227/passing-whole-object-vs-passing-objects-property) – krmaj Nov 15 '16 at 06:25
  • None of those are bad and there is no really best practice (afaik) for this rather then individual opinion. You can use any of the 2 methods. – Enzokie Nov 15 '16 at 06:27

7 Answers7

4

About the performance, two of them are the same. If you implement your function as

public printUsernameAndGroup(String username, String group){
    sout(username);
    sout(group);
}

You will access username and group attributes of your object before calling the function. Also when you implement it as

public printUsernameAndGroup(Application application){
    sout(application.username);
    sout(application.group);
}

You are just passing a copy of the reference of that object, not copying all the 10 fields, and again accessing two attributes of it.

But if you think about the ease of use, the second one will be more user friendly.

Bünyamin Sarıgül
  • 3,031
  • 4
  • 30
  • 55
3

Out of these two which is the best to use? Is it better to pass in the whole application object, or pass in only which is needed for the utility method? Please explain why.?

According to Clean Code (Uncle Bob). It's obvious, a function with two or more arguments is harder to understand than a one argument function. For example, writeField(name) is easier to understand than writeField(output-Stream, name).

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.

Common Monadic Form: There are two very common reasons to pass a single argument into a function. You may be asking a question about that argument, as in boolean fileExists(“MyFile”). Or you may be operating on that argument, transforming it into something else and returning it. For example, InputStream fileOpen(“MyFile”) transforms a file name String into an InputStream return value. These two uses are what readers expect when they see a function.

Dyadic Functions: A function with two arguments is harder to understand than a monadic function. For example, writeField(name) is easier to understand than writeField(output-Stream, name). Though the meaning of both is clear, the first glides past the eye, easily depositing its meaning. The second requires a short pause until we learn to ignore the first parameter. And that, of course, eventually results in problems because we should never ignore any part of code. The parts we ignore are where the bugs will hide.

There are times, of course, where two arguments are appropriate. For example, Point p = new Point(0,0); is perfectly reasonable. Cartesian points naturally take two arguments. Indeed, we’d be very surprised to see new Point(0). However, the two arguments in this case are ordered components of a single value!Whereas output-Stream and name have neither a natural cohesion, nor a natural ordering.

Triads: Functions that take three arguments are significantly harder to understand than dyads. The issues of ordering, pausing, and ignoring are more than doubled. I suggest you think very carefully before creating a triad.

For example, consider the common overload of assertEquals that takes three arguments:assertEquals(message, expected, actual). How many times have you read the message and thought it was the expected? I have stumbled and paused over that particular triad many times. In fact, every time I see it,I do a double-take and then learn to ignore the message.

Arguments Object (you asked for this): When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own. Consider, for example, the difference between the two following declarations:

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

Reducing the number of arguments by creating objects out of them may seem like cheating, but it’s not. When groups of variables are passed together, the way x and y are in the example above, they are likely part of a concept that deserves a name of its own.

Argumen Lists: Sometimes we want to pass a variable number of arguments into a function. Consider, for example, the String.format method:

String.format("%s worked %.2f hours.", name, hours);

If the variable arguments are all treated identically, as they are in the example above, then they are equivalent to a single argument of type List. By that reasoning, String.format is actually dyadic. Indeed, the declaration of String.format as shown below is clearly dyadic.

public String format(String format, Object... args)

So all the same rules apply. Functions that take variable arguments can be monads, dyads, or even triads. But it would be a mistake to give them more arguments than that.

void monad(Integer... args);
void dyad(String name, Integer... args);
void triad(String name, int count, Integer... args);


In short, every system is built from a domain-specific language designed by the programmers to describe that system. Functions are the verbs of that language, and classes are the nouns. If you follow the rules here in, your functions will be good and nicely organized.

But never forget that your real goal is to tell the story of the system, and that the functions you write need to fit cleanly together into a clear and precise language to help you with that telling.


If I have a chance to modify your code, I will modify as below:

//it's already fine:
public printUsernameAndGroup(String username, String group){
    sout(username);
    sout(group);
}

//I changed printUsernameAndGroup to printUserInformation and Application to User for more readable
public printUserInformation(User user){
    sout(user.username);
    sout(user.group);
}

But it's just my suggestions as to your current code. For the whole projects design and methods, it's up to you and I hope you got what you really needed to know in here.

Ye Win
  • 2,020
  • 14
  • 21
2

If you only need two values, pass only two values. But if you need more than 2 fields or may be all 10 fields of Application object, its better to pass object.

One more thing, if you are passing an application object, your function will work only with application object and nothing else. But in first scenario, you can pass any two strings from different objects to make it work. e.g. I assume you are printing values or whatever.

mallaudin
  • 4,744
  • 3
  • 36
  • 68
0

Consider what would be the right abstraction for your function. Does it work on the entire object, or only part of it. Is it natural for it to have access to the entire object? Or should it have access to it's own natural things.

In general, try to keep the number of arguments 'short'. Depending on your coding guidelines, it may be between 3 and 8 arguments.

If you have a method that needs to many arguments, it is an indication it's either doing too much, or it probably needs a separate object to indicate its function, and might be a candidate to refactor to a separate object.

Consider:

rightTurn(int leftwheelPos, int rightweelPos, int newSteeringPos, int speed, int acceleration, int weight, int maximumTurnSize);

vs.

rightTurn(Vehicle v);

The second is probably clearer. However, also consider the following method:

showSpeedOnDashboard(int speed, DashBoard dashboard) {
    dashboard.moveSpeedIndicatorTo(speed/10);
}

vs.

showSpeedOnDashboard(Vehicle v) {
    v.getDashboard().moveSpeedIndiatorTo(v.getSpeed()/10);
}

This seems to be the same... but all of a sudden your showSpeedonDashboard is dependent upon a vehicle. What if that dashboard was generic and was showing the speed of the watercurrent? What if I that dashboard was on my screen, showing the speed of different cars in turn?

So, keep things 'short', don't go overboard passing entire objects when you need almost nothing. If there are too many arguments, consider making a value object which can be initialized (using a builder, probably).

Koos Gadellaa
  • 1,220
  • 7
  • 17
0

Some questions you should notice:

  1. Is a String a Username? No! A Username can not be "" and should not have 2,147,483,647 characters. It should not be a whitespace and not linebreaks.
  2. Is a String a Groupname? No! A Groupname can not be "" and should not have 2,147,483,647 characters. It should not be a whitespace and not linebreaks.

80% of String-methods are not capable for Username nor Groupname.

Make simple classes for them like User and Group.

Now the method printUsernameAndGroup. Its not designed for a broad usage, so you MUST repeat parts of the code if you only like to print the username or the groupname - this fact violates the DRY-Principle.

My solution:

Instead of call printUsernameAndGroup call:
sout(application.user);
sout(application.group);
Grim
  • 1,938
  • 10
  • 56
  • 123
0

My suggestion is do not go and blindly create classes just because you have to pass more than 7 parameters (of course I follow code compete book which states that you should create objects if parameters are > 5-7).

For e.g creating an instance, applying 100 mutators and then passing it as object should not be the solution just because its bad to pass 100 parameters in method. That is also a code smell

Revisit your design and see if anything else can be done. You don't want to end up creating classes/objects every time you pass parameters. A bit of common sense. That being said, if you do have a valid scenario where you have to pass > 5-7 parameters, do pass objects.

Best advice, make use of tools like PMD or Checkstyle. They will help raise a flag when you are not following few standards, these tools are basically called source code analyser

Note: When it comes to constructors do read about constructor chaining as mentioned in this link.

Community
  • 1
  • 1
Kamal Kunjapur
  • 8,547
  • 2
  • 22
  • 32
-1

Neither! :) (My personal point of view, for your specific case)

Best OO practice is to manipulate data items within the owner class, not in some external utility class that can magically see all data items. That's a style that maximally breaks encapsulation. And the owner class should model the real-world (User). Application class can additionally exist, and can reference User. Better:

public class User {
   private String username;
   private String group;
   ...

   public void printUsernameAndGroup(Writer w) {
      w.write(this.username);
      w.write(", ");
      w.write(this.group);
   }
}

public class Application {
    private User user;
    ....

    public void printAppDetails(Writer w) {
        ...
        this.user.printUsernameAndGroup(w);
        ...
}

If user details always contain the same attributes, can use the standard toString method. Even better:

public class User {
   private String username;
   private String group;
   ...

   public String toString(Writer w) {
      StringBuffer sb = new StringBuffer(this.username);
      sb.append(", ");
      sb.append(this.group);
      return sb; // implicitly calls sb.toString() to cast
   }
}

public class Application {
    private User user;
    ....

    public void printAppDetails(Writer w) {
        ...
        w.write(user); // implicitly calls user.toString() to cast
        ...
}
Glen Best
  • 22,769
  • 3
  • 58
  • 74