Applying Law of Demeter in a way that does not improve design

421 views Asked by At

Suppose, we have the following Boy class that tries to arrange a date with a Girl by analyzing her schedule (example in Java):

public class Boy {

  public boolean tryArrangeDate(Girl girl, Date time) {
    boolean success = true;
    for (GirlRoutine routine : girl.getSchedule()) {
      if (routine.happensAt(time)) {
        success = false;
        break;
      }
    }
    return success;
  }

}

Boy.tryArrangeDate() method clearly violates Law of Demeter because of routine.happensAt() call. One of the ways to resolve this is to move schedule analysis directly to Girl and avoid having a dependency on GirlRoutine. That would probably be one the best decisions in this case. RFC for Boy class will be reduced.

But suppose we choose a different direction for resolving violations of Law of Demeter and change the code in this way:

public class Boy {

  public boolean tryArrangeDate(Girl girl, Date time) {
    return isFree(girl.getSchedule(), time);
  }

  private boolean isFree(List<GirlRoutine> schedule, Date time) {
    boolean free = true;
    for (GirlRoutine routine : schedule) {
      if (happensAt(routine, time)) {
        free = false;
        break;
      }
    }
    return free;
  }

  private boolean happensAt(GirlRoutine routine, Date time) {
    return routine.happensAt(time);
  }

}

There are two private methods added that just delegate calls down to the Girl and her schedule / routines.

Each method taken individually does not seem to violate Law of Demeter (for simplicity reasons let us treat retrieval of item from collection as a primitive no-method-call operation). But overall we have not reduced RFC for this class, did not improve the cohesion, and actually increased WMC. Tell, don't ask principle is not preserved. So, Law of Demeter is satisfied but design is still flaky.

Question: Is it true that (formally) second code snippet does not violate Law of Demeter?

NOTE: Purpose of the question is NOT to find alternative solutions, but to confirm / refute that the solution adheres to the Law of Demeter

1

There are 1 answers

2
Psycho Punch On

Update

Since you're just asking if the second still violates the Law of Demeter, then yes, I'd argue it does. No matter how you refactor tryArrangeDate, it doesn't change the fact that it's calling a method from GirlRoutine. When the Law of Demeter mentions "any method of an object", I take it that it refers to methods accessible to other objects, methods that are exposed to the "outside world". The private methods in your second code snippet are merely helper methods to tryArrangeDate.

Originally

The Girl class is the "expert" when it comes to its own GirlRoutines. As much as possible, you must hide it from anyone else. Remember the principle of tell, don't ask.

Perhaps you can do:

Response response = girl.askOut(boy, date);

Don't read it like the Girl instance is asking out the Boy instance; the rule of thumb I follow (most of the time) is that the object from which you call the method is the direct object of the action that the method represents. In this case, the direct object of askOut is the Girl instance.

The Response class can have information about how it went, or it can be something simple like:

enum Response { ACCEPTED, REJECTED }

This way, a Girl instance has everything it needs to know to accept or reject the Boy instance's request.