CSC/ECE 517 Fall 2012/ch2a 2w25 nr: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
Line 27: Line 27:
<br> Less risk of breaking functionality
<br> Less risk of breaking functionality
<br> Modular code
<br> Modular code
=='''Example'''==
Law of Demeter is a technique for reducing the 'coupling' between objects in the code. Here is an example based on Java that illustrates the Law of Demeter.
<br><br>
A ‘Paperboy’ has to get a payment from a ‘Customer’. There has to be a mechanism used by the paperboy for receiving payments from that customer. Lets assume that the paperboy will run a snippet in order to achieve the above. The definition of each Java class for this example is as follows.
===Customer===
The Customer class can have a first name, a last name, an account number, a shipping address, some method of payment, etc. In this example lets define the customer with just enough functionality to make our illustration of Law of Demeter to work.
<pre>
public class Customer {
    private String firstName;
    private String lastName;
    private Wallet myWallet;
    public String getFirstName(){
        return firstName;
    }
    public String getLastName(){
        return lastName;
    }
    public Wallet getWallet(){
        return myWallet;
    }
}
</pre>
===Wallet===
Each customer is associated with a wallet by which the payment is made. A wallet can have total money, credits cards, drivers license, different types of currency, etc. For this example we design a pretty simplistic wallet.
<pre>
public class Wallet {
    private float value;
    public float getTotalMoney() {
        return value;
    }
    public void setTotalMoney(float newValue) {
        value = newValue;
    }
    public void addMoney(float deposit) {
        value += deposit;
    }
    public void subtractMoney(float debit) {
        value -= debit;
    }
}
</pre>
Now that we have all required information about the customer and his wallet. Lets turn our focus on to the paperboy.
===Paperboy===
In this paperboy class we focus more on the method by which payment is made. In order to get the payment from the customer the paperboy executes a code snippet. The code snippet is some method in the Paperboy class (eg : getPayment() ) which achieves this objective. The typical getPayment method for this illustration is as follows
Paying the Paperboy - getPayment()
<pre>
void getPayment(Customer myCustomer) {
  payment = 2.00;  // Payment amount
    Wallet theWallet = myCustomer.getWallet(); // Wallet of the corresponding customer who has to pay
    if (theWallet.getTotalMoney() > payment) {
        theWallet.subtractMoney(payment);
    } else {
        // Other code
    }
}
</pre>
This code snippet gets the wallet from the customer, checks to make sure if the customer has the money and transfers it from the customers wallet into the paperboy's wallet.
Lets analyse what is bad about this code. We can actually translate what the code is actually doing into real-language.  Apparently, when the paperboy stops by and demands payment, the customer is just going to turn around, let the paperboy take the wallet out of his back pocket, and take out two bucks. This is not the typical how some will handle their wallet.
There are a number of 'real-world' problems with this, not to mention we are trusting the paperboy to be honest and just take out what he's owed.  If our future Wallet object holds credit cards, the paperboy has access to those too. The basic problem is that “the paperboy is being exposed to more information than he needs to be”.
The most important concept is the 'Paperboy' class now 'knows' that the customer has a wallet and can manipulate it.  When we compile the Paperboy class, it will need  the Customer class and the Wallet class.  These three classes (Customer, Wallet and Paperboy) are now 'tightly coupled'.  If we change the Wallet class, we may have to make changes to both of the other classes.
There is another classic problem that this can create. The customer wallet can be stolen or lost. In this case we can set the Wallet to null like
<pre>
victim.setWallet(null);
</pre>
This seems a reasonable assumption. This code enforces any mandatory value for wallet and there is a certain 'elegance' about using null for this condition but  this never handles the paperboy. The code assumes there will be a wallet so the paperboy will get a runtime exception for calling a method on a null pointer. We could fix this by checking for 'null' on the wallet before we call any methods on it, but this starts to clutter the paperboy's class.  The real-language description is becoming even worse as more and more complex arises. This makes the design and code more convoluted.
===Code improvement , better design using Law of Demeter===
The example illustrated can be made much better by incorporating proper design and improving the code structures.  The proper way to fix the issue to move along the  'real world' scenario.
When the paperboy needs payment, he would approach the customer but he is not going to handle the customer wallet. In fact he won't even know whether the customer has a wallet or not even if he does, he can’t be sure that the customer is willing to pay him.
===The New Customer===
The New customer has design changes such that he no longer has a 'getWallet()' method but it does have a getPayment method. The new customer class is as shown below
<pre>
public class Customer {
    private String firstName;
    private String lastName;
    private Wallet myWallet;
    public String getFirstName(){
        return firstName;
  }
    public String getLastName(){
        return lastName;
    }
    public float getPayment(float bill) {
        if (myWallet != null) {
            if (myWallet.getTotalMoney() > bill) {
                theWallet.subtractMoney(payment);
                return payment;
            }
        }
    }
}
</pre>
===The New Wallet===
There won’t be any changes in design or the code of the Wallet as it performs the required function without any hassle in this example.
===The New Paperboy===
In this new paperboy class we have a method for getting the payment but this done by customer and not the right of the paperboy as per the previous design. In order to get the payment from the customer the paperboy executes a code snippet. The code snippet is some method in the Paperboy class (eg : requestPayment() ) which achieves this objective. The typical requestPayment method for this illustration is as follows
<pre>
void RequestPayment(Customer myCustomer) {
  payment = 2.00;  // Payment amount
  paidAmount = myCustomer.getPayment(payment);
  if (paidAmount == payment) {
        // Give customer a receipt
    } else {
        // Other code
}
</pre>
This had made the code and design much better.
===Reasons that led to better design and code===
The first reason that this is better is because it better models the real world scenario. The Paperboy code is now 'asking' the customer for a payment. The paperboy does not have direct access to the wallet.
The second reason that this is better is because the Wallet class can now change and the paperboy is completely isolated from that change.  If the interface to Wallet were to change, the Customer would have to be updated, but that's it. As long as the interface to Customer stays the same, none of the client's of Customer will care that he got a new Wallet.  Code will be more maintainable, because changes will not 'ripple' through a large project.
The third and probably most 'object-oriented' answer is that it is now free to change the
implementation of 'getPayment()'.  In the old example, the assumption was that the Customer would have a wallet.  This led to the null pointer exception.  In the real world though, when the paper boy comes, the customer may actually get the two bucks from a jar of change, search between the cushions of his couch or borrow it from his roommate, whatever.  All of this is 'Business Logic',and is of no concern to the paper boy. All this could be implemented inside of the getPayment() method, and could change in the future, without any modification to the paper boy code.
Those these above discussion have led to lots of advantages we need to accept the fact that it has made the customer a more complex object.


==References==
==References==

Revision as of 00:57, 27 October 2012

Law of Demeter

Introduction

Law of demeter(LoD) states "Only talk to your friends", which means do not talk to friend's friend. By doing this, we don't get to know the internals of our friend. In software terms, a class A should only talk to or invoke methods on closely related classes, class B and should not try to poke around the internals of B to reach a new class C. We have two options in order to achieve this, either class A can directly invoke methods on class C or the interface of class B should be changed to accomodate this call from A, then A will invoke methods on B and B will take care of invoking methods on C internally that would satisfy A's request.

In a way, law of demeter can be thought of a more specialized version of low coupling principle. Object oriented design recommends low coupling for better design and maintainability of software systems. The more loosely the classes are coupled, they become more easily testable. Classes need to directly interact with its collaborators and be shielded from understanding their internal structure. Using the internal knowledge of classes introduces risk of breaking functionality, instead they should talk to well known public interfaces. By doing all this we try to create a more shallow relationship than deeper relationship, which reduces the number of classes which may be affected by changes within the system.

Who can class A interact with ?<ref name="">Who can class interact with</ref>

If M is a method belonging to class A, then M may invoke other methods belonging to the following:
1. Class A
2. Members of A
3. Parameters of A
4. Objects created by M
5. Objects created by other methods of class A called by M
6. Globally accessible objects

When and How to identify Law of Demeter

Chained 'get' statements

value = object.getName().getCountry().getZip()
This clearly denotes we are violating the law of demeter since we our access crosses the boundary.

Importing or including many classes

In java we tend to include a lot of libraries, this is also an indication of law violation.

Advantages<ref name="Advantages">Advantages</ref>

Reusable code
Better design
Less risk of breaking functionality
Modular code

Example

Law of Demeter is a technique for reducing the 'coupling' between objects in the code. Here is an example based on Java that illustrates the Law of Demeter.

A ‘Paperboy’ has to get a payment from a ‘Customer’. There has to be a mechanism used by the paperboy for receiving payments from that customer. Lets assume that the paperboy will run a snippet in order to achieve the above. The definition of each Java class for this example is as follows.

Customer

The Customer class can have a first name, a last name, an account number, a shipping address, some method of payment, etc. In this example lets define the customer with just enough functionality to make our illustration of Law of Demeter to work.

public class Customer {
    private String firstName;
    private String lastName;
    private Wallet myWallet;
    public String getFirstName(){
        return firstName;
    }
    public String getLastName(){
        return lastName;
    }
    public Wallet getWallet(){
        return myWallet;
    }
}

Wallet

Each customer is associated with a wallet by which the payment is made. A wallet can have total money, credits cards, drivers license, different types of currency, etc. For this example we design a pretty simplistic wallet.

public class Wallet {
    private float value;
    public float getTotalMoney() {
        return value;
    }
    public void setTotalMoney(float newValue) {
        value = newValue;
    }
    public void addMoney(float deposit) {
        value += deposit;
    }
    public void subtractMoney(float debit) {
        value -= debit;
    }
}

Now that we have all required information about the customer and his wallet. Lets turn our focus on to the paperboy.

Paperboy

In this paperboy class we focus more on the method by which payment is made. In order to get the payment from the customer the paperboy executes a code snippet. The code snippet is some method in the Paperboy class (eg : getPayment() ) which achieves this objective. The typical getPayment method for this illustration is as follows

Paying the Paperboy - getPayment()

void getPayment(Customer myCustomer) {
   payment = 2.00;  // Payment amount
    Wallet theWallet = myCustomer.getWallet(); // Wallet of the corresponding customer who has to pay
    if (theWallet.getTotalMoney() > payment) {
        theWallet.subtractMoney(payment);
    } else {
        // Other code
    }
}

This code snippet gets the wallet from the customer, checks to make sure if the customer has the money and transfers it from the customers wallet into the paperboy's wallet.

Lets analyse what is bad about this code. We can actually translate what the code is actually doing into real-language. Apparently, when the paperboy stops by and demands payment, the customer is just going to turn around, let the paperboy take the wallet out of his back pocket, and take out two bucks. This is not the typical how some will handle their wallet.

There are a number of 'real-world' problems with this, not to mention we are trusting the paperboy to be honest and just take out what he's owed. If our future Wallet object holds credit cards, the paperboy has access to those too. The basic problem is that “the paperboy is being exposed to more information than he needs to be”.

The most important concept is the 'Paperboy' class now 'knows' that the customer has a wallet and can manipulate it. When we compile the Paperboy class, it will need the Customer class and the Wallet class. These three classes (Customer, Wallet and Paperboy) are now 'tightly coupled'. If we change the Wallet class, we may have to make changes to both of the other classes.

There is another classic problem that this can create. The customer wallet can be stolen or lost. In this case we can set the Wallet to null like

victim.setWallet(null);

This seems a reasonable assumption. This code enforces any mandatory value for wallet and there is a certain 'elegance' about using null for this condition but this never handles the paperboy. The code assumes there will be a wallet so the paperboy will get a runtime exception for calling a method on a null pointer. We could fix this by checking for 'null' on the wallet before we call any methods on it, but this starts to clutter the paperboy's class. The real-language description is becoming even worse as more and more complex arises. This makes the design and code more convoluted.

Code improvement , better design using Law of Demeter

The example illustrated can be made much better by incorporating proper design and improving the code structures. The proper way to fix the issue to move along the 'real world' scenario. When the paperboy needs payment, he would approach the customer but he is not going to handle the customer wallet. In fact he won't even know whether the customer has a wallet or not even if he does, he can’t be sure that the customer is willing to pay him.

The New Customer

The New customer has design changes such that he no longer has a 'getWallet()' method but it does have a getPayment method. The new customer class is as shown below

public class Customer {
    private String firstName;
    private String lastName;
    private Wallet myWallet;
    public String getFirstName(){
        return firstName;
  }
    public String getLastName(){
        return lastName;
    }
    public float getPayment(float bill) {
        if (myWallet != null) {
            if (myWallet.getTotalMoney() > bill) {
                theWallet.subtractMoney(payment);
                return payment;
            }
        }
    }
}

The New Wallet

There won’t be any changes in design or the code of the Wallet as it performs the required function without any hassle in this example.

The New Paperboy

In this new paperboy class we have a method for getting the payment but this done by customer and not the right of the paperboy as per the previous design. In order to get the payment from the customer the paperboy executes a code snippet. The code snippet is some method in the Paperboy class (eg : requestPayment() ) which achieves this objective. The typical requestPayment method for this illustration is as follows

void RequestPayment(Customer myCustomer) {
   payment = 2.00;  // Payment amount
   paidAmount = myCustomer.getPayment(payment);
   if (paidAmount == payment) {
        // Give customer a receipt
    } else { 
        // Other code
}

This had made the code and design much better.

Reasons that led to better design and code

The first reason that this is better is because it better models the real world scenario. The Paperboy code is now 'asking' the customer for a payment. The paperboy does not have direct access to the wallet.

The second reason that this is better is because the Wallet class can now change and the paperboy is completely isolated from that change. If the interface to Wallet were to change, the Customer would have to be updated, but that's it. As long as the interface to Customer stays the same, none of the client's of Customer will care that he got a new Wallet. Code will be more maintainable, because changes will not 'ripple' through a large project.

The third and probably most 'object-oriented' answer is that it is now free to change the implementation of 'getPayment()'. In the old example, the assumption was that the Customer would have a wallet. This led to the null pointer exception. In the real world though, when the paper boy comes, the customer may actually get the two bucks from a jar of change, search between the cushions of his couch or borrow it from his roommate, whatever. All of this is 'Business Logic',and is of no concern to the paper boy. All this could be implemented inside of the getPayment() method, and could change in the future, without any modification to the paper boy code.

Those these above discussion have led to lots of advantages we need to accept the fact that it has made the customer a more complex object.

References

<references/>

External Links

  1. Law of demeter(LoD) Law of Demeter
  2. Controlling the Complexity of Software Designs
  3. Loose Coupling with Demeter
  4. The Paperboy, The Wallet,and The Law Of Demeter