CSC/ECE 517 Fall 2012/ch2b 2w68 sa

From Expertiza_Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Introduction

A software design principle is a comprehensive and fundamental doctrine or rule that governs the creation of quality software designs. These software design principles aid the cause of avoiding bad designs in software<ref>http://www.oodesign.com/design-principles.html</ref>. These design principles are drafted around the concept of avoiding rigidity, fragility and immobility in software designs.

The Single Responsibility Principle is one such design principle<ref>http://www.oodesign.com/single-responsibility-principle.html</ref>, which states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.


History

This principle was described in the work of Tom DeMarco<ref>http://en.wikipedia.org/wiki/Tom_DeMarco</ref> and Meilir Page-Jones and was referred to as cohesion. The term “The Single Responsibility principle” was introduced by Robert C. Martin<ref>http://en.wikipedia.org/wiki/Robert_Cecil_Martin</ref> in an article by the same name as part of his Principles of Object Oriented Design made popular by his book “Agile Software Development, Principles, Patterns, and Practices”.


God Object

<ref>http://en.wikipedia.org/wiki/God_object</ref>The God Object is an anti-pattern, in which one class performs too many functionality. Classes and methods should only have one method or purpose.If it has more than that, it goes against the single responsibility principal. The solution to this is to refactor the code.

Example of A Bad Design

Consider the below class:

class Session
{
    function startSession()
    {
        // send HTTP session cookies
    }

    function activateUserAccount()
    {
        // query the DB and toggle some flag in a column
    }

    function generateRandomId()
    {
	//generates a Random ID
    }

    function editAccount()
    {
        // issue some SQL UPDATE query to update an user account
    }

    function login()
    {
        // perform authentication logic
    }

    function checkAccessRights()
    {
        // read cookies, perform authorization
    }
}

In the above example the Session Class hold too many responsibility, exhibiting low cohesion.

In most OO languages, the top-level Object class is a good example of a bad design. In Ruby, for example, the Object class (or more precisely the Kernel mixin, which gets mixed into Object) has 45 public instance methods.


The Single Responsibility Principle

Before explaining the principle, there is a need to understand ‘What is a Responsibility? ‘.In the context of the Single Responsibility Principle (SRP), a responsibility can be defined as “a reason for change”. The Single Responsibility Principle says that a class should have one, and only one, reason to change<ref>http://www.code-magazine.com/article.aspx?quickid=1001061&page=4</ref>.

The Single Responsibility Principle<ref>http://en.wikipedia.org/wiki/Single_responsibility_principle</ref> can also be defined as follows: A class has a single responsibility: it does it all, does it well, and does it only. It can also be stated that SRP deals with cohesion at class level.

Adhering strictly to SRP may result in code being duplicated for distinct responsibilities, but avoids coupling of unrelated responsibilities, thus making the system less rigid under requirements change and modularity.

Example 1: Email Message Class

Consider the following EmailMessage class:

class EmailMessage
{
        public EmailMessage(string to, string subject, string message)
        {
  	      // Code to Email message;
        }
        public void SendMessage()
        {
  	     // Send message using sendTo, subject and message
        }
        public void Login(string username, string password)
        {
  	     // Code to login
        }
}

The Login method of the class is remotely related to the EmailMessage class and this could make it a low cohesive class. These functions raise the need for separation and if left unattended may result in code being tightly coupled and complicated to accommodate any change in the future. Thus there is a need to separate these functions into different classes so that they can change independently of each other.

Example 2: Process Report Class

Consider a class that can compile and print reports.

class ProcessReport
{
	public void compileReport()
        {
	   //Code to compile;
        }
        public void printReport()
        {
	  //Code to print;
        }
}

This class can be subjected to change for two reasons.

  • The content of the report can change and
  • The format of the report can change.

If there is a change to the report compilation process, there is greater danger that the printing code will break if it is part of the same class. The SRP says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes.It would be a bad design to couple two things that change for different reasons at different times.

The above class can be separated as follows:


class ReportsCompiler
{
	public void compileReport()
        {
	   //Code to compile;
        }
}

class ReportsPrinter
{
       public void printReport()
       {
	 //Code to print;
       }
}

Example 3: Employee Class

<ref>http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html</ref>Consider the following class:

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

This class violates the SRP 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.

This causes a single class to be impacted by these three completely different forces.It is evident that the Employee class gets modified every time the accounts decide to change the format of the hourly report, or every time the DBAs make a change to the database schema, as well as every time the managers change the payroll calculation. The solution is to separate these functions out into different classes so that each can change independently of each other.

Additional Example: User Authentication

<ref>http://blog.sanaulla.info/2011/11/16/solid-single-responsibility-principle/</ref>Consider a user setting service class which controls the changing of different settings which a user has. However, in order to prevent fraudulent changes to settings that are not allowed, the system must first check whether the user that is attempting to change the setting has access rights to that setting. This issue is can be handled in the following way.

Bad Design
public class UserSettingService
{
  	public void changeEmail(User user)
  	{
    		if(checkAccess(user))
    		{
       //Grant option to change
    		}
  	}

  	public boolean checkAccess(User user)
  	{
   		 //Verify if the user is valid.
  	}
} 

In the above example, as the number of uses for checkAccess increases, the mantainability of the code where checkAccess is in a UserSettingService gets worse. Instead the above class should be split into two each for one responsibility.

Good Design
public class UserSettingService
{
  	public void changeEmail(User user)
 	 {
    		if(SecurityService.checkAccess(user))
    		{
       			//Grant option to change
   		 }
 	 }
}

public class SecurityService
{
  	public static boolean checkAccess(User user)
  	{
    		//check the access.
  	}
}

Now this SecurityService class can be used by any number of other classes in order to allow someone to check the access rights of a user, whereas the UserSettingService is reserved for changing different types of settings.


Multiple Inheritance

<ref>http://programmers.stackexchange.com/questions/159885/does-multiple-inheritance-violate-single-responsibility-principle</ref>When using Multiple Inheritance, the classes that are being inherited could either violate the SRP or could aid it.

In general, if the inherited classes are categorized as essential and accidental, then inheriting such essential class could still preserve SRP while the latter violates it.

The SRP is a guideline to avoid god-classes, which are bad, but it also can be taken too literally and a project starts with tons of classes that can't really do anything without a handful being combined.

An Example

Consider the below example of a class that has methods that deal with business rules, reports, and database. Below example shows how it violates the rule of Single Responsibility Principle. Employee class has designed to save, modify and GetEmployeeInfo() records from database but MAPEmployee() method is used for mapping database columns with the Employee class attributes which can be altered if there is any changes in database.

Public Class EmployeeService
{
       public void SaveEmployeeInfo(Employee e)
       {
          // Code 
       }

       public void UpdateEmployeeInfo(int empId, Employee e)
       {
          // Code 
       }

       public Employee GetEmployeeInfo(int empID)
       {
         // Code 
       }
       public void MAPEmployee(SqlDatareader reader)
       {
         // Code 
       }
}

The above class can be segregated into two classes, so that if there are any changes in DB no need to change EmployeeService class.

Public Class EmployeeService
{
       public void SaveEmployeeInfo(Employee e)
       {
            // Code 
       }

       public void UpdateEmployeeInfo(int empId, Employee e)
       {
            // Code 
       }
    
       public Employee GetEmployeeInfo(int empID)
       {
           // Code 
       }
}

Public Class ExtendEmployeeService:EmployeeService
{
       public void MAPEmployee(SqlDatareader reader)
       {
           // Code 
       }
}

Thus multiple inheritance can be a sign that a class is getting too big, but it could also be a sign that a class’ focus is far too granular for the task at hand, or it could simply be a perfectly valid use case for multiple inheritance.


Single Responsibility Principle Vs. Separation of Concerns

Separation of Concerns (SoC)<ref>http://en.wikipedia.org/wiki/Separation_of_concerns</ref> is the process of breaking a computer program into distinct features that overlap in functionality as little as possible. A concern is any piece of interest or focus in a program. Typically, concerns are synonymous with features or behaviors.

Single Responsibility Principle (SRP) states that every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility. On some level Cohesion is considered as synonym for SRP.

Separation of Concerns is a process while the Single Responsibility Principle is a design / architecture philosophy. They're not completely disjoint, but they serve different purposes.


Advantages of SRP

  • The SRP ensures understandable and flexible structure for the code.
  • A small, atomic and independent code is ideal for reuse.
  • Overall system complexity can be reduced and thus increasing the reliability.


Avoiding violation of SRP

Below are few of the many ways to ensure that SRP is not violated.

  • Comments are the ideal way to keep track of all the functionality of a class. This helps in identifying the number of different responsibilities that a class implements.
  • The instance variables are used as separated groups throughout the methods and thus ensuring each group belongs to a distinct class.
  • Abstracting the number of parameters that are being passed on method is another way to avoid breaking SRP.
  • The use of private/protected methods often (but not always) means that chunk of code doesn't belongs there and should be extracted;


Conclusion

It is natural to assume, that beyond a level of abstraction SRP becomes pointless. But in practice, the granularity of the single responsibility should match the level of modeling. As one moves up in levels of abstraction, the granularity of responsibilities also should move toward a higher level of abstraction.


See Also


References

<references/>