CSC/ECE 517 Fall 2012/ch2b 2w68 sa: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
=Introduction= | ==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. These design principles are drafted around the concept of avoiding rigidity, fragility and immobility in software designs. | 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. 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, which states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. | The Single Responsibility Principle is one such design principle, which states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. | ||
=History= | ==History== | ||
This principle was described in the work of Tom DeMarco(http://en.wikipedia.org/wiki/Tom_DeMarco) and Meilir Page-Jones and was referred to as cohesion. | This principle was described in the work of Tom DeMarco(http://en.wikipedia.org/wiki/Tom_DeMarco) and Meilir Page-Jones and was referred to as cohesion. | ||
Line 10: | Line 10: | ||
=God Object= | ==God Object== | ||
The God Object(http://en.wikipedia.org/wiki/God_object) is an anti-pattern(http://en.wikipedia.org/wiki/Anti-pattern), in which one class performs too many functionalities. 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. | The God Object(http://en.wikipedia.org/wiki/God_object) is an anti-pattern(http://en.wikipedia.org/wiki/Anti-pattern), in which one class performs too many functionalities. 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== | ===Example of A Bad Design=== | ||
Consider the below class: | Consider the below class: | ||
Line 53: | Line 53: | ||
Actually, in most OO languages, the top-level Object class is a good example of a bad design. In Ruby(http://en.wikipedia.org/wiki/Ruby_(programming_language)), for example, the Object class (or more precisely the Kernel(http://en.wikipedia.org/wiki/Kernel_(computing)) mixin(http://en.wikipedia.org/wiki/Mixin), which gets mixed into Object) has 45 public instance methods. | Actually, in most OO languages, the top-level Object class is a good example of a bad design. In Ruby(http://en.wikipedia.org/wiki/Ruby_(programming_language)), for example, the Object class (or more precisely the Kernel(http://en.wikipedia.org/wiki/Kernel_(computing)) mixin(http://en.wikipedia.org/wiki/Mixin), which gets mixed into Object) has 45 public instance methods. | ||
=The Single Responsibility Principle= | ==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. | 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. | ||
Line 61: | Line 61: | ||
==Example 1: Email Message Class== | ===Example 1: Email Message Class=== | ||
Consider the following EmailMessage class: | Consider the following EmailMessage class: | ||
Line 83: | Line 83: | ||
==Example 2: Process Report Class== | ===Example 2: Process Report Class=== | ||
Consider a class that can compile and print reports. | Consider a class that can compile and print reports. | ||
Line 124: | Line 124: | ||
} | } | ||
==Example 3: Employee Class== | ===Example 3: Employee Class=== | ||
Consider the following class: | Consider the following class: | ||
class Employee | class Employee | ||
Line 145: | Line 145: | ||
The solution is to separate these functions out into different classes so that each can change independently of each other. | The solution is to separate these functions out into different classes so that each can change independently of each other. | ||
==Additional Example: User Authentication== | ===Additional Example: User Authentication=== | ||
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. | 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=== | ====Bad Design==== | ||
public class UserSettingService | public class UserSettingService | ||
{ | { | ||
Line 168: | Line 168: | ||
===Good Design=== | ====Good Design==== | ||
public class UserSettingService | public class UserSettingService | ||
{ | { | ||
Line 190: | Line 190: | ||
=Multiple Inheritance= | ==Multiple Inheritance== | ||
When using Multiple Inheritance(http://en.wikipedia.org/wiki/Multiple_inheritance), the classes that are being inherited could either violate the SRP or could aid it. | When using Multiple Inheritance(http://en.wikipedia.org/wiki/Multiple_inheritance), the classes that are being inherited could either violate the SRP or could aid it. | ||
Line 255: | Line 255: | ||
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. | 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= | ==Single Responsibility Principle Vs. Separation of Concerns== | ||
Separation of Concerns (SoC) (http://en.wikipedia.org/wiki/Separation_of_concerns) 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. | Separation of Concerns (SoC) (http://en.wikipedia.org/wiki/Separation_of_concerns) 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. | ||
Line 262: | Line 262: | ||
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. | 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= | ==Advantages of SRP== | ||
1. The SRP ensures understandable and flexible structure for the code. | 1. The SRP ensures understandable and flexible structure for the code. | ||
2. A small, atomic and independent code is ideal for reuse. | 2. A small, atomic and independent code is ideal for reuse. | ||
3. Overall system complexity can be reduced and thus increasing the reliability. | 3. Overall system complexity can be reduced and thus increasing the reliability. | ||
=Avoiding violation of SRP= | ==Avoiding violation of SRP== | ||
Below are few of the many ways to ensure that SRP is not violated. | Below are few of the many ways to ensure that SRP is not violated. | ||
1. Comments are the ideal way to keep track of all the functionalities of a class. This helps in identifying the number of different responsibilities that a class implements. | 1. Comments are the ideal way to keep track of all the functionalities of a class. This helps in identifying the number of different responsibilities that a class implements. | ||
Line 274: | Line 274: | ||
4. The use of private/protected methods often (but not always) means that chunk of code doesn’t belongs there and should be extracted; | 4. The use of private/protected methods often (but not always) means that chunk of code doesn’t belongs there and should be extracted; | ||
=Conclusion= | ==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. | 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= | ==See Also== | ||
Open/closed principle (http://en.wikipedia.org/wiki/Open/closed_principle) | Open/closed principle (http://en.wikipedia.org/wiki/Open/closed_principle) | ||
Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) | Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) | ||
Line 283: | Line 283: | ||
Dependency inversion principle (http://en.wikipedia.org/wiki/Dependency_inversion_principle) | Dependency inversion principle (http://en.wikipedia.org/wiki/Dependency_inversion_principle) | ||
=References= | ==References== | ||
1. http://en.wikipedia.org/wiki/Single_responsibility_principle. | 1. http://en.wikipedia.org/wiki/Single_responsibility_principle. | ||
2. http://en.wikipedia.org/wiki/Separation_of_concerns. | 2. http://en.wikipedia.org/wiki/Separation_of_concerns. |
Revision as of 02:05, 20 November 2012
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. 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, 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(http://en.wikipedia.org/wiki/Tom_DeMarco) and Meilir Page-Jones and was referred to as cohesion.
The term “The Single Responsibility principle” was introduced by Robert C. Martin(http://en.wikipedia.org/wiki/Robert_Cecil_Martin) in an article by the same name as part of his Principles of Object Oriented Design(http://en.wikipedia.org/wiki/Object_oriented_design) made popular by his book “Agile Software Development, Principles, Patterns, and Practices”.
God Object
The God Object(http://en.wikipedia.org/wiki/God_object) is an anti-pattern(http://en.wikipedia.org/wiki/Anti-pattern), in which one class performs too many functionalities. 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.
Actually, in most OO languages, the top-level Object class is a good example of a bad design. In Ruby(http://en.wikipedia.org/wiki/Ruby_(programming_language)), for example, the Object class (or more precisely the Kernel(http://en.wikipedia.org/wiki/Kernel_(computing)) mixin(http://en.wikipedia.org/wiki/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.
The Single Responsibility Principle 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(http://en.wikipedia.org/wiki/Cohesion_(computer_science)) 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(http://en.wikipedia.org/wiki/Coupling_(computer_programming)) 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. (1) The content of the report can change and (2) 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
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:
(1) The business rules having to do with calculating pay. (2) The database schema. (3) 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
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
When using Multiple Inheritance(http://en.wikipedia.org/wiki/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.
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) (http://en.wikipedia.org/wiki/Separation_of_concerns) 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
1. The SRP ensures understandable and flexible structure for the code. 2. A small, atomic and independent code is ideal for reuse. 3. 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. 1. Comments are the ideal way to keep track of all the functionalities of a class. This helps in identifying the number of different responsibilities that a class implements. 2. The instance variables are used as separated groups throughout the methods and thus ensuring each group belongs to a distinct class. 3. Abstracting the number of parameters that are being passed on method is another way to avoid breaking SRP. 4. 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
Open/closed principle (http://en.wikipedia.org/wiki/Open/closed_principle) Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) Interface segregation principle (http://en.wikipedia.org/wiki/Interface_segregation_principle) Dependency inversion principle (http://en.wikipedia.org/wiki/Dependency_inversion_principle)
References
1. http://en.wikipedia.org/wiki/Single_responsibility_principle. 2. http://en.wikipedia.org/wiki/Separation_of_concerns. 3. http://www.oodesign.com/single-responsibility-principle.html. 4. http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod. 5. http://en.wikipedia.org/wiki/Robert_Cecil_Martin. 6. http://en.wikipedia.org/wiki/Tom_DeMarco. 7. http://en.wikipedia.org/wiki/God_object. 8. http://www.code-magazine.com/article.aspx?quickid=1001061&page=4. 9. http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html. 10. http://programmers.stackexchange.com/questions/159885/does-multiple-inheritance-violate-single-responsibility-principle. 11. http://stackoverflow.com/questions/442713/how-to-use-the-single-responsibility-principle-in-large-wcf-services. 12. http://devlicio.us/blogs/tim_barcz/archive/2009/01/05/real-life-single-responsibility-principle.aspx. 13. http://blog.sanaulla.info/2011/11/16/solid-single-responsibility-principle/