5

According to the essay "The Object Calisthenics" by Jeff Bay in the book ThoughtWorks Anthology, Use of Getters and Setters should be avoided as they break encapsulation and we should instead ask the object to do something for us instead of digging in to the object and getting its fields.

how about comparison of objects ?

I have to find best object say a Resort for the customer on the basis of a rule that uses few properties of the Resort object.

This rule can change in future. So I find use of comparator interface better as compared to comparable interface. But then I will have to use getters and setters for to access individual properties to compare to objects in a different class.

4 Answers4

15

Books can be wrong or misleading. It is certainly true that having getters and setters for every field provides little more encapsulation than having those fields be public, and it's unfortunate that this kind of design is very widespread.

But it's nonsense to conclude that getters and setters should be avoided altogether. Instead, they should be added deliberately where appropriate, as part of the API design of the class:

  1. If everything a field does can be handled by domain-specific methods, don't have a getter or setter - this is the best case.
  2. If the value of a field is by itself useful and necessary for other classes, add a getter (not a setter)
  3. If the fields represents something like a configuration value, a way for the behaviour of the object to be influenced from outside, add a setter (and a getter iff knowing this configuration could also be useful to other classes).

A Comparator could be seen as an example for case 2, especially if the Comparator is deliberately used instead of having the class implement Comparable. This indicates that the comparison logic is external to the class, and it depends on the field, so the field is useful outside the class.

A compromise might be to have a package private (default visibility) getter and keep the comparator in the same package - or even have it as an inner class that accesses the private field. That would be appropriate if you want the class itself to offer a choice of different sort orders.

If your only concern is that the sort order may change globally in the future, I'd actually stay with Comparable - it's still a change in once place, no need to separate it artificially.

7

Encapsulation is hiding implementation details from outer world, in order to make implementation changes transparent. The very notion of "outer world" assumes existence of some "inner world". But where are the boundaries? They do not always have to be identical to the class boundaries. Often several classes are tightly dependant and can be seen as monolithic programming entity, for example, arcs and nodes in a graph implementation. In such a case, it's ok to use not only getters and setters, but even direct access to fields of siblings classes.

In your case, use Comparator if you like it. To underscore the fact that it belongs to the same programming entity as the class being compared (Resort), you can make it a static class inside the Resort class. Then you'll be able to access private fields of Resort from the comparator.

1

Object calisthenics is not meant to be taken as a series of rules on OO design, rather they are designed to provoke a new thought process. Taking your scenario for example, and working within the boundaries of Object Calisthenics, I might take an approach wildly different to what I normally would have taken..

enum ResortFacility
{
    Pool,
    Sauna,
    Gym,
    Daycare
}

class Resort
{
    private ResortFacility[] facilities;

    boolean HasFacility(ResortFacility facility)
    {
        return facilities.Contains(facility);
    }
}

class CustomerRequirements
{
    private ResortFacility[] requiredFacilities;

    public boolean AreMetBy(Resort resort)
    {
        return requiredFacilities.All(required => resort.HasFacility(required));
    }
}


// possible usage:
var customerRequirements = new CustomerRequirements(ResortFacility.Pool, ResortFacility.Gym);

var resortA = new Resort(ResortFacility.Daycare);
var resortB = new Resort(ResortFacility.Pool, ResortFacility.Sauna);
var resortC = new Resort(ResortFacility.Pool, ResortFacility.Gym);

customerRequirements.AreMetBy(resortA) == false;
customerRequirements.AreMetBy(resortB) == false;
customerRequirements.AreMetBy(resortC) == true;

Again, whether this approach has sufficient value over a much simpler public property comparison is something to consider.

MattDavey
  • 7,176
0

If you create a comparator: Either the code is outside the class, so it can only use properties that at publicly available, so encapsulation is not affected. Or the class has a method to create and return a comparator object, then the comparator can access private parts of an instance, but doesn’t break encapsulation either.

gnasher729
  • 49,096