11

I have a situation where I want to call a function which requires a number of parameters. This function is not called directly, it is called indirectly and the parameters are delegated several times. Some extra parameters may be added in between the different layers, but those aren't included in the example for the sake of simplicity.

In pseudo code, A indirectly calls the functionality that occurs in C.abc.

class A {
    private B b;

    public int abc() {
        return b.abc(value1, value2, value3);
    }
}

class B {
    private C c;

    public int abc(int x, int y, int z) {
        return c.abc(x, y, z);
    }
}

class C {
    private int w = 100;

    public int abc(int x, int y, int z) {
        // Do something with parameters x, y, z, e.g.:
        return w + x + y + z;
    }
}

The number of parameters could be different than three of course: it could be smaller or larger as long as the parameters are going to be used together. The Introduce Parameter Object is sometimes advised in such a situation (here and here).

It would be applied as follows:

class XYZParameterObject {

    private int x, y, z;

    public constructor(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public int abc(int w) {
        return w + x + y + z;
    }
}

class A {
    private B b;

    public int abc() {
        return b.abc(new XYZParameterObject(value1, value2, value3));
    }
}

class B {
    private C c;

    public int abc(XYZParameterObject xyz) {
        return c.abc(xyz);
    }
}

class C {
    private int w = 10;

    public int abc(XYZParameterObject xyz) {
        // Do something with parameters x, y, z, e.g.:
        return xyz.abc(this.w);
    }
}

To me this seems like a class is designed around just one method, while it does not have any behavior. It would actually be the same as passing a closure instead of creating a XYZParameterObject instance. In both situations the x, y, z parameters are reduced to simply one parameter. Both looks like abusing a class just for the sake of not having to pass the same arguments over and over. On the other hand, that would be somewhat convenient of course!

So what is an advisable practice in such a situation?

3
  • 4
    Those xyz, abc, c, b, a example names doesn't really help understand the code. They don't show useful abstractions. Commented Aug 30, 2016 at 14:57
  • XYZParameterObject isn't a Parameter Object. This is a fully immutable, truly encapsulated, POJO. Commented Oct 3, 2019 at 12:21
  • Does this answer your question? Should we avoid custom objects as parameters?
    – gnat
    Commented Sep 11, 2020 at 7:58

4 Answers 4

8

A parameter object is just a way of collecting a set of parameters into a unit. But in your example you are doing something more - your are adding business logic (the addition) into the parameter object itself (which in turn means the C class becomes is superfluous). But then it is not just a parameter object anymore.

A parameter object is not a class built around a single method, it is a data transfer object, which means it does not have methods at all (or at at least only methods for accessing data). But it could be the starting point for a real class with business methods.

Whether a parameter object is a good idea in the first place depends on your code. Your example is too abstract to tell. But if x, y and z say represented coordinates for a point in 3D it would probably be useful to group them in a parameter object, but probably also useful furthermore to add some business logic for calculations on points, e.g scaling, adding etc.

5
  • It is always possible to group parameters in a class, given that one can come up with a name for that class. So if it is correct practice to group them in a ParameterObject or ValueObject (I'm unsure what the best name is), then where is the limit? All I want in A is the result of some calculation and it turns out that it is not very convenient to pass all that calculation's arguments around all the time. So I could create a ParameterObject. But what if B has an additional argument? Does a new ParameterObject emerge? Commented Aug 30, 2016 at 12:40
  • @user2180613: If an additional method has an additional argument, then introducing the PO reduces the number of arguments from 4 to 2 (the parameter object + the additional parameter). And 2 parameters instead of 4 is still an advantage.
    – Doc Brown
    Commented Aug 30, 2016 at 12:54
  • @user2180613: It depends what Aand B is. Whether a pattern is appropriate always depends on a particular situation and what your code is trying to achieve. It like asking if it is appropriate to create an adapter for a class called X or a decorator for class called Y - it is impossible to answer. Try to come up with a realistic example.
    – JacquesB
    Commented Aug 30, 2016 at 12:56
  • @DocBrown I understand the advantage of creating small objects on the fly for some distinct functionality. The question is if this is a conceptually correct, because I see issues when I apply this solution consistently. I could imagine that additional arguments should cause the creation of another ParameterObject. Commented Aug 30, 2016 at 13:06
  • @user2180613: one important point in this answer is IMHO "your example is too abstract too tell". If a class (parameter object or whatever) is conceptually correct depends on if it groups things together which belong together and can be given a descriptive name. If you can group one parameter object together with one additional parameter under a good, descriptive name, then it is probably conceptually correct. If the grouping is very artificial, then it is not.
    – Doc Brown
    Commented Aug 30, 2016 at 13:26
2

Is “Introduce Parameter Object” actually a good pattern?

Yes. In every respect.

It makes code more readable and in consequence more comprehensible: the less information you have to parse, the easier to understand.

From this point of view, it makes sense to refactor a list of parameters to an object. And if you give this object a meaningful name, you win double.

After your edit:

To me this seems like a class is designed around just one method, while it does not have any behavior.

Putting one method into the object doesn't make (in the context you provided) sense.

A call to a.abc() delegates the call to b.abc(), which itself does something with the values contained in your value object. So the place, where the method should belong is from your example C.

The method only makes sense, when a call to it should be reusable - say A needs the result to inform a possible D about it.

So what is an advisable practice in such a situation?

It is okay, to use a value object even for the purpose of simply shortening parameters list. It makes the code more readable.

Add methods only, when there are benefits in more than one place from that.

2
  • One counterpoint to the "Introduce Object Parameter" pattern: What about potentially hiding code smells behind it? Suppose you have a class which takes a bunch of params. You can wrap those params inside a parameter object, but possibly this class holds too much functionality and should be refactored instead. I am wondering about how this pattern adresses such a scenario Commented Feb 13, 2023 at 8:36
  • @Koenigsberg This pattern doesn't hinder you doing not so smart stuff. OTOH: The code smell isn't hidden as such - as it is in the place like before. Taking parameters as a solo indicator for bad code seems a bit overinterpreted. The bad code is inside the function and having one parameter doesn't make code looking good per se. Commented Feb 14, 2023 at 9:51
1

A better question than "is <refactoring> good" is: what does this refactor do for your code?

Refactorings are tools

Refactorings by themselves are neither good nor bad; they are simply a way to reexpress code in a way that behaves identically. After all, many refactoring are paired: extract method and inline method move in opposite directions from each other. Whether they are situationally good or bad depends on what benefits they bring:

  • Do they immediately simplify the code? This is often viewed by newbies (or even experienced engineers, sadly) as the only value of a refactoring.
  • Do they allow further refactoring? Chained refactoring may initially complicate code, but end up producing a more cohesive whole at the end. For instance, if you find a bunch of functions with bad names, lots of inputs and outputs, and chained in sequence, inlining them all may be the first step in extracting out better methods.
  • Do they simplify a code change? Sometimes, something that makes the code more complex, can pay for itself when it simplifies a big change you need to make. Before adding a new type of data a system needs to handle, for instance, you might first move around your existing code to end up with an interface that gives you a clear extension point for the new behaviour.

This last one is a real power tool, and sadly missed by a lot of beginners (and also, oddly, by TDD, which insists on refactoring only after making a change).

Above all, no refactoring is "always good". You should never blindly apply any refactoring, just because it's there. (This is true for any pattern!) This will likely result in obfuscated code where the true intent has been lost.

Should you use this tool

So: does this change make your code simpler? Or make a new piece of code you need to add simpler? Maybe Introduce Parameter Object is a first step in a multi-part refactor, where you end up moving a bunch of your business logic onto this new class, encapsulating it better and making your whole code more readable. Or maybe you need to add more data to the system, and instead of adding more parameters to every method and passing them all through, you first introduce a parameter object, so adding those new data items is trivial.

(Another pro-tip: sometimes a refactoring only becomes obvious when you try to make a change. Don't be afraid to give up your initial attempt, and start clean on a fresh branch with a nice refactor. You can always recover salient bits of your initial attempt from your SCM.)

Code is an art; try things out, and see how they work. Readability is subjective; chat with your teammates (if you are working in a team) to see what they think. Or raise a PR to solicit informed feedback, if that's easier.

Ultimately, only you can answer whether it is good.

0

You need to pass information from caller to callee. The amount of information can usually not be changed. You can divide this information into lots of primitive parameters, or you can combine some or all primitives into parameter objects.

Parameter objects are useful in some situations. One is if the callee calls another function with the exact some primitives, so it can just pass the parameter object. If the number of meaning of the primitives changes as your code is developed, then the parameter object can be changed, while the function declaration and call stay unchanged. Even better if the parameter object is passed straight to another function, then the function in the middle stays unchanged.

If there is a structure to your primitives and you can combine that structure in the parameter object, that makes your code easier to understand. Say you have a customer represented by five primitive objects and an item to buy represented by three primitives. You change it to two parameter objects. In this case they might not be just parameter objects but actual generally useful objects.

It’s usually not useful if there is no relationship between the parameters. So if you have a customer object and a purchased item object, I wouldn’t put both into a parameter object but pass two parameters.

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