7

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?

Nick Alexeev
  • 2,484
  • 2
  • 17
  • 24
user2180613
  • 1,752
  • 3
  • 14
  • 17
  • 2
    Those xyz, abc, c, b, a example names doesn't really help understand the code. They don't show useful abstractions. – Tulains Córdova Aug 30 '16 at 14:57
  • `XYZParameterObject` isn't a Parameter Object. This is a fully immutable, truly encapsulated, [POJO](https://softwareengineering.stackexchange.com/a/350164/131624). – candied_orange Oct 03 '19 at 12:21
  • Does this answer your question? [Should we avoid custom objects as parameters?](https://softwareengineering.stackexchange.com/questions/318226/should-we-avoid-custom-objects-as-parameters) – gnat Sep 11 '20 at 07:58

2 Answers2

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.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • 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? – user2180613 Aug 30 '16 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 Aug 30 '16 at 12:54
  • @user2180613: It depends what `A`and `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 Aug 30 '16 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. – user2180613 Aug 30 '16 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 Aug 30 '16 at 13:26
1

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.

Thomas Junk
  • 9,405
  • 2
  • 22
  • 45
  • 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 – Koenigsberg Feb 13 '23 at 08: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. – Thomas Junk Feb 14 '23 at 09:51