0

Say hi to the method:

public void hello(Data data);

This method uses some data and performs… whatever. Now, written like this, the method is very specific – i.e. the argument is very specific. But now, in real life, this Data (for example) may come from e.g. class called DataProvider:

public class DataProvider() {
    public Data getMeData(int from, int to);
}

Now most of the times when we call hello we need to call dataProvider.getMeData(). Since this is repetitive, you might do:

public void hello(int from, int to);

Which is essentially:

public void hello(DataProvider dp);

This looks convenient as, maybe, in future, I will need different data besides the Data class, and no signature will be changed. In other words, method itself will know which data to take.

This second case is an example of a method with more general arguments.

Which one would make more sense:

  • The first one, more strict, more focused.
  • The second one, generic, but less focused.

This is not about passing custom objects. In both cases, custom objects are passed. The difference is that the scope of passing the object is different. The first one contains just raw data. The second one is more general, contains more what that method needs.

4
  • 1
    Possible duplicate of Should we avoid custom objects as parameters?
    – gnat
    Jul 29, 2016 at 9:17
  • Its not that. I dont have an issue with passing custom objects.
    – igor
    Jul 29, 2016 at 9:20
  • 2
    I wouldn't pass a service when you could pass data instead. Jul 29, 2016 at 9:38
  • 2
    Yet you are asking exactly that, whether you're supposed to pass an object or deconstruct it. Passing the Data object is the most feasible solution.
    – Andy
    Jul 29, 2016 at 9:51

5 Answers 5

4

I think the most relevant guideline here is the Single Responsibility Principle. A method that is responsible for processing a Data object should have no business in creating or retrieving it. Instead, it should be passed in as a dependency; just what your first version does.

I disagree that the second version is, by any standard, more generic. It only works for Data objects retrieved from a DataProvider by specifying a pair of ints. The first version will work with any Data, no matter where it came from.

If you find yourself typing

hello(this.dataProvider.getMeData(14, 92));

a lot, there is nothing wrong with adding a private convenience method

private void helloData(final int from, final int to) {
    hello(this.dataProvider.getMeData(from, to));
}

to that class and use it internally as this.helloData(14, 92). Going the other way round – making the function that expectes a DataProvider and a pair of ints to accept a Data object that came from elsewhere – is not nearly as easily possible, however.

1

So you are asking when to introduce an additional layer of abstraction to decouple pieces of code. It's impossible to give a general answer. Most of developers just follow their experience and the Open/Closed-Principle.

… maybe, in future, I will need different data …

The best answer I can give based on the information provided: You Aren't Gonna Need It!

1

The hello method should require exactly the parameters it needs to do whatever it does. If the parameter list is long, then the method may be violating the Single Responsibility Principle.

One major reason for this is testability. If the method takes a DataProvider, then you need to mock up a whole series of nested objects to test the method. If the method takes ints, then you just throw a couple ints at it and you're done.

Once the method is tested, you don't want to change it. If your requirements change, leave the original method alone and overload or override it. This is the Open-Closed Principle.

0

As the other answer already pointed out, you can make a case-by-case decision based on standard principles like OCP or YAGNI.

Next, it generally makes sense to distinguish two different abstraction levels:

  1. On the lower level, the hello method is part of a smaller component and rather far away from service interfaces.

In that case, you typically want your argument as generic as possible for reuse. Though "generic" here refers to the data, not the data-creation or -retrieval.

For example, you don't want the hello signature to require an ArrayList when a more generic List or Collection interface would be sufficient.

  1. On the higher level, the hello method can be considered a sort of facade to a lot of deeper functionality, realized by multiple classes or what-not.

In that case, your functionality changes can quickly require a change in the signature of the data. So in order to not have to change that vital signature all the time, it makes more sense in such a scenario to get more data than you may need at the moment.

In summary, the fun conclusion is that the scope of the method determines the scope of the data it requires. Duh, I know.

0

Question: what, if anything, is the relationship between from and to?

If they can be "most anything", then adding one extra argument for something more general and easier to unit test is a win.

But if there are complex relationships and restrictions, e.g. the variables are actually the page of a book and the word on that page, or the Index of an XML tag and the index of one of its children, then using a more specific object (that you know is valid) may be indicated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.