250 likes | 409 Views
Lecture 13. Law of Demeter. Cohesion. Cohesion : the “glue” that holds a module together. Don’t do things that do not support a common goal As with coupling there are degrees of cohesion Listed from most to least desirable. Taxonomy of Cohesion.
E N D
Lecture 13 Law of Demeter
Cohesion • Cohesion: the “glue” that holds a module together. Don’t do things that do not support a common goal • As with coupling there are degrees of cohesion • Listed from most to least desirable
Taxonomy of Cohesion • Data Cohesion: A module implements a data abstraction • Functional Cohesion: All elements are dedicated to performing a single function • Sequential Cohesion: Calling the elements in a specific order • Communication Cohesion: All elements operate on the same input/output data
Taxonomy of Cohesion (ctd) • Temporal Cohesion: Elements are invoked at or near the same time • Logical Cohesion: If tasks the elements perform are conceptually related • Coincidental Cohesion: If the tasks the elements perform are totally unrelated
Functional Cohesion • Elements perform tasks in a specific domain for a specific purpose • Meyers: “The purpose of an informational strength module is to hide some concept, data structure or resource in a single module” class bakeCake{ void addIngredients(){...} void mixBatter(){...} void bake(){...} }
Sequential Cohesion • The output from one element becomes the input for another • The previous example is sequentially cohesive, because the order and tasks performed are sequentially determined • Bad for reusability and maintainability
Communication Cohesion • Share portions of a common data structure or parameters • Example:Calculate employee statistics and write monthly paycheck • Problems • Wide interface • Bad reusability and maintenenace • Cure: Isolate each sharing member into a separate module
Temporal Cohesion • Tasks are performed at about the same time • E.g. Initialize a program at boot time • Main problem: Multiple functions, elements are not necessarily cohesive class initStuff{ void initMemory(){...} void initDisk(){...} void initPrinter{...} }
Logical Cohesion • Tasks are conceptually related, but no data or control connection • Problems: • Wide Interface • Multiple Functions class areaUtils{ double squareArea(){...} double triangArea(){...} double circleArea(){...} }
Coincidental Cohesion • The worst kind: the “kitchen sink”. No real reason for grouping • Big offender one-class Java • Problems: • Multiple functionality • Result of poor maintenance class myStuff{ void initPrinter(){...} double calculateTaxes(){...} Date getDate(){...}
Also: Procedural Cohesion • Associate elements on the basis of algorithmic or procedural relationships • First do A then B, hence put them together in one method or element class Stack{... public void push(){int addMe=readInt (“Type an int”); elements{++sp]=addMe;} public void pop(){int next=elements[SP--]; println(next);} }
Law of Demeter (LoD) • Good technique for reducing coupling between objects in programming code • Proven “Industrial Strength”. i.e. used in major SW projects to reduce maintenance costs • Let’s look at an example
POST Project • Consider a customer at check-out: public class Customer{ private String firstName; private String lastName; private String myWallet; public String getFirstName(){return firstName;} public String getLastName(){return lastName;} public String getWallet(){ return myWallet:]
POST Project • Now define a Wallet class: public class Wallet{ private float value; public float getTotalMoney(){return value;} public void setTotalMoney(float val){value = val;} public void addMoney(){float deposit){ value += deposit;} public void subtractMoney(float debit){ value -= debit;} }
POST Porject • Now include some code from the Cashier class: payment = 15.95 Wallet theWallet = myCustomer.getWallet(); if (theWallet.getTotalMoney()) >= payment){ theWallet.subtractMoney(payment);} else ...
Why Is This Bad? • Do you just want to give your wallet to the Cashier? The Cashier is exposed to more information than it actually needs • The Cashier knows too much about the wallet and can manipulate it • The classes Customer, Wallet and Cashier are tightly coupled. If you change the Wallet class then a change to the other two is probably necessary.
Try This • What if the wallet was stolen in the store? Then call victim.setWallet(null);? • Our code assumes a wallet, so you will get a runtime exception! If you check for null the code starts becoming complicated • Improvement: Rewrite the Customer class, throw out the getWallet() method, but put in a makePayment()
public class customer{ ... public float makePayment(float amount){ if (myWallet != null){ if (myWallet.getTotalMoney() >= amount){ myWallet.subtractMoney(amount); return amount;} else .. ... } }
Conclusion • Cashier doesn’t even know Customer has a Wallet!! • Now use the Cashier code: payment = 15.95; paidUp = myCustomer.getPayment(payment); if(paidUp == payment) printReceipt(payment); else ...
Why Is This Better? • Fact: Customer has become more complex • But, this is more realistic--Cashier asks Customer for payment; Cashier has no direct access to the customer’s Wallet • The class Wallet can now change and the Cashier detects no difference. • If the Wallet interface changed, then only Customer has to be updated. • Code is easier to maintain • Changes don’t ripple up
The Are Drawbacks • Customer is more complex, i.e. has more methods • But before Cashier needed to know about the Wallet, now not. • Complexity has been reduced to where is belongs • If, e.g. the Wallet had a credit card, then the method getCreditCardNumber(); returns the cc#, not the Wallet
LoD (weak version) • Law of Demeter: A method of an object should only invoke methods from the following kind of objects • itself • its parameters • any objects is creates • its direct component objects • Each object should have only a limited knowledge of other objects
When and How to Apply • Eliminate chained get-Statements: e.g. ccnr = person.getWallet().getccnr(); should be changed ccnr = person.getCCnr(); and let person worry about the ccnr. • Get rid of temps to do the chaining • Never import what you don’t need
LoD (strong version) • In addition to LoD, require • Never access data directly ininheritance--always use accessors (i.e. gets and sets)
LoD and Design Patterns • Most obvious: LoD implies delegation, because the complexity is put where it belongs • Adapter, Proxy and Decorator wrap objects and so force the user to a more suitable interface • Facade hides other classes behind an API