CSC/ECE 517 Fall 2007/wiki3 3 qq

From Expertiza_Wiki
Revision as of 16:39, 19 November 2007 by Rwkanne (talk | contribs) (→‎Example 3)
Jump to navigation Jump to search

Topic

Take the principle of Separation of Responsibility and catalog the information on it available on the Web. Find good descriptions and good, concise, understandable examples. Tell which you consider the best to present to a class.


Defination of Separation of Responsibility (SOR)

In Object-Oriented Design (OOD)

  • One class should be responsible for knowing and maintaining a set of data, even if that data is used by many other classes.[1]
  • Different responsibilities should be divided among different objects. Ideally, each individual object has one single responsibility, which we call Single Responsibility Principle (SRP).


Why Separation of Responsibility

  • Reusability: Ready to be used in more than one application or environment.
  • Flexibility: An object with few responsibilities can be modified for use in applications or environments other than those for which it was specifically designed.
  • Maintainability: An object with few responsibilities can be easily modified to correct faults, improve performance, or other attributes, or adapt to a changed environment without affecting other functionality.[2]
  • Once and Only Once(The Dry Principle): The separation of responsibility can help to eliminate duplicated declarations of behavior, typically by merging them or replacing multiple similar implementations with a unifying abstraction.[3]


Applications of Separation of Responsibility

An Example Not using SOR

I am thinking that maybe it is better for us to first take a look at what is NOT a Separation of Responsibility. The SOR principle says that a class should have one, and only one, reason to change. As an example, imagine the following class:

class Employee
{
  public Money calculatePay()
  public void save()
  public String reportHours()
}

This class violates the SOR because it has three reasons to change:

  • The business rules having to do with calculating pay.
  • The database schema.
  • The format of the string that reports hours.

Obviously, we don't want a single class to be impacted by these three completely different forces. We don't want to modify the Employee class every time the accounts decide to change the format of the hourly report.


Example 2

Let’s look into the matter with another example. Consider following code:

public void createCustomer(Map requestParameters) {
	Customer customer = new Customer();
	customer.setName = requestParameters.get("name"); 
        if (customerService.getCustomerByName(customer.getName()) != null) {
		System.out.println("Customer already exists");
		return;
	}
        ... //Check if a customer was already registered with that name
	customer.setShoppingCart(new ShoppingCart());	
        customerService.save(customer);
        ...
}

The above method name is save. So I expect that the code only does some saving. Instead, it creates a new customer, checks if it’s valid (no double names) and then saves it. So the method is actually performing 3 things.

This method should be refactored into 4 methods. The first method, which I would call bindValidateAndSave is the algorithm method. It tells what to do, not how it’s done. Its responsibility is to provide the algorithm. The 3 other methods would be called validateCustomer, bindCustomer (bind and add new shoppingCart),and saveCustomer respectively.[4] The code after using Separation of Responsibility is shown below:

public void bindValidateAndSave(Map requestParameters) {
        customer = validateCustomer(requestParameters);
        bindCustomer(customer);
        saveCustomer(customer);	
}
public Customer validateCustomer(Map requestParameters) {
       Customer customer = new Customer();
       customer.setName = requestParameters.get("name"); 
       if (customerService.getCustomerByName(customer.getName()) != null) {
		System.out.println("Customer already exists");
	}
        ...
}
public void bindCustomer(Customer customer) {
       customer.setShoppingCart(new ShoppingCart());
}
public void saveCustomer(Customer customer) {
       customerService.save(customer);
}

Example 3

Let's think about the example shown below: Each node consists of data and a link (next) and the LinkedList implemented from Nodes.

    class Node { 
      Object data; 
      Node next; //point to next node 
      } 

    class Linklist implement Node{
        ...
        ...
       public Object currentNode() 
    { 
          Node temp=cursor(); 
          return temp.data; 
      }
    }

Then, we make an object client responsible for traversing a list and keeping track of where it is in the list traversal.

    class client{
        Linklist listNodes;
        Node curNode;
    }

This design conforms to the SOR because the container class is only responsible for maintaining the list while the client class is responsible for tracking the current node during list traversal. This is a good design since it allows multiple clients to traverse the list simulaneously. If the container class kept track of the current node, then simultaneous list traversal would cause one client to overwrite the current location of another client.

A third player in the list example could be a class whose sole responsibility is the creation of nodes. This class is typically referred to as a factory class whose role is to manage instance allocation and an object's lifespan.

    class NodeFactory{
        Object createNode { return new Node; }
    }

So, for a simple list, there are three responsibilities: containment, iteration, and instantiation. For proper SOR these concerns should be divided between three classes whose sole responsibilities are the three responsibilities. Modifications to one responsibility should have minimal affects on the responsibilities of the other two classes. E.g., if the container is switched from a linked list to an array, neither the node creation nor the client's method of iteration needs to be modified.

Usage of the Standard Template Library's container classes in C++ can be tricky when dealing with management of object lifespans due to this library not separating object lifetime management from containment.6 For this reason the C++ auto_ptr (a smart pointer class with automatic garbage collection) is not compatible with these containers.7 What should be understood from this example is that when one class takes on multiple responsibilities, the client must beware.

Further Reading

Concluding Remarks

Reference

[1] http://courses.ncsu.edu/csc517/common/lectures/notes/lec20.doc

[2] Programming Life and the Zen of Computers

[3] http://www.codinghorror.com/blog/archives/000805.html

[4] Java by experience

[5] http://www.zzrose.com/tech/pmr_sweEffectiveObjectResponsibility.html

[6] http://www.artima.com/weblogs/viewpost.jsp?thread=134947

[7] http://www.devx.com/tips/Tip/13606