Neither of these options is better than the other, inherently. It is all a relative question depending on the exact domain, etc of the objects at hand. Decreasing coupling can be both a good and a bad thing, or you simply end up trading one kind of coupling for another.
Take the example of the system temperature. Is this coupled to a specific class or instance? No. You might think that the operations available on the temperature are relatively limited, like overclocking or underclocking the processor. But then we get into 3rd party apps that slow down or speed up fans, or just display your temperature on the screen. These would be impossible if the temperature was architected as the SystemMonitor
above. The reality is that the temperature of the system is a global fact. Even the human being operating the machine can observe it. It's not an internal value. Trying to hide it only makes things worse.
Meanwhile, another interesting observation is that in the "not so good", the sound_alarms call does not have to be coupled to the SystemMonitor
. In the "better" case the SystemMonitor itself needs to know how to sound an alarm. We have simply changed one kind of coupling for another.
That being said, there are other examples that behave exactly the opposite. One good example would be that when reading or writing from a file stream, you probably don't want to give the user the underlying OS handle. They might do God knows what with it and break your class. Or this might even expose you to contract/ABI problems. In this case it's quite true to say that the data and the relevant operations belong together.
In example 1 we see a particularly bad example. The author has in both cases coupled the user object to the rendering of a welcome message. It would be better to avoid this coupling entirely by having the caller decide the welcome message and the user only exposes admin
.
Fundamentally, some coupling is unavoidable, but exactly what coupling is problematic and what coupling is no problem depends on the exact circumstances involved. The programmer has to make an informed decision as to whether any specific piece of data or state is internal or public. So far I'm generally finding that objects either have all public data and no operations, or very little to no public data and all operations. Mixing the two seems like it rarely produces a good result.
Also note that Null Object is generally an anti-pattern when applied to data, as data integrity and clarity concerns are much more important than the odd missing null handler. It's much harder to report, migrate, or change the semantics of missing data if you have supplied a default. Often what ends up happening is basically the same as null handling anyway but the default takes the place of null. When it comes to data you should generally be as exact and precise as possible and never throw it away or lose fidelity if it can be avoided.