CSC/ECE 517 Fall 2007/wiki3 3 qq: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(59 intermediate revisions by 2 users not shown)
Line 3: Line 3:




== Defination of Separation of Responsibility (SOR)==
== Definition of Separation of Responsibility (SOR)==
In Object-Oriented Design (OOD)
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.[http://courses.ncsu.edu/csc517/common/lectures/notes/lec20.doc]  
* A responsibility is an obligation of an object to other objects. Or, in more abstract terms, a responsibility can be considered a reason to change, meaning that this is the only characteristic of an object that can motivate a change. See the first example below for an example of a class that has multiple reasons to change.
* Different responsibilities should be divided among different objects. Ideally, each individual object has one single responsibility, which we call Single Responsibility Principle (SRP).
* Different responsibilities should be divided among different objects. Ideally, each individual object has one single responsibility, which we call the Single Responsibility Principle (SRP). Another way to put this is "a class should only have one reason to change."[http://davidhayden.com/blog/dave/archive/2005/05/29/1066.aspx]
* Multiple responsibilities are often grouped into single software requirements. much of the process of designing software is about separating the responsibilities in the requirements. Once this is done, classes can be developed based on the responsibilities.
* Only one class should be responsible for knowing and maintaining a set of related data, even if that data is used by many other classes.[http://courses.ncsu.edu/csc517/common/lectures/notes/lec20.doc]


== Why Separation of Responsibility? ==


== Why Separation of Responsibility ==
*[[Encapsulation]]: Data for a single responsibility are kept in only one place.


*[[Encapsulation]]: Data are kept in only one place.
*[[Reusability]]: A class with a single responsibility can be more readily used in more than one application or environment.


*[[Reusability]]: Ready to be used in more than one application or environment.
*[[Flexibility]]: An object with a single responsibility can be modified for use in applications or environments other than those for which it was specifically designed. Changes to an object only affect a single responsibility.


*[[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 a single responsibility can be easily modified to correct faults, improve performance, or other attributes, or adapt to a changed environment without affecting other functionality.[http://64.233.169.104/search?q=cache:hv11-bOg9tAJ:www.hanselman.com/blog/PermaLink.aspx%3Fguid%3Da0a65e0c-5ef8-41e4-a566-1739b4428aa5+Separation+of+Responsibility+programming&hl=en&ct=clnk&cd=32&gl=us]


*[[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.[http://64.233.169.104/search?q=cache:hv11-bOg9tAJ:www.hanselman.com/blog/PermaLink.aspx%3Fguid%3Da0a65e0c-5ef8-41e4-a566-1739b4428aa5+Separation+of+Responsibility+programming&hl=en&ct=clnk&cd=32&gl=us]
*[[Once and Only Once(The Dry Principle)]]: The separation of responsibility can help to eliminate duplicate behavior, usually by merging the behavior or replacement by common behavior. Duplicate behavior can lead to inconsistencies or difficult to modify code.
 
*[[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]
 
*[[Decreases the coupling between two objests]]: One class do not need to know what happened and implementation details in the other class (e.x.,what kind of object the pointer points to). [http://www.codinghorror.com/blog/archives/000805.html]


*[[Decreases the coupling between two objects]]: One class does not need to know what happened and implementation details in other classes (e.g., what kind of object the pointer points to). It is easier to decouple a single responsibility than an object that has multiple responsibilities.[http://www.codinghorror.com/blog/archives/000805.html]


== Applications of Separation of Responsibility ==
== Applications of Separation of Responsibility ==
=== An Example Not using SOR===
=== An Example Not using SOR===
I am thinking maybe it is better for us to first take a look at what is NOT an Single Responsibility Principle. The SOR principle says that a class should have one, and only one, reason to change. As an example, imagine the following class:  
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:  
<pre>
<pre>
class Employee
class Employee
Line 43: Line 43:


=== Example 2 ===
=== Example 2 ===
Let’s look into the matter with another example. Consider following code:
Let’s look into the matter with another example. Consider the following code:
<pre>
<pre>
public void createCustomer(Map requestParameters) {
public void createCustomer(Map requestParameters) {
Customer customer = new Customer();
Customer customer = new Customer();
customer.setName = requestParameters.get("name");  
customer.setName = requestParameters.get("name");  
            //Check if a customer was already registered with that name
        if (customerService.getCustomerByName(customer.getName()) != null) {
if (customerService.getCustomerByName(customer.getName()) != null) {
System.out.println("Customer already exists");
System.out.println("Customer already exists");
return;
return;
}
}
customer.setShoppingCart(new ShoppingCart()); customerService.save(customer);
        ... //Check if a customer was already registered with that name
customer.setShoppingCart(new ShoppingCart());
        customerService.save(customer);
        ...
}
}
</pre>
</pre>
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 bindCustomer (bind and add new shoppingCart), validateCustomer and saveCustomer.
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.


With the last method (saveCustomer) I get the WHY? question a lot, since the extracted method is actually only 1 line of code long. Although it might not improve readability for programmers, it’s a paradigm shift in how the method is addressed. In our example, if the saveNewCustomer calls customerService.save() it’s responsible that the save method is actually called right. Instead if we let it delegate to a newly extracted method (saveCustomer) it isn’t responsible for the explicit saving. Instead now it defines the algorithm. In this case bind, validate, save. The implementation is just the save method.
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.[http://designparadigm.wordpress.com/] The code after using Separation of Responsibility is shown below:
 
Working in this fashion takes some getting used to, but in the end I truly believe this produces stabler and more maintainable code.


<pre>
public void bindValidateAndSave(Map requestParameters) {
        customer = validateCustomer(requestParameters);
        bindCustomer(customer);
        saveCustomer(customer);
}
</pre>
<pre>
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);
}


</pre>


=== Example 3===
=== Example 3===
Let's think about a example shown below:
Let's think about the example shown below:
Each ''node'' consists of data and a link (next) and the ''LinkedList'' implemented from Nodes.
Each ''node'' consists of data and a link (next) and the ''LinkedList'' implemented from Nodes.
<pre>
<pre>
Line 85: Line 107:
     }
     }
</pre>
</pre>
Then, we make a object ''client''  responsible for traversing a list which and keeping track of where it is.  
Then, we make an object ''client''  responsible for traversing a list and keeping track of where it is in the list traversal.  
<pre>
<pre>
     class client{
     class client{
         Linklist
         Linklist listNodes;
        Node curNode;
     }
     }


</pre>
</pre>
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 simultaneously. 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 [http://en.wikipedia.org/wiki/Factory_method_pattern ''factory''] class whose role is to manage instance allocation and an object's lifespan.
<pre>
    class NodeFactory{
        static Object createNode(Object data) { return new Node(data); }
    }
</pre>
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.[http://www.artima.com/weblogs/viewpost.jsp?thread=134947] For this reason the C++ auto_ptr (a smart pointer class with automatic garbage collection) is not compatible with these containers.[http://www.devx.com/tips/Tip/13606] What should be understood from this example is that when one class takes on multiple responsibilities, the client must beware.
== Further Reading ==
*[http://www.geometryit.com/tip/separation_of_concerns.pdf Design Pattern: Separation of Concerns]
A good document by Steven Smith describing the separation of concerns principles. It discusses some common pitfalls related to multiple responsibilities that developers fall into when designing components with a GUI. The Subject/Observer pattern is described as a technique to separate data components from presentation components.
*[http://www.objectmentor.com/resources/articles/srp.pdf SRP: The Single Responsibility Principle]
This document has a good example of how to recognize multiple responsibilities in a single class - a technique that can be hard to master.
*[http://sbtourist.blogspot.com/2007/10/avoid-your-getters-and-setters-part-2.html The Expert Pattern]
A blog on implementing the Expert Design Pattern. This webpage walks through an example of how to extract the objects with the information that makes them the expert, thus making them a candidate to be a standalone class.
*[http://www.augustana.ab.ca/~mohrj/courses/2003.fall/csc220/lecture_notes/responsibilities.html GRASP: Designing Objects with Responsibilities]
From a website for a software engineering class, this website offers some quick in context definitions of some vocabulary discussed on this wiki page.
*[http://www.objectsbydesign.com/books/larman_notes/6-DesignAndImplementationTechniques.html Design and Implementation Techniques]
Another webpage with outline style definitions of many terms related to GRASP (General Responsibility Assignment Software Patterns) in software design. Some of the terms described and defined are mapping designs to code, responsibilities, Expert pattern, Factory pattern, plus many more patterns.
*[http://www.zzrose.com/tech/pmr_sweEffectiveObjectResponsibility.html Designing Effective Object Responsibility Separation]
This document uses an example of a submarine and destroyer controller application to describe the process of coming up with a design solution for the application. It covers designing by interface, container-contained relationships, service classes, and the plumbing involved in inter-class communication.
*[http://sbtourist.blogspot.com/2007/10/avoid-your-getters-and-setters-part-2.html Avoid your getters and setters, Part 2]
This webpage has a good example of refactoring toward an Expert design pattern.
== Concluding Remarks ==
There are several important concepts that the SOR principle embodies. One is that each software entity should have one responsibility, and it should perform that responsibility well which also implies that different responsibilities are separated into different objects. Another is that, when designing these entities, the entity that contains the data necessary should be the one that performs a required task (the Expert Pattern).
As the above examples show, the SOR is a principle that should extend to all levels in the design of software. We showed examples of how it can be applied at the function and class levels, but its utility extends to the highest levels of the software design hierarchy where responsibilities become more general and abstract. Application components should also exhibit the SOR for the same reasons that we described in the “Why Separation of Responsibility” section. The SOR is a perspective that should be adhered to throughout software design when planning how entities will behave, what each entity will know about itself, and how each entity will interact with other entities.


== Reference ==
== Reference ==
[1] http://courses.ncsu.edu/csc517/common/lectures/notes/lec20.doc
[1] http://davidhayden.com/blog/dave/archive/2005/05/29/1066.aspx
 
[2] http://courses.ncsu.edu/csc517/common/lectures/notes/lec20.doc
 
[3] [http://64.233.169.104/search?q=cache:hv11-bOg9tAJ:www.hanselman.com/blog/PermaLink.aspx%3Fguid%3Da0a65e0c-5ef8-41e4-a566-1739b4428aa5+Separation+of+Responsibility+programming&hl=en&ct=clnk&cd=32&gl=us Programming Life and the Zen of Computers]
 
[4] http://www.codinghorror.com/blog/archives/000805.html
 
[5] [http://designparadigm.wordpress.com/ Java by experience]
 
[6] http://www.zzrose.com/tech/pmr_sweEffectiveObjectResponsibility.html


[2] [http://64.233.169.104/search?q=cache:hv11-bOg9tAJ:www.hanselman.com/blog/PermaLink.aspx%3Fguid%3Da0a65e0c-5ef8-41e4-a566-1739b4428aa5+Separation+of+Responsibility+programming&hl=en&ct=clnk&cd=32&gl=us Programming Life and the Zen of Computers]
[7] http://www.artima.com/weblogs/viewpost.jsp?thread=134947


[3] http://www.codinghorror.com/blog/archives/000805.html
[8] http://www.devx.com/tips/Tip/13606

Latest revision as of 01:28, 29 November 2007

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.


Definition of Separation of Responsibility (SOR)

In Object-Oriented Design (OOD)

  • A responsibility is an obligation of an object to other objects. Or, in more abstract terms, a responsibility can be considered a reason to change, meaning that this is the only characteristic of an object that can motivate a change. See the first example below for an example of a class that has multiple reasons to change.
  • Different responsibilities should be divided among different objects. Ideally, each individual object has one single responsibility, which we call the Single Responsibility Principle (SRP). Another way to put this is "a class should only have one reason to change."[1]
  • Multiple responsibilities are often grouped into single software requirements. much of the process of designing software is about separating the responsibilities in the requirements. Once this is done, classes can be developed based on the responsibilities.
  • Only one class should be responsible for knowing and maintaining a set of related data, even if that data is used by many other classes.[2]

Why Separation of Responsibility?

  • Encapsulation: Data for a single responsibility are kept in only one place.
  • Reusability: A class with a single responsibility can be more readily used in more than one application or environment.
  • Flexibility: An object with a single responsibility can be modified for use in applications or environments other than those for which it was specifically designed. Changes to an object only affect a single responsibility.
  • Maintainability: An object with a single responsibility can be easily modified to correct faults, improve performance, or other attributes, or adapt to a changed environment without affecting other functionality.[3]
  • Once and Only Once(The Dry Principle): The separation of responsibility can help to eliminate duplicate behavior, usually by merging the behavior or replacement by common behavior. Duplicate behavior can lead to inconsistencies or difficult to modify code.
  • Decreases the coupling between two objects: One class does not need to know what happened and implementation details in other classes (e.g., what kind of object the pointer points to). It is easier to decouple a single responsibility than an object that has multiple responsibilities.[4]

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 the 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.[5] 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 simultaneously. 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{
        static Object createNode(Object data) { return new Node(data); }
    }

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

A good document by Steven Smith describing the separation of concerns principles. It discusses some common pitfalls related to multiple responsibilities that developers fall into when designing components with a GUI. The Subject/Observer pattern is described as a technique to separate data components from presentation components.

This document has a good example of how to recognize multiple responsibilities in a single class - a technique that can be hard to master.

A blog on implementing the Expert Design Pattern. This webpage walks through an example of how to extract the objects with the information that makes them the expert, thus making them a candidate to be a standalone class.

From a website for a software engineering class, this website offers some quick in context definitions of some vocabulary discussed on this wiki page.

Another webpage with outline style definitions of many terms related to GRASP (General Responsibility Assignment Software Patterns) in software design. Some of the terms described and defined are mapping designs to code, responsibilities, Expert pattern, Factory pattern, plus many more patterns.

This document uses an example of a submarine and destroyer controller application to describe the process of coming up with a design solution for the application. It covers designing by interface, container-contained relationships, service classes, and the plumbing involved in inter-class communication.

This webpage has a good example of refactoring toward an Expert design pattern.

Concluding Remarks

There are several important concepts that the SOR principle embodies. One is that each software entity should have one responsibility, and it should perform that responsibility well which also implies that different responsibilities are separated into different objects. Another is that, when designing these entities, the entity that contains the data necessary should be the one that performs a required task (the Expert Pattern).

As the above examples show, the SOR is a principle that should extend to all levels in the design of software. We showed examples of how it can be applied at the function and class levels, but its utility extends to the highest levels of the software design hierarchy where responsibilities become more general and abstract. Application components should also exhibit the SOR for the same reasons that we described in the “Why Separation of Responsibility” section. The SOR is a perspective that should be adhered to throughout software design when planning how entities will behave, what each entity will know about itself, and how each entity will interact with other entities.

Reference

[1] http://davidhayden.com/blog/dave/archive/2005/05/29/1066.aspx

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

[3] Programming Life and the Zen of Computers

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

[5] Java by experience

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

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

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