Law of Demeter & Objective Sense of Style

2,140 views

Published on

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

Published in: Technology, Business
  • rfc = Response for Class, see https://www.safaribooksonline.com/library/view/sonar-code-quality/9781849517867/ch09s04.html
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Law of Demeter & Objective Sense of Style

  1. 1. The Law Of Demeter & objective sense of style
  2. 2. vladimir tsukur partner @ team & tech lead @ Vladimir Tsukur / FOLLOW ME: twitter.com/flushdia or EMAIL TO: flushdia@gmail.com
  3. 3. Example: Boy dating a girl
  4. 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. 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. 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. 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. 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
  9. 9. Privacy!
  10. 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. 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. 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. 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. 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. 15. law of Law of Demeter Ian Holland 1987 •oop design style •aka principle of least knowledge •specific case of loose coupling •Introduced by:
  16. 16. demeter Law of Demeter goddess of the harvest
  17. 17. « grow software in small steps »
  18. 18. only talk to your
  19. 19. 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
  20. 20. 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:
  21. 21. 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); } }
  22. 22. 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); } }
  23. 23. 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); } }
  24. 24. class Girl { boolean freeAt(Date time) { return new Random().nextBoolean(); } } Law of Demeter new objects: ALLOWED
  25. 25. class Girl { private static final Girl BEST_FRIEND = ...; boolean freeAt(Date time) { return BEST_FRIEND.freeAt(time); } } Law of Demeter global context: ALLOWED
  26. 26. 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
  27. 27. getThis().getThat(). getSomethingElse(). doTheWork(); Law of Demeter LOD: VIOLATION
  28. 28. immediate friends only! Law of Demeter BA C
  29. 29. positive implications • Simplifies modifications • Simplifies complexity of programming Law of Demeter
  30. 30. positive implications • Less dependencies, loose coupling => • Better maintainability • Better reuse • Less bugs Law of Demeter
  31. 31. LOD & unit tests
  32. 32. 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
  33. 33. @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)
  34. 34. ... 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)
  35. 35. thanks no!
  36. 36. 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
  37. 37. 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
  38. 38. ... /* 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)
  39. 39. «shy» code is beneficial
  40. 40. Law of Demeter
  41. 41. class PageSecurityService { PageSecurityService(SecurityContext securityContext) { ... } boolean hasAccessTo(User user, Page page) { return !securityContext.getGlobalLock().isEnabled() && securityContext.getApplicationContext(). getSecurityDao().userHasPermission(user, page); } } Law of Demeter response for class RFC = 7 violates lod
  42. 42. class PageSecurityService { PageSecurityService(GlobalLock globalLock, SecurityDao securityDao) { ... } boolean hasAccessTo(User user, Page page) { return !globalLock.isEnabled() && securityDao.userHasPermission(user, page); } } Law of Demeter response for class RFC = 4 Satisfies lod
  43. 43. LAW OF DEMETER => (usually) Lower RFC
  44. 44. 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
  45. 45. 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
  46. 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(), 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
  47. 47. 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
  48. 48. 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
  49. 49. Law of Demeter
  50. 50. 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
  51. 51. person.toString().toUpperCase() Law of Demeter violates lod but looks ok!
  52. 52. List<Number> collection = ...; int value = collection.get(0).intValue(); Law of Demeter violates lod but looks ok!
  53. 53. CustomerDTO customer = ...; customer.getAddressDTO().getCountryDTO(); Law of Demeter violates lod but looks ok!
  54. 54. Column column = builder.createColumn(). withId(«res»). withName(«Resolution»). build(); Law of Demeter violates lod but looks ok!
  55. 55. Law of Demeter use only one dot! object.method() BUT ...
  56. 56. column. setId(«res»). setName(«Resolution»); Law of Demeter looks ok!
  57. 57. follows lod-F but still SO-So class Boy { private SoccerGame soccerGame; boolean arrangeDate(Girl girl, Date time) { return !soccerGame.at(time) && girlIsFree(girl.getSchedule(), time); } private boolean girlIsFree(List<GirlRoutine> schedule, Date time) { for (GirlRoutine routine : schedule) { if (routineAt(routine, time)) return false; } return true; } private boolean routineAt(GirlRoutine routine, Date time) { return routine.at(time); } } Law of Demeter
  58. 58. law of Law of Demeter 1. DRY 2. min method arguments 3. min methods per class + good style =
  59. 59. Law of Demeter tooling 5.0 +
  60. 60. Questions?
  61. 61. 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

×