What you describe is known as an anemic domain model. As with many OOP design principles (like Law of Demeter etc.), it's not worth bending over backwards just to satisfy a rule.
Nothing wrong about having bags of values, as long as they don't clutter the entire landscape and don't rely on other objects to do the housekeeping they could be doing for themselves.
It would certainly be a code smell if you had a separate class just for modifying properties of Card
- if it could be reasonably expected to take care of them on its own.
But is it really a job of a Card
to know which Player
it is visible to?
And why implement Card.isVisibleTo(Player p)
, but not Player.isVisibleTo(Card c)
? Or vice versa?
Yes, you can try to come up with some sort of a rule for that as you did - like Player
being more high level than a Card
(?) - but it's not that straightforward to guess and I'll have to look in more than one place to find the method.
Over time it can lead to a rotten design compromise of implementing isVisibleTo
on both Card
and Player
class, which I believe is a no-no. Why so? Because I already imagine the shameful day when player1.isVisibleTo(card1)
will return a different value than card1.isVisibleTo(player1).
I think - it's subjective - this should be made impossible by design.
Mutual visibility of cards and players should better be governed by some sort of a context object - be it Viewport
, Deal
or Game
.
It's not equal to having global functions. After all, there may be many concurrent games. Note that the same card can be used simultaneously on many tables. Shall we create many Card
instances for each ace of spade?
I might still implement isVisibleTo
on Card
, but pass a context object to it and make Card
delegate the query. Program to interface to avoid high coupling.
As for your second example - if the document ID consists only of a BigDecimal
, why create a wrapper class for it at all?
I'd say all you need is a DocumentRepository.getDocument(BigDecimal documentID);
By the way, while absent from Java, there are struct
s in C#.
See
for reference. It's a highly object-oriented language, but noone makes a big deal out of it.
I think the sibling "Strategy Pattern" is slightly closer. Mediator is more about allowing loosely-coupled objects to talk to each other through the Mediator. Strategy is more about choosing the right loosely-coupled object to fulfill the request.
In a different playbook of patterns, there is the "Service Locator" pattern, which also seems to fit.
In practice, you would typically create a service interface, and concrete services adhering to the interface. You could leverage a DI Container as the Service Locator.
Best Answer
One way to make it easier is that all classes implement the same interface so you can treat them indistinctly. Theres a known pattern for this:
The good news is that as this creates a tree. You can then write, in an external managing class, recursive methods to look up items.
For example you could write a method
findAndAdd(String tagToFind, Component componentToAdd);
To the base interfaces documented in the pattern you should add a
tagMatches(String tag)
as well as all common getters and setters so you can, again, treat items indistincltly of their class.I would add
hasChild()
andnextChild()
methods to implement iterations.