Let's say I have two components talking to each other. The first one calls a "get results" function from the second one. How should I return the results?
For example,
SomeObject getResults(String name);
works fine but I am not sure whether it's the best way to go. This way both components seems coupled to the SomeObject
type and I really want to avoid that. But what would an alternative be?
I could return the results as a List
, but this transfer the responsibility of interpreting the results to the first component.
So, how should I do that? What is the best design approach for this kind of problems?
2 Answers 2
There's no such thing as complete decoupling. To give meaning to the data that is returned from the function, you have to interpret it somehow, and that means having some sort of prior knowledge about the data.
Accordingly, you'll have to weigh the tradeoffs:
Tight coupling using DTO's
- Type Safety
- Ease of Use
- High-performance
- Proliferation of Data Types
Loose coupling using dynamic objects
- Easier to interchange, interoperate and modularize
- Simpler interface contracts
- Lower performance
- Greater complexity
If you wish, you can provide the greatest amount of decoupling by passing everything around encoded as text (i.e. serialized), and decoding it at the receiver end. But this comes at great computational complexity and cost, and is therefore normally utilized only at machine boundaries, where the benefits of a high degree of decoupling outweigh the costs.
Further Reading
Protocol Buffers
The idea of loose coupling is not to remove knowledge about other objects, it is meant to minimize that knowledge (hence loose coupling and high cohesion are often discussed together). Complete decoupling is usually not applied to domain objects (Java classes that hold data, e.g. Student, Car, Job, Result). The main point is to decouple behavior, so that different classes that process a domain object are independent.
An example: for domain object Student
you may be implementing software that checks if student is eligible to graduate. This might involve checking if he has enough credit points and if all bills for credit points have been paid. Although both conditions deal with credit points, business-wise they check very different conditions. Hence you would like to build those checks separately from each other.
The way to reduce dependencies from domain objects is via inheritance. If, for example, you would like to return all Student
s that have not paid their bills, but you are using some generic system that also applies for Professor
s and Contractor
s then you make them implement a same interface, e.g. Student implements Payee
. That way you can always return Payee
instead of specific class.
In your example make SomeObject
implement SomeSpecificInterface
and return an instance of that.
List
to return the results isn't too unwieldy for the client, go with that. It'll be easier to achieve stability that way, as in your code will have fewer reasons to change in the future the fewer user-defined types your code is coupled against.