law of demeter & objective sense of style

62
The Law Of Demeter & objective sense of style

Upload: vladimir-tsukur

Post on 15-Jan-2015

929 views

Category:

Technology


0 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Law of Demeter & Objective Sense of Style

The Law Of Demeter& objective sense of style

Page 2: Law of Demeter & Objective Sense of Style

vladimir tsukur partner @

team &tech lead @

Vladimir Tsukur / FOLLOW ME: twitter.com/flushdia or EMAIL TO: [email protected]

Page 3: Law of Demeter & Objective Sense of Style

Example:B!" #$%&'( $ (&r)

Page 4: Law of Demeter & Objective Sense of Style

Example: B!" #$%&'( $ (&r)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

Page 5: Law of Demeter & Objective Sense of Style

Example: B!" #$%&'( $ (&r)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

Page 6: Law of Demeter & Objective Sense of Style

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?

Page 7: Law of Demeter & Objective Sense of Style

WHY IS THIS BAD?class Boy {

...

boolean arrangeDate(Girl girl, Date time) {

...

...

for (GirlRoutine routine : girl.getSchedule()) {

if (routine.happensAt(time)) {

...

...

}

}

...

}

}

Law of Demeter

W*" %*+ *+)) ,*!-)# *+ )!!. $% *+r ,/*+#-)+???

Page 8: Law of Demeter & Objective Sense of Style

WHY IS THIS BAD?class DecisionMaker {

...

boolean decide(Container container, Object param) {

...

...

for (Item item : container.getItems()) {

if (item.checkSomething(param)) {

...

...

}

}

...

}

}

Law of Demeter

Page 9: Law of Demeter & Objective Sense of Style

Privacy!

Page 10: Law of Demeter & Objective Sense of Style

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

Page 11: Law of Demeter & Objective Sense of Style

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

Page 12: Law of Demeter & Objective Sense of Style

Law of Demeter

Better Boy

class Boy {

private SoccerGame soccerGame;

boolean tryArrangeDate(Girl girl, Date time) {

return !soccerGame.at(time) && girl.freeAt(time);

}

}

Page 13: Law of Demeter & Objective Sense of Style

Better Girlclass 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

Page 14: Law of Demeter & Objective Sense of Style

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

Page 15: Law of Demeter & Objective Sense of Style

law of

Law of Demeter

Ian Holland1987

• oop design style• aka principle of least knowledge• specific case of loose coupling• Introduced by:

Page 16: Law of Demeter & Objective Sense of Style

demeter

Law of Demeter

goddess of the harvest

Page 17: Law of Demeter & Objective Sense of Style

« grow softwarein small steps »

Page 18: Law of Demeter & Objective Sense of Style
Page 19: Law of Demeter & Objective Sense of Style

only talk to your

Page 20: Law of Demeter & Objective Sense of Style

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

Page 21: Law of Demeter & Objective Sense of Style

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:

Page 22: Law of Demeter & Objective Sense of Style

Law of Demeter

self: ALLOWEDclass Boy {

private SoccerGame soccerGame;

boolean arrangeDate(Girl girl, Date time) {

return freeAt(time) && girl.freeAt(time);

}

boolean freeAt(Date time) {

return !soccerGame.at(time);

}

}

Page 23: Law of Demeter & Objective Sense of Style

Law of Demeter

fields: ALLOWEDclass Boy {

private SoccerGame soccerGame;

boolean arrangeDate(Girl girl, Date time) {

return freeAt(time) && girl.freeAt(time);

}

boolean freeAt(Date time) {

return !soccerGame.at(time);

}

}

Page 24: Law of Demeter & Objective Sense of Style

Law of Demeter

parameters: ALLOWEDclass Boy {

private SoccerGame soccerGame;

boolean arrangeDate(Girl girl, Date time) {

return freeAt(time) && girl.freeAt(time);

}

boolean freeAt(Date time) {

return !soccerGame.at(time);

}

}

Page 25: Law of Demeter & Objective Sense of Style

class Girl {

boolean freeAt(Date time) {

return new Random().nextBoolean();

}

}

Law of Demeter

new objects: ALLOWED

Page 26: Law of Demeter & Objective Sense of Style

class Girl {

private static final Girl BEST_FRIEND = ...;

boolean freeAt(Date time) {

return BEST_FRIEND.freeAt(time);

}

}

Law of Demeter

global context: ALLOWED

Page 27: Law of Demeter & Objective Sense of Style

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

Page 28: Law of Demeter & Objective Sense of Style

getThis().getThat(). getSomethingElse(). doTheWork();

Law of Demeter

LOD: VIOLATION

Page 29: Law of Demeter & Objective Sense of Style

immediate friends only!

Law of Demeter

B A C

Page 30: Law of Demeter & Objective Sense of Style

positive implications

• Simplifies modifications• Simplifies complexity of programming

Law of Demeter

Page 31: Law of Demeter & Objective Sense of Style

positive implications• Less dependencies, loose coupling =>• Better maintainability• Better reuse• Less bugs

Law of Demeter

Page 32: Law of Demeter & Objective Sense of Style

LOD &unit tests

Page 33: Law of Demeter & Objective Sense of Style

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

Page 34: Law of Demeter & Objective Sense of Style

@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)

Page 35: Law of Demeter & Objective Sense of Style

...

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)

Page 36: Law of Demeter & Objective Sense of Style

thanks no!

Page 37: Law of Demeter & Objective Sense of Style

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

Page 38: Law of Demeter & Objective Sense of Style

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

Page 39: Law of Demeter & Objective Sense of Style

...

/* 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)

Page 40: Law of Demeter & Objective Sense of Style

«shy» codeis beneficial

Page 41: Law of Demeter & Objective Sense of Style

Law of Demeter

Page 42: Law of Demeter & Objective Sense of Style

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

Page 43: Law of Demeter & Objective Sense of Style

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

Page 44: Law of Demeter & Objective Sense of Style

LAW OF DEMETER=> (usually) Lower RFC

Page 45: Law of Demeter & Objective Sense of Style

response for classS!"#$

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

Page 46: Law of Demeter & Objective Sense of Style

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

Page 47: Law of Demeter & Objective Sense of Style

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

Page 48: Law of Demeter & Objective Sense of Style

weighted methods per classS!"#$

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

Page 49: Law of Demeter & Objective Sense of Style

law of demeter

Lower RFC> (is more important than)

Higher WMCLaw of Demeter

0%

3%

6%

9%

12%

All New Ext DB UI

0%

3%

6%

9%

12%

All New Ext DB UI

Page 50: Law of Demeter & Objective Sense of Style

Law of Demeter

Page 51: Law of Demeter & Objective Sense of Style

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

S!"#$

Page 52: Law of Demeter & Objective Sense of Style

person.toString().toUpperCase()

Law of Demeter

violates lod but looks ok!

Page 53: Law of Demeter & Objective Sense of Style

List<Number> collection = ...;

int value = collection.get(0).intValue();

Law of Demeter

violates lod but looks ok!

Page 54: Law of Demeter & Objective Sense of Style

CustomerDTO customer = ...;

customer.getAddressDTO().getCountryDTO();

Law of Demeter

violates lod but looks ok!

Page 55: Law of Demeter & Objective Sense of Style

Column column = builder.createColumn().

withId(«res»).

withName(«Resolution»).

build();

Law of Demeter

violates lod but looks ok!

Page 56: Law of Demeter & Objective Sense of Style

Law of Demeter

use only one dot!

object.method()

BUT ...

Page 57: Law of Demeter & Objective Sense of Style

column. setId(«res»). setName(«Resolution»);

Law of Demeter

looks ok!

Page 58: Law of Demeter & Objective Sense of Style

follows lod-F but still SO-Soclass 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

Page 59: Law of Demeter & Objective Sense of Style

law of

Law of Demeter

1. DRY2. min method arguments 3. min methods per class

+good style =

Page 60: Law of Demeter & Objective Sense of Style

Law of Demeter

tooling

5.0 +

Page 61: Law of Demeter & Objective Sense of Style

Questions?

Page 62: Law of Demeter & Objective Sense of Style

At first sightthe idea of any rules or principles

being superimposed on the creative mindseems more likely to hinder than to help, but

this is really quite untrue in practice.

Disciplined thinking focuses inspiratioNrather than blinkers it[1]

Law of Demeter [1] Gordon l. glegg. "the design of design". cambridge university press. 1969