So, for me, this method does one thing. It retrieves (gets) ObjectOutputStream object as its name says
No. You can't retrieve something that doesn't exist. Creating an object which didn't exist before and returning that object changes program state (possibly with other significant side effects). It's not a retrieval. Retrieval should be a safe, low-impact operation.
Creating a new object and handing it back changes program state. At the very least, it is adding a new object to the heap, consuming a bit more memory and using whatever cpu cycles are involved in object creation. That alone can have a significant impact on application performance if this function is called frequently.
And creating a new object may have other side effects:
- Opening a file
- Starting a thread
- Writing to a database
- Opening a network socket
- Resetting the state of other objects
Do you know how many of those an Oos may trigger on creation? If all you want (and all you are given) is a reference to the existing object, none of those need bother you.
My last point there is very important. The code snippet implies that there is only one Oos in this context. So the creation of a new Oos may wipe the state of related objects (caches, counters, stacks) and return them to a virginal condition. Oops.
renaming it to createOrReturnOos would be revealing implementation details to the caller.
If the Oos object were never returned to the caller - if it were a transient, hidden thing - that would be true. But you're returning the created object. Its provenance and history are important.
And as a caller I just want to get ObjectOutputStream and I don't care about how was it retrieved/created.
You should. Knowing which operations change state and which don't is very important.
- Some resources (e.g. databases, clustered data caches) scale by having one node which takes writes while all the others can only be read from. If you don't know which functions change state and which do not, your application will break unexpectedly.
- Some internal resources (e.g. thread pools) can be exhausted by problems with just one function/component. For resilience, it is good practice to segregate these resources into separate pools and assign potentially destructive components to separate pools, so that one component suddenly exhausting a resource doesn't starve the others. But for that to work you have to know which functions consume these resources.
- Security concerns often require you to know which functions change state and which just report it. If you have that knowledge, functions which change state can be restricted so that only privileged processes can use them.
- Graceful degradation (a desirable feature) means that your applications can still do much even if they have lost some of their resources (e.g. the master database, where writes can be performed). A well designed application can still return information even if it can't change it. But designing such code requires you to know which functions change state.
So far, I've only been talking about side effects which you may not have considered and why those are important. But there is another significant consideration.
You're lying to the caller.
The caller should expect ObjectOutputStream to return the existing stream. It may have significant state (a cache, a history of operations, output written to a file) which the caller expects to inherit, even if they don't have direct access. Streams in particular tend to have a lot of state and history. Well, you lied to them. Here is a brand new object without that state. They don't know that. You shouldn't lie like that.
Maybe there should be an existing Oos in the current context. The lack of one may mean something died or a bug elsewhere in the code deleted the old Oos. Now you hid that error. Debugging this is going to be fun.
You're also denying the caller the freedom to create his own control flow. Now, I'm a FP kind of guy and I am suspicious of exceptions, but we're in Java land here. It is a common Java idiom to do things which may or may not break and leave catch blocks to deal with those cases where it does. Sometimes a whole code path lives only in there. You're denying the caller that option. And other, simpler things.
Do not automagically create things which were not there when you were not asked to. If the caller asks for something which isn't there, tell him. Let him decide what to do next.
Stop lying to him.
maybe Bob found a bad example of what he wanted to say
He did not. Clean Code has some of the best writing on software development that I have ever read and there's nothing in the section you quote that I would complain about.