Short introduction into Law of Demeter with examples of satisfaction and violation that demonstrates benefits of following the style as well as potential complexities. Work includes references to empirical validation of OO metrics including violations of Law of Demeter
4. Example:Boy dating a girl
class Girl {
private List<GirlRoutine> schedule = new ArrayList<>();
List<GirlRoutine> getSchedule() {
return schedule;
}
}
class GirlRoutine {
private String description;
private Date startTime;
private Date endTime;
boolean happensAt(Date time) {
return time.after(startTime) && time.before(endTime);
}
}
Law of Demeter
5. Example:Boy dating a girl
class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
if (soccerGame.at(time))) return false;
boolean success = true;
for (GirlRoutine routine : girl.getSchedule()) {
if (routine.happensAt(time)) {
success = false;
break; // my heart :(
}
}
return success;
}
}
Law of Demeter
6. class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
if (soccerGame.at(time))) return false;
boolean success = true;
for (GirlRoutine routine : girl.getSchedule()) {
if (routine.happensAt(time)) {
success = false;
break; // my heart :(
}
}
return success;
}
}
Law of Demeter
WHY IS THIS BAD?
7. WHY IS THIS BAD?
class Boy {
...
boolean arrangeDate(Girl girl, Date time) {
...
...
for (GirlRoutine routine : girl.getSchedule()) {
if (routine.happensAt(time)) {
...
...
}
}
...
}
}
Law of Demeter
Why the hell should he look
at her schedule???
8. WHY IS THIS BAD?
class DecisionMaker {
...
boolean decide(Container container, Object param) {
...
...
for (Item item : container.getItems()) {
if (item.checkSomething(param)) {
...
...
}
}
...
}
}
Law of Demeter
10. technically
• Boy will require Girl and GirlRoutine
during compilation
• Changes in GirlRoutine will affect Boy
and Girl
=> Boy, Girl and GirlRoutine are tightly
coupled
(structural coupling)
Law of Demeter
11. Questions
• What if the list of routines is null?
• What if decision is not going to
depend on routines at all?
• What if mother or father would like to
check if their daughter is free?
Law of Demeter
12. Law of Demeter
Better Boy
class Boy {
private SoccerGame soccerGame;
boolean tryArrangeDate(Girl girl, Date time) {
return !soccerGame.at(time) && girl.freeAt(time);
}
}
13. Better Girl
class Girl {
private List<GirlRoutine> schedule = new ArrayList<>();
boolean freeAt(Date time) {
boolean free = true;
for (GirlRoutine routine : schedule) {
if (routine.happensAt(time)) {
free = false;
break;
}
}
return free;
}
}
Law of Demeter
14. benefits
• Better models real-world scenario
• GirlRoutine may change not affecting
Boy in any way
• Implementation of freeAt() method
may now change easily
Law of Demeter
15. law of
Law of Demeter
Ian Holland
1987
•oop design style
•aka principle of least knowledge
•specific case of loose coupling
•Introduced by:
20. class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
if (soccerGame.at(time))) return false;
boolean success = true;
for (GirlRoutine routine : girl.getSchedule()) {
if (routine.happensAt(time)) {
success = false;
break; // my heart :(
}
}
return success;
}
}
Law of Demeter
law of demeter
21. lod-f formally
• O itself
• m's parameters
• Any objects created within m
• O's direct component objects
Law of Demeter
Method m of an object O may only invoke the methods of
the following kinds of objects:
22. Law of Demeter
self: ALLOWED
class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
return freeAt(time) && girl.freeAt(time);
}
boolean freeAt(Date time) {
return !soccerGame.at(time);
}
}
23. Law of Demeter
fields: ALLOWED
class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
return freeAt(time) && girl.freeAt(time);
}
boolean freeAt(Date time) {
return !soccerGame.at(time);
}
}
24. Law of Demeter
parameters: ALLOWED
class Boy {
private SoccerGame soccerGame;
boolean arrangeDate(Girl girl, Date time) {
return freeAt(time) && girl.freeAt(time);
}
boolean freeAt(Date time) {
return !soccerGame.at(time);
}
}
25. class Girl {
boolean freeAt(Date time) {
return new Random().nextBoolean();
}
}
Law of Demeter
new objects: ALLOWED
26. class Girl {
private static final Girl BEST_FRIEND = ...;
boolean freeAt(Date time) {
return BEST_FRIEND.freeAt(time);
}
}
Law of Demeter
global context: ALLOWED
27. class Seller {
void sell(Client client, Product product) {
Wallet wallet = client.getWallet();
if (wallet.getMoney() > product.getPrice()) {
wallet.setMoney(wallet.getMoney() - product.getPrice());
}
else {
throw new NotEnoughMoneyException();
}
}
}
Law of Demeter
LOD: VIOLATION
33. class PageSecurityService {
PageSecurityService(SecurityContext securityContext) { ... }
boolean checkAccess(User user, Page page) {
return !securityContext.getGlobalLock().isEnabled() &&
securityContext.getApplicationContext().
getSecurityDao().userHasPermission(user, page);
}
}
Law of Demeter
page security service
34. @Test
public void user_should_have_access_to_an_open_page() {
User user = new User("John");
Page page = new Page("/john/hangouts");
/* Prepare System Under Test (SUT). */
PageSecurityService sut = ...;
assertThat(sut.checkAccess(user, page), is(true));
}
Law of Demeter
unit test (Shell)
35. ...
GlobalLock globalLock = mock(GlobalLock.class);
when(globalLock.isEnabled()).thenReturn(false);
SecurityDao securityDao = mock(SecurityDao.class);
when(securityDao.userHasPermission(user, page)).thenReturn(true);
ApplicationContext applicationContext = mock(ApplicationContext.class);
when(applicationContext.getSecurityDao()).thenReturn(securityDao);
SecurityContext securityContext = mock(SecurityContext.class);
when(securityContext.getGlobalLock()).thenReturn(globalLock);
when(securityContext.getApplicationContext()).thenReturn(applicationContext);
PageSecurityService sut = new PageSecurityService(securityContext);
...
Law of Demeter
unit test (SUT setup)
37. class PageSecurityService {
private final SecurityDao securityDao;
private final GlobalLock globalLock;
PageSecurityService(SecurityContext securityContext) {
securityDao = securityContext.getAppContext().getSecurityDao();
globalLock = securityContext.getGlobalLock();
}
...
boolean checkNonAuthenticatedAccess(Page page) {
return !globalLock.isEnabled() && page.isPublic();
}
}
Law of Demeter
put it to constructor?
even worse
38. class PageSecurityService {
PageSecurityService(GlobalLock aGlobalLock,
SecurityDao aSecurityDao) {
this.globalLock = aGlobalLock;
this.securityDao = aSecurityDao;
}
boolean hasAccessTo(User user, Page page) {
return !globalLock.isEnabled() &&
securityDao.userHasPermission(user, page);
}
}
Law of Demeter
better service
39. ...
/* Prepare SecurityContext. */
GlobalLock globalLock = mock(GlobalLock.class);
when(globalLock.isEnabled()).thenReturn(false);
SecurityDao securityDao = mock(SecurityDao.class);
when(securityDao.userHasPermission(user, page)).thenReturn(true);
PageSecurityService sut =
new PageSecurityService(globalLock, securityDao);
...
Law of Demeter
unit test (SUT setup)
45. response for class
Study
WHAT?
Number of distinct methods
and constructors invoked by
a class
why bother?
The larger the RFC, the larger
the probability of fault
detection
0%
3%
6%
9%
12%
All New Ext DB UI
Law of Demeter
∆ψ[1]
[1] Basili, Victor; Briand, L.; Melo, W. L. (1996-10). http://www.cs.umd.edu/~basili/publications/journals/J62.pdf
46. class RedirectService {
void service(HttpServletRequest request,
HttpServletResponse response) throws IOException {
if («GET».equals(request.getMethod())) {
String uri = String.format("http://to.com%s?sessionId=%s",
request.getRequestURI(),
request.getSession(true).getId());
response.sendRedirect(uri);
}
}
}
Law of Demeter
weighted methods per class
WMC = 2
violates lod
47. class RedirectService {
void service(HttpServletRequest request,
HttpServletResponse response) throws IOException {
if («GET».equals(request.getMethod())) {
String uri = String.format("http://to.com%s?sessionId=%s",
request.getRequestURI(),
getSessionId(request.getSession()));
response.sendRedirect(uri);
}
}
private String getSessionId(HttpSession session) {
return session.getId();
}
}
Law of Demeter
weighted methods per class
WMC = 3
Satisfies lod
48. weighted methods per class
Study
WHAT?
The sum of the complexities
of all class methods
why bother?
The larger the WMC, the
larger the probability of fault
detection
0%
3%
6%
9%
12%
All New Ext DB UI
Law of Demeter
∆ψ[1]
[1] Basili, Victor; Briand, L.; Melo, W. L. (1996-10). http://www.cs.umd.edu/~basili/publications/journals/J62.pdf
49. law of demeter
Lower RFC
> (is more important than)
Higher WMC
Law of Demeter
0%
3%
6%
9%
12%
All New Ext DB UI
0%
3%
6%
9%
12%
All New Ext DB UI
51. bugs-LOD violations
correlation[1]
Title LOC SVLoD WVLoD
eclipse.jdt.core 0.76 0.67 0.59
eclipse.pde.core 0.64 0.61 0.61
eclipse.jface 0.82 0.73 0.67
eclipse.compare 0.62 0.43 0.42
eclipse.debug.core 0.72 0.7 0.62
Law of Demeter
[1] Yi guo, michael wursch, emanuel giger, harald c. gall, An Empirical Validation of the Benefits of
Adhering to the Law of Demeter (2011). http://www.ccs.neu.edu/home/lieber/LoD/LoD-2011-Zurich.pdf
Study
62. At first sight
the idea of any rules or principles
being superimposed on the creative mind
seems more likely to hinder than to help, but
this is really quite untrue in practice.
Disciplined thinking focuses inspiratioN
rather than blinkers it[1]
Law of Demeter [1] Gordon l. glegg. "the design of design". cambridge university press. 1969