OOP: How do I deal with objects that have mutual relations?
Let's say there are two classes related to each other via some relations. For example, a Student
maintains a list of the Class
es he takes, and each Class
has a list of Student
s taking it. Then I am afraid of letting the Student
directly being able to modify its set of Class
es, because each modification would have to be followed by a similar modification of a Class
开发者_如何学Python's list of Student
s, and vice versa.
One solution is to have a class whose sole purpose is to keep track of Class
-Student
relations, say Registrar
. But then if some method in Student
requires knowledge of its Class
list, the Student
needs to be passed the Registrar
. This seems bad. It seems Student
shouldn't have access to the Registrar
, where it can also access other Student
s. I can think of a solution, creating a class that acts as a mediator between Student
and Registrar
, showing the Student
only what it needs to know, but this seems possibly like overkill. Another solution is to remove from Student
any method that needs to access its classes and put it instead in Registrar
or some other class that has access to Registrar
.
The reason I'm asking is that I'm working on a chess game in Java. I'm thinking about the Piece-Cell relations and the Piece-Player relations. If in the above example it wasn't OK for a Student
to have access to the Registrar
, is it OK here for a Piece
to have access to the Board
, since a Piece
needs to look around anyway to decide if a move is valid?
What's the standard practice in such cases?
If relations can be changed - classes should be decoupled as much as possible, so along with each class create an interface, do not introduce tied relations between classes.
High level of separation you can achieve using intermediate services/helpers which encapsulates logic of communication between classes, so in this case you should not inject one class to an other even both are abstracted by interfaces, basically Student
does not know anything about Class
, and Class
does not know anything about Student
. I'm not sure whether such complexity is makes sense in your case but anyway you can achieve it.
Here is you may find a useful design pattern Mediator which can encapsulate interaction logic between two decoupled entities, take a look at it.
With the mediator pattern, communication between objects is encapsulated with a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby lowering the coupling.
What I think you have found in your pretty nice example and explanation is that OO does not solve all problems well. As long as the responsibility is well shaped and sharp, everything is fine. And as long each responsibility fits in exactly one bucket (the class), it is pretty easy to design. But here you have a tradeoff:
- If I define for each responsibility a separate class, I will get a bloated design that is pretty difficult to understand (and sometimes to maintain).
- If I include for each separate responsibility at least one interface, I will get more classes and interfaces than I need.
- If I decide that one of the two classes is responsible for the relation as well, this one object has more knowledge than usual about the other.
- And if you introduce in each case a mediator or something similar, your design will be more complex than the problem.
So perhaps you should ask the questions:
- What is the likelihood that the relation between the 2 objects will change?
- What is the likelihood that the relation will exist between more 1 type of objects at each end?
- Is that part of the system a highly visible part, so that a lot of other parts will interface it (and therefore will be dependent on it)?
Take the simplest solution that could possibly work and start with that. As long as the solution is kept simple, it is only your code (you don't design a library for others), there are chances that you can change the design later without hassle.
So in your concrete case,
- the board field should have access to the whole board XOR
- the figure on the field should have the responsibility of moving XOR
- there should be an object type (ChessGame?) that is responsible for the overall knowledge about moving, blocking, attacking ...
I do think that all are valid, and it depends on your special "business case" which one is the most valid.
精彩评论