(I'm going to be writing code examples in C# rather than Java, hopefullyfor brevity and because I'm more familiar with it. Hopefully that's not a problem. The concepts are the same and the translation should be simple.)
Note that this doesn't protect against other methods in the class mutating X and Y. To be more strictly immutable, you could use readonly (final in Java). But either way- whether you make your properties truly immutable or just prevent direct public mutation through setters- it does the trick of removing your public setters. In the vast majority of situations, this works just fine.
public class Piece
{
public Coordinate Position {get; private set;}
}
public class Coordinate
{
public int X {get; private set;}
public int Y {get; private set;}
}
//...InsideAnd then, inside some class
public void DoPiecesCollide(Piece one, Piece two)
{
return one.X == two.X && one.Y == two.Y;
}
ThisSo the above is a good start, but sooner or later you will probably run into a situation where you have behavior which is associated with a class, which in some way depends on the class's state, but which doesn't belong on the class. This sort of behavior is what typically lives in the service layer of your application.
As you can probably tell, _x and _y aren't really encapsulated any more. We could extract them by creating an IPositionTransformer<Tuple<int,int>> which just returns them directly. Depending on taste, you may feel this makes the entire exercise pointless.
However, with public getters, it's actually simplervery easy to do itthings the rightwrong way, just pulling data out directly and using it in violation of Tell, Don't Ask. Whereas using this pattern enforces a certain amount of discipline in thatit's actually simpler to do it the right way: when you want to create behaviour, you'll automatically start by creating a type associated with it. Violations of TDA will be very obviously smelly and probably require working around a simpler, better solution. In practice, these points make it much, much easier to do it the right, OO, way than the "evil" way that getters encourage.
Finally, even if it isn't initially obvious, there may in fact be ways to expose enough of what you need as behavior to avoid needing to expose state, even if this isn't initially obvious. For example, using our previous version of Coordinate whose only public member is Equals() (in practice it would need a full IEquatable implementation), you could write the following class in your presentation layer:
It turns out, perhaps surprisingly, that all the behavior we really needed from a coordinate to achieve our goal was equality checking! Of course, this examplesolution is tailored to this problem, and makes assumptions about acceptable memory usage/performance. It's just an example that fits this particular problem domain, rather than a blueprint for a general solution.
And again, opinions will vary on whether in practice this is needless complexity. In some cases, no such solution like this might exist, or it might be prohibitively weird or complex, in which case you can revert to the above three.