80

I am programming in Java, and I always make converters sort of like this:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

At the new workplace the pattern is:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

For me it is a little bit smelly, as I got used to not to change the incoming parameters. Is this incoming parameter alteration an antipattern or is it OK? Does it have some serious drawbacks?

9
  • 4
    The right answer for this is going to be language-specific, since those where pass-by-value parameters effectively turn them into local variables.
    – Blrfl
    Jun 23, 2014 at 14:36
  • 1
    The second pattern is sometimes used as an efficiency measure to reuse objects. Assuming that oo is an object that is passed to the method, not a pointer that is set to a new object. Is that the case here? If this is Java it probably is, if its C++ it might not be Jun 23, 2014 at 15:24
  • 3
    This looks like premature optimization, aye. I recommend that you use the first form in general, but if you run into a performance bottleneck, you can use the second form with a comment to justify it. A comment would prevent yourself and others from looking at that code and having this same question pop up all over again.
    – Keen
    Jun 23, 2014 at 16:01
  • 2
    The second pattern is useful when the function returns a value as well, for ex, success/fail. But there's nothing really 'wrong' with it. It really depends on where you want to create the object. Jun 23, 2014 at 16:49
  • 12
    I would not name functions implementing these two different patterns the same way.
    – Casey
    Jun 23, 2014 at 17:23

8 Answers 8

92

It's not an antipattern, it's a bad practice.

The difference between an antipattern and a mere bad practice is here: anti-pattern definition.

The new workplace style you show is a bad practice, vestigial or pre-OOP times, according to Uncle Bob's Clean Code.

Arguments are most naturally interpreted as inputs to a function.

Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided. In the days before object oriented programming it was sometimes necessary to have output arguments. However, much of the need for output arguments disappears in OO languages

6
  • 7
    I don't see why the definition you linked to precludes this being considered an anti-pattern. Jun 23, 2014 at 20:23
  • 9
    I suppose it's because that the rationale that "we're saving CPU/mem by not creating new object, instead we should recycle one we have and pass it as an arg" is so obviously wrong for about everybody with serious OOP background that this could not constitute a pattern ever - so there's no way we could consider it an anti-pattern by the definition...
    – user88637
    Jun 23, 2014 at 22:36
  • 3
    What about the case when an input parameter is a large collection of objects that need to be modified? Nov 27, 2017 at 9:46
  • Though I also prefer option 1, what if OtherObject is an interface? Only the caller (hopefully) knows what concrete type it wants.
    – user949300
    Dec 24, 2017 at 15:55
  • 1
    What has OO got to do with not needing output arguments? If we're talking about Java, the fact that it's a garbage-collected language seems to be much more important for avoiding output arguments than the fact that it's object-oriented. Jan 28, 2021 at 16:20
21

Quoting Robert C. Martin's famous book "Clean Code":

Output arguments should be avoided

Functions should have a small numbers of arguments

The second pattern violates both rules, particularly the "output argument" one. In this sense it is worse than the first pattern.

8
  • 1
    I would say it violates the first one, lot of arguments mean messy code, but I think two arguments are totally ok. I think that rule on clean code means if you need 5 or 6 incoming data, you do want to do too much in a single method, so you should refactor to increase the code readability and OOP scope. Jun 23, 2014 at 12:10
  • 2
    anyway I suspect this is not a strict law, but a strong suggestion. I know it won't work with primitive types, if it is a wide-spread pattern in a project a junior would get ideas to pass an int and expect it to be changed like Objects. Jun 23, 2014 at 12:22
  • 36
    Regardless of how correct Clean Code may be I don't think this is a valuable answer without explaining why these are to be avoided. They're not commandments coming down on a stone tablet, understanding is important. This answer could be improved by providing a summary of the reasoning in the book and a chapter reference
    – Daenyth
    Jun 23, 2014 at 14:54
  • 5
    Well, hell, if some guy wrote it in a book it must be good advice for any conceivable situation. No need for thought.
    – Casey
    Jun 23, 2014 at 16:58
  • 3
    @emodendroket But dude, it's the guy! Jun 24, 2014 at 8:35
15

The second idiom can be faster, because caller can reuse one variable over long loop, instead of each iteration creating new instance.

I would not generally use it, but eg. in game programing it has its place. For example look at JavaMonkey's Vector3f lot of operations allows to pass instance that should be modified and returned as result.

7
  • 13
    well, I can agree if this is the bottleneck, but wherever I worked, the bottlenecks are usually algorithms with low efficiency, not cloning or creating objects. I know less of game development. Jun 23, 2014 at 14:28
  • 4
    @CsBalazsHungary I believe the issues when object creation is a concern tend to be related to memory allocations and garbage collection, which would probably be the next level of bottlenecks following algorithmic complexity (especially in a limited-memory environment, e.g. smartphones and the like).
    – JAB
    Jun 23, 2014 at 15:14
  • JMonkey is also where I have seen this a lot since it is often performance critical. I've never heard it called "JavaMonkey" though Jun 23, 2014 at 15:27
  • 3
    @JAB: The JVM's GC is tuned specifically for the case of ephemeral objects. Large quantities of very short lived objects are trivial to collect, and in many cases your entire ephemeral generation could be collected with a single pointer move.
    – Phoshi
    Jun 23, 2014 at 15:51
  • 3
    actually, if one has to create an object, it won't be performance-effective; if a code block has to be heavily optimized for speed, you should use primitives and native arrays instead; because of that, I consider that the statement in this answer doesn't hold.
    – user88637
    Jun 23, 2014 at 22:39
10

I don't think those are two equivalent pieces of code. In first case, you have to create the otherObject. You can modify existing instance in the second. Both have it's uses. Code smell would be preferring one over another.

6
  • I know they are not equivalent. You are right, we are persisting these things, so it would make difference. So you are saying none of them are antipattern, it is just question of the use case? Jun 23, 2014 at 12:01
  • @CsBalazsHungary I would say yes. Judging from the small piece of code you provided.
    – Euphoric
    Jun 23, 2014 at 12:02
  • I am guessing it becomes more serious issue if you have an incoming primitive parameter, which of course won't be updated, so like @claasz wrote: it should be avoided unless it is neccessary. Jun 23, 2014 at 12:20
  • 1
    @CsBalazsHungary In case of primitive argument, the function wouldn't even work. So you have worse problem than changing arguments.
    – Euphoric
    Jun 23, 2014 at 12:22
  • I would say a junior would fall into the trap of trying to make a primitive type an output parameter. So I would say the the first converter should be preferred if possible. Jun 23, 2014 at 12:24
7

It really does depend on the language.

In Java the second form may be an anti-pattern, but some languages treat parameter passing differently. In Ada and VHDL for example, instead of pass by value or by reference, parameters can have modes in, out, or in out.

It is an error to modify an in parameter or read an out parameter, but any changes to an in out parameter are passed back to the caller.

So the two forms in Ada (these are also legal VHDL) are

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

and

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Both have their uses; a procedure can return multiple values in multiple Out parameters while a function can only return a single result. Because the purpose of the second parameter is clearly stated in the procedure declaration there can be no objection to this form. I tend to prefer the function for readability. but there will be cases where the procedure is better, e.g. where the caller has already created the object.

3

The advantage of the second pattern is that it forces the caller to take ownership and responsibility for the created object. There is no question whether or not the method created the object or got it from a reusable pool. The caller knows that they are responsible for the lifetime and disposal of the new object.

The disadvantages of this method are:

  1. Can not be used as a factory method, the caller must know the exact subtype of OtherObject it needs and construct it in advance.
  2. Can not be used with objects that require parameters in their constructor if those parameters come from MyObject. OtherObject must be constructable without knowing about MyObject.

The answer is based on my experience in c#, I hope the logic translates to Java.

3
  • I think with the responsibility you mention, it also creates a "harder to see" lifecycle of objects. In a complex method system I would prefer a direct modification of an object rather than start to look through all the method we are passing. Jun 23, 2014 at 12:47
  • 1
    This was what i was thinking. A primitive kind of Dependency Injection Jun 24, 2014 at 23:36
  • Another advantage is if OtherObject is abstract or an interface. The function has no idea which type to create but the caller might.
    – user949300
    Feb 1, 2016 at 22:48
3

It does indeed seem smelly, and without seeing more context it's impossible to say for sure. There could be two reasons for doing this, although there are alternatives for both.

First, it's a concise way of implementing partial conversion, or leaving the result with default values if conversion fails. That is, you might have this:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Of course, usually this is handled using exceptions. However, even if exceptions need to be avoided for whatever reason, there's a better way to do this - the TryParse pattern being one option.

Another reason is that it could be for purely consistency reasons, for example it's part of a public API where this method is used for all conversion functions for whatever reason (such as other conversion functions having multiple outputs).

Java's not great at dealing with multiple outputs - it can't have output-only parameters like some languages, or have multiple return values like others - but even still, you could use return objects.

The consistency reason is rather lame but sadly it may be the most common.

  • Perhaps the style cops at your workplace (or your codebase) come from a non-Java background and have been reluctant to change.
  • Your code may have been a port from a language where this style is more idiomatic.
  • Your organisation may need to maintain API consistency across different languages, and this was the lowest-common-denominator style (it's daft but it happens even for Google).
  • Or perhaps the style made more sense in the distant past and morphed into its current form (for example, it could have been the TryParse pattern but some well-intentioned predecessor removed the return value after discovering that nobody checked it at all).
2

Given the loose semantics -- myObjectToOtherObject could equally mean that you move some data from the first object to the second or that you convert it entirely anew, the second method seems more adequate.

However, if the method name were Convert (which it should be if we look at the "... do the conversion" part), I'd say the second method wouldn't make any sense. You don't convert a value into another value that already exists, IMO.

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